bash - ¿puedo hacer: encontrar ... -ejecutar esto y eso?


23

¿Hay alguna manera de combinar lógicamente dos comandos de shell que se invocan con find - exec ?

Por ejemplo, para imprimir todos los archivos .csv que contienen la cadena foo junto con su aparición, me gustaría hacer:

find . -iname \*.csv -exec grep foo {} && echo {} \;

pero bash se queja con "argumento perdido para '-exec'"


2
Podría usar 2 -execen secuencia o usar uno solo -exec sh -c 'grep foo "$0" && printf %s\\n "$0"' {} \;.
— jw013

Esto me ha hecho tropezar repetidamente: siempre espero que el primer argumento pasado sh(en este caso {}) sea $1y $0sea ​​algo así sh. Pero, de hecho, tienes razón, el primer argumento aparece como $0. Tener el primer argumento como el nombre del comando de invocación es solo una convención, que no se aplica automáticamente en estos casos.
— dubiousjim

Tal vez debería fusionarse con esta pregunta: unix.stackexchange.com/q/18077/4801
— dubiousjim

Respuestas:


24

-execes un predicado que ejecuta un comando (no un shell) y se evalúa como verdadero o falso en función del resultado del comando (estado de salida cero o no cero).

Asi que:

find . -iname '*.csv' -exec grep foo {} \; -print

sería imprimir la ruta del archivo, si greplos hallazgos de Foo en el archivo. En lugar de -printque pueda usar otro -execpredicado o cualquier otro predicado

find . -iname '*.csv' -exec grep foo {} \; -exec echo {} \;

Vea también los operadores !y -ofind para negación y or .

Alternativamente, puede iniciar un shell como:

find . -iname '*.csv' -exec sh -c '
   grep foo "$1" && echo "$1"' sh {} \;

O para evitar tener que iniciar un shell para cada archivo:

find . -iname '*.csv' -exec sh -c '
  for i do
    grep foo "$i" && echo "$i"
  done' sh {} +

10

El problema que enfrenta es que el shell primero analiza la línea de comando y ve dos comandos simples separados por el &&operador: find . -iname \*.csv -exec grep foo {}y echo {} \;. Citando &&( find . -iname \*.csv -exec grep foo {} '&&' echo {} \;) no pasa por eso, pero ahora el comando ejecutado por findalgo así como grepcon los argumentos foo, wibble.csv, &&, echoy wibble.csv. Debe indicarle findque ejecute un shell que interpretará al &&operador:

find . -iname \*.csv -exec sh -c 'grep foo "$0" && echo "$0"' {} \;

Tenga en cuenta que el primer argumento después sh -c SOMECOMMANDes $0, no $1.

Puede guardar el tiempo de inicio de un proceso de shell para cada archivo agrupando las invocaciones de comando con -exec … +. Para facilitar el procesamiento, pase un valor ficticio $0para "$@"enumerar los nombres de archivo.

find . -iname \*.csv -exec sh -c 'for x in "$@"; do grep foo "$x" && echo "$x"; done' \ {} +

Si el comando de shell solo tiene dos programas separados &&, findpuede hacer el trabajo por sí mismo: escriba dos -execacciones consecutivas , y la segunda solo se ejecutará si la primera sale con el estado 0.

find . -iname \*.csv -exec grep foo {} \; -exec echo {} \;

(Supongo que grepy echoson solo para fines ilustrativos, ya que -exec echopueden reemplazarse -printy el resultado resultante no es particularmente útil de todos modos).


2
Tiendo a evitar usar "$ 0" para eso, ya que también lo usa el shell para mostrar mensajes de error. Por ejemplo, podría ver un ./some-file: grep: command not foundmensaje de error confuso . -exec find sh -c '... "$1"' sh {} \;No tendría el problema. Hay un error tipográfico (relacionado) en su segundo comando de búsqueda.
— Stéphane Chazelas

3

En este caso específico, haría:

find . -iname \*.csv -exec grep -l foo \{\} \;

O si tienes ack :

ack -al -G '.*\.csv' foo

Para responder a su pregunta real, algo como esto puede funcionar:

find . -iname \*.csv -exec sh -c "grep foo {} && echo {}" \;


3
Este último comando de búsqueda no solo no es portátil, sino que también es muy peligroso, ya que las rutas de archivos terminan siendo evaluadas como código shell.
— Stéphane Chazelas

Razón de más para tratar de evitarlo usando ack / grep correctamente :)
— Dennis Kaarsemaker

1
La alternativa de jw013 (dada en un comentario a la pregunta) es más segura a este respecto. (Acabo de notar que las respuestas de Gilles y Stephane usan la misma técnica.)
— dudoso jim
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.