¿Cómo usar la salida grep como ruta para cd?

9

¿Cómo puedo canalizar la grepsalida como argumento del cdcomando?

Por ejemplo:

[root@xxx xxx]# pip install django | grep '/usr.*'  
Requirement already satisfied (use --upgrade to upgrade): django in   /usr/lib64/python2.7/site-packages

Aquí /usr/lib64/python2.7/site-packagesestá resaltado y quiero pasarle esta cadena cd.

micgeronimo
fuente

Respuestas:

16

Use la sustitución de comandos de Bash $(), también necesita -ocon grep para seleccionar solo la parte coincidente:

cd "$(pip install django | grep -o '/usr.*')"

Tenga en cuenta que aunque se saldrá con la suya en este caso, siempre debe encerrar la sustitución del comando con comillas dobles para que el shell no realice la división de palabras en espacios en blanco (por defecto, el espacio, la pestaña y la nueva línea, depende de la IFSvariable en caso de bash).

heemayl
fuente
9

Dependiendo de lo que haga y no sepa de antemano acerca de lo que se pipgenerará, es posible que decida grepalgo diferente /usr.*.

Si sabe que el directorio comienza con/usr (y que aparece al final de la línea de salida de pip, y que /usrno aparece en ninguna parte de la línea antes del nombre del directorio), entonces es una buena elección; La respuesta de Heemayl te dice cómo.

Si la razón por la que sabe que comienza /usres porque acaba de ejecutar el comando y conoce el directorio al que desea cambiar, sugiero la solución más simple de ejecutar el comando cd /usr/lib64/python2.7/site-packages. Esto es menos escribir incluso si no hace uso de la finalización de la pestaña .

De lo contrario, puede elegir una expresión regular diferente según lo que sepa sobre el resultado que se analiza. Todas las alternativas a continuación aún asumen que el nombre del directorio aparece al final de la línea, pero los otros supuestos varían.

Si sabe que el nombre del directorio es absoluto (es decir, comienza con a /) y no /aparece en la línea antes del nombre del directorio , puede usar la misma expresión regular que en la respuesta de heemayl pero con en /lugar de /usr:

cd "$(pip install django | grep -o '/.*')"

Esto coincide con un /seguido de cero o más ( *) de cualquier carácter ( .).

Si sabe que el nombre del directorio no contiene espacios en blanco horizontales (sin espacios ni pestañas) y aparece al final de la línea , puede usar:

cd "$(pip install django | grep -oP '[^\h]+$')"

Aquí he usado un Perl regexp ( -P) porque la \habreviatura (for [:blank:]) hace que sea más fácil escribir y leer que un regexp extendido equivalente ( -E). Esto coincide con uno o más ( +) de cualquier carácter en una clase de caracteres ( [ ]) que no sea ( ^) un espacio o tabulación ( \h).

Si sabe que el nombre del directorio está inmediatamente precedido por inun espacio en blanco horizontal (es decir, rellenado con espacios en blanco a la izquierda y a la derecha), y que esta es la única ocurrencia inen la línea , puede usar:

cd "$(pip install django | grep -oP '\hin\h+\K.+')"

Esto utiliza una aserción positiva de ancho cero ( \K) para que coincida con uno o más caracteres ( .+) que aparecen después de un espacio o tabulación ( \h) in, y otro uno o más espacios o tabulaciones ( \h+), sin incluir realmente inlos espacios en blanco rodeándolo en el partido. Las afirmaciones de mirar alrededor son una característica de las expresiones regulares de Perl.

El patrón también habría funcionado, pero solo necesitamos buscar un espacio en blanco antes , independientemente de cuántos estén presentes. Por el contrario, debemos hacer coincidir todos los espacios en blanco después , de lo contrario, no se descartarían y coincidirían como parte del nombre del directorio.\h+in\h+\K.+inin\K

Si sabe que el nombre del directorio está precedido inmediatamente por la última aparición de la línea inseguida de espacios en blanco horizontales , puede usar:

set +H
cd "$(pip install django | grep -oP '\hin\h+(?!.*\hin\h.*)\K.*')"
set -H

Allí, la afirmación de retrospectiva positiva de ancho cero en sí misma contiene una aserción de anticipación negativa de ancho cero ( (?! )).

El !aparece de tal manera que es difícil escapar con elegancia; el método que he usado para evitar que active la expansión del historial de shell antes de pasar grepes deshabilitar temporalmente la expansión del historial ( set +H) antes de ejecutar el comando y volver a habilitarlo ( set -H) después. Si está usando esto en un script y su script no contiene set -H, no necesita hacer esto ya que la expansión del historial se habilita automáticamente solo cuando un shell se ejecuta de forma interactiva.

Finalmente, tenga en cuenta que ninguno de estos, ni la respuesta de heemayl , en realidad están canalizando la salida de grepa cd(aunque la salida de piptodavía se está canalizando a grep). En lugar de tuberías , la herramienta adecuada para este trabajo es la sustitución de comandos .

Eliah Kagan
fuente
Por supuesto, si desea ser técnico , el shell utiliza una tubería internamente para leer la salida del comando.
Random832
@ Random832 Buen punto: he editado un poco en consideración a esto. Por cierto, ¿sabe si se puede confiar en el uso de tuberías "debajo del capó" en la sustitución de comandos? ¿O es un detalle de implementación que podría hacerse de manera diferente en una versión futura diferente de bash? (Por supuesto, en la práctica, entiendo que es la mejor manera de hacerlo, y por lo tanto es poco probable que se implemente de manera diferente.)
Eliah Kagan
1
No, no está garantizado, en teoría un shell podría usar un archivo llamado fifo o temporal, aunque no hay razón para hacerlo.
Random832
¿Estás seguro de la expansión de la historia? Los únicos caracteres que pueden bloquearlo son ` and '', y tiene comillas simples alrededor de la expresión grep.
muru
1
@muru Sí, se ha expandido. Yo tampoco me había dado cuenta, pero lo descubrí cuando lo probé. El !allí no se cita con '-quotes, ya que los '-quotes se citan ellos mismos. La complejidad del comando hace que sea fácil predecir mal su efecto, pero aparentemente debido al orden de las expansiones ( !es temprano) termina funcionando como x=foo; echo "'$x'"(-> 'foo'). La eliminación de las "comillas externas provocaría !una 'comilla y evitaría la expansión del historial, pero luego cd fallaría si el directorio tiene espacios en blanco en su nombre.
Eliah Kagan