extraer parte de una cadena usando bash / cut / split

121

Tengo una cadena como esta:

/var/cpanel/users/joebloggs:DNS9=domain.com

Necesito extraer el nombre de usuario ( joebloggs) de esta cadena y almacenarlo en una variable.

El formato de la cadena siempre será la misma, con excepción de joebloggsy domain.compor lo que estoy pensando en la cadena se puede dividir en dos ocasiones usando cut?

La primera división se dividiría por :y almacenaríamos la primera parte en una variable para pasar a la segunda función de división.

La segunda división se dividiría /y almacenaría la última palabra ( joebloggs) en una variable

Sé cómo hacer esto en php usando matrices y divisiones, pero estoy un poco perdido en bash.

Craig Edmonds
fuente

Respuestas:

333

Para extraer joebloggsde esta cadena en bash usando la expansión de parámetros sin ningún proceso adicional ...

MYVAR="/var/cpanel/users/joebloggs:DNS9=domain.com" 

NAME=${MYVAR%:*}  # retain the part before the colon
NAME=${NAME##*/}  # retain the part after the last slash
echo $NAME

No depende de joebloggsestar a una profundidad particular en el camino.


Resumen

Una descripción general de algunos modos de expansión de parámetros, como referencia ...

${MYVAR#pattern}     # delete shortest match of pattern from the beginning
${MYVAR##pattern}    # delete longest match of pattern from the beginning
${MYVAR%pattern}     # delete shortest match of pattern from the end
${MYVAR%%pattern}    # delete longest match of pattern from the end

Entonces #significa coincidir desde el principio (piense en una línea de comentario) y %significa desde el final. Una instancia significa más corta y dos instancias significa más larga.

Puede obtener subcadenas basadas en la posición usando números:

${MYVAR:3}   # Remove the first three chars (leaving 4..end)
${MYVAR::3}  # Return the first three characters
${MYVAR:3:5} # The next five characters after removing the first 3 (chars 4-9)

También puede reemplazar cadenas o patrones particulares usando:

${MYVAR/search/replace}

Tiene patternel mismo formato que la coincidencia de nombre de archivo, por lo que *(cualquier carácter) es común, a menudo seguido de un símbolo particular como /o.

Ejemplos:

Dada una variable como

MYVAR="users/joebloggs/domain.com" 

Elimine la ruta dejando el nombre del archivo (todos los caracteres hasta una barra):

echo ${MYVAR##*/}
domain.com

Elimine el nombre del archivo, dejando la ruta (elimine la coincidencia más corta después de la última /):

echo ${MYVAR%/*}
users/joebloggs

Obtenga solo la extensión del archivo (elimine todo antes del último período):

echo ${MYVAR##*.}
com

NOTA: Para hacer dos operaciones, no puede combinarlas, pero debe asignarlas a una variable intermedia. Entonces, para obtener el nombre del archivo sin ruta o extensión:

NAME=${MYVAR##*/}      # remove part before last slash
echo ${NAME%.*}        # from the new var remove the part after the last period
domain
beroe
fuente
No estoy seguro de si este es un argumento a favor o en contra del uso creativo de grep, pero inténtelo con VAR = / here / is / a / path: with / a / colon / inside: DNS9 =
domain.com
2
¡Dulce! Y se hace dentro del shell de ejecución, por lo que es mucho más rápido que los que usan otros comandos.
stolsvik
3
@Fadi Tienes que cambiar el comodín para que venga antes de los dos puntos y usar en #lugar de %. Si solo desea la parte después de los últimos dos puntos, use ${MYVAR##*:}para obtener la parte después de los primeros dos puntos, use${MYVAR#*:}
beroe
4
Amigo, no sabes cuántas veces he vuelto a esta respuesta. ¡Gracias!
Joel B
1
¡Gran respuesta! Pregunta: Si mi patrón fuera una variable, ¿lo escribiría así ${RET##*$CHOP}o así ${RET##*CHOP}(o de otra manera)? EDITAR: Parece ser el primero,${RET##*$CHOP}
Ctrl S
43

Defina una función como esta:

getUserName() {
    echo $1 | cut -d : -f 1 | xargs basename
}

Y pasa la cadena como parámetro:

userName=$(getUserName "/var/cpanel/users/joebloggs:DNS9=domain.com")
echo $userName
Stefano Sanfilippo
fuente
1
Esta respuesta me ayudó a lograr lo que vine aquí. No hay respuestas aceptadas y esta obtiene mi voto por simplicidad.
harperville
1
La única corrección que tuve que hacer en el comando anterior fue eliminar el ':', así echo $1 | cut -d -f 1 | xargs. +1 para ans simples y ordenadas.
Bhushan
20

¿Y sed? Eso funcionará en un solo comando:

sed 's#.*/\([^:]*\).*#\1#' <<<$string
  • Se #están utilizando para divisores de expresiones regulares en lugar de /porque la cadena tiene /.
  • .*/ agarra la cadena hasta la última barra invertida.
  • \( .. \)marca un grupo de captura. Esto es \([^:]*\).
    • El [^:]dice cualquier carácter _except dos puntos y los *medios cero o más.
  • .* significa el resto de la línea.
  • \1significa sustituir lo que se encontró en el primer (y único) grupo de captura. Este es el nombre.

Aquí está el desglose que hace coincidir la cadena con la expresión regular:

        /var/cpanel/users/           joebloggs  :DNS9=domain.com joebloggs
sed 's#.*/                          \([^:]*\)   .*              #\1       #'
David W.
fuente
¡Disección super bonita!
kyb
11

Usando un solo sed

echo "/var/cpanel/users/joebloggs:DNS9=domain.com" | sed 's/.*\/\(.*\):.*/\1/'
Yann Moisan
fuente
10

Usando un solo Awk:

... | awk -F '[/:]' '{print $5}'

Es decir, usando como separador de campo /o :, el nombre de usuario siempre está en el campo 5.

Para almacenarlo en una variable:

username=$(... | awk -F '[/:]' '{print $5}')

Una implementación más flexible sedque no requiere que el nombre de usuario sea el campo 5:

... | sed -e s/:.*// -e s?.*/??

Es decir, elimine todo desde :y más allá, y luego elimine todo hasta el último /. sedprobablemente también sea más rápido que awk, por lo que esta alternativa es definitivamente mejor.

Janos
fuente