Me encuentro constantemente buscando la sintaxis de
find . -name "FILENAME" -exec rm {} \;
principalmente porque no veo exactamente cómo funciona la -exec
pieza. ¿Cuál es el significado de las llaves, la barra diagonal inversa y el punto y coma? ¿Hay otros casos de uso para esa sintaxis?
man
página POSIX anterior dice: Un nombre de utilidad o argumento que contiene solo los dos caracteres "{}" se reemplazará por el nombre de ruta actual , que me parece suficiente. Además, tiene un ejemplo con-exec rm {} \;
, al igual que en su pregunta. En mis días, apenas había otros recursos que la "gran pared gris", libros deman
páginas impresas (el papel era más barato que el almacenamiento). Entonces sé que esto es suficiente para alguien nuevo en el tema. Sin embargo, su última pregunta es justa de hacer aquí. Desafortunadamente ni @Kusalananda ni yo tenemos una respuesta a eso.xargs
veces es útil,find
puede pasar múltiples argumentos de ruta al comando sin él.-exec command... {} +
(con en+
lugar de\;
) pasa tantas rutas despuéscommand...
como quepan (cada sistema operativo tiene su propio límite en cuanto a la longitud de una línea de comandos). Y al igual quexargs
, la+
forma terminada en defind
's-exec
acción también se ejecutarácommand...
varias veces en el caso raro de que hay demasiados caminos para ajustarse al límite.Respuestas:
Esta respuesta viene en las siguientes partes:
-exec
-exec
en combinación consh -c
-exec ... {} +
-execdir
Uso básico de
-exec
La
-exec
opción toma una utilidad externa con argumentos opcionales como argumento y la ejecuta.Si la cadena
{}
está presente en cualquier parte del comando dado, cada instancia de la misma será reemplazada por el nombre de ruta que se está procesando actualmente (por ejemplo./some/path/FILENAME
). En la mayoría de los shells,{}
no es necesario citar los dos caracteres .El comando debe terminarse con un
;
parafind
saber dónde termina (ya que puede haber más opciones después). Para protegerlo;
del shell, debe citarse como\;
o';'
, de lo contrario, el shell lo verá como el final delfind
comando.Ejemplo (
\
al final de las dos primeras líneas son solo para continuar con las líneas):Esto encontrará todos los archivos normales (
-type f
) cuyos nombres coincidan con el patrón*.txt
en o debajo del directorio actual. Luego probará si la cadena sehello
produce en alguno de los archivos encontradosgrep -q
(lo que no produce ningún resultado, solo un estado de salida). Para aquellos archivos que contienen la cadena,cat
se ejecutará para enviar el contenido del archivo al terminal.Cada uno
-exec
también actúa como una "prueba" en los nombres de ruta encontrados porfind
, al igual que-type
y lo-name
hace. Si el comando devuelve un estado de salida cero (que significa "éxito"), se considera la siguiente parte delfind
comando; de lo contrario, elfind
comando continúa con el siguiente nombre de ruta. Esto se usa en el ejemplo anterior para buscar archivos que contienen la cadenahello
, pero para ignorar todos los demás archivos.El ejemplo anterior ilustra los dos casos de uso más comunes de
-exec
:find
comando).Utilizando
-exec
en combinación consh -c
El comando que
-exec
puede ejecutarse está limitado a una utilidad externa con argumentos opcionales.-exec
No es posible utilizar las funciones integradas de shell, funciones, condicionales, canalizaciones, redirecciones, etc. directamente , a menos que esté envuelto en algo así como unsh -c
shell secundario.Si
bash
se requieren funciones, úselasbash -c
en lugar desh -c
.sh -c
se ejecuta/bin/sh
con un script dado en la línea de comando, seguido de argumentos opcionales de la línea de comando para ese script.Un ejemplo simple de uso
sh -c
por sí mismo, sinfind
:Esto pasa dos argumentos al script de shell hijo:
La cadena
sh
. Estará disponible$0
dentro del script, y si el shell interno genera un mensaje de error, lo colocará como prefijo con esta cadena.El argumento
apples
está disponible como$1
en el script, y si hubiera habido más argumentos, entonces estos hubieran estado disponibles como$2
,$3
etc. También estarían disponibles en la lista"$@"
(excepto de los$0
cuales no serían parte de"$@"
).Esto es útil en combinación con,
-exec
ya que nos permite crear scripts arbitrariamente complejos que actúan sobre los nombres de ruta encontrados porfind
.Ejemplo: busque todos los archivos normales que tengan un sufijo de nombre de archivo determinado y cambie ese sufijo de nombre de archivo a otro sufijo, donde los sufijos se mantienen en variables:
Dentro de la secuencia de comandos interna,
$1
estaría la cadenatext
,$2
sería la cadenatxt
y$3
sería cualquier ruta quefind
haya encontrado para nosotros. La expansión del parámetro${3%.$1}
tomaría la ruta y eliminaría el sufijo.text
.O, usando
dirname
/basename
:o, con variables agregadas en el script interno:
Tenga en cuenta que en esta última variación, las variables
from
yto
en el shell hijo son distintas de las variables con los mismos nombres en el script externo.Lo anterior es la forma correcta de llamar a un script complejo arbitrario
-exec
confind
. Usandofind
en un bucle comoes propenso a errores y poco elegante (opinión personal). Está dividiendo nombres de archivos en espacios en blanco, invocando el bloqueo de nombres de archivo, y también obliga al shell a expandir el resultado completo
find
incluso antes de ejecutar la primera iteración del bucle.Ver también:
Utilizando
-exec ... {} +
Al
;
final puede ser reemplazado por+
. Esto hacefind
que se ejecute el comando dado con tantos argumentos (nombres de ruta encontrados) como sea posible en lugar de una vez para cada nombre de ruta encontrado. La cadena{}
debe aparecer justo antes+
de que esto funcione .Aquí,
find
recopilará los nombres de ruta resultantes y los ejecutarácat
en tantos como sea posible a la vez.Del mismo modo aquí,
mv
se ejecutará la menor cantidad de veces posible. Este último ejemplo requiere GNUmv
de coreutils (que admite la-t
opción).El uso
-exec sh -c ... {} +
también es una forma eficiente de recorrer un conjunto de nombres de ruta con un script arbitrariamente complejo.Lo básico es lo mismo que cuando se usa
-exec sh -c ... {} ';'
, pero el script ahora toma una lista de argumentos mucho más larga. Estos se pueden recorrer en bucle"$@"
dentro del script.Nuestro ejemplo de la última sección que cambia los sufijos de nombre de archivo:
Utilizando
-execdir
También existe
-execdir
(implementado por la mayoría de lasfind
variantes, pero no es una opción estándar).Esto funciona como
-exec
la diferencia de que el comando de shell dado se ejecuta con el directorio del nombre de ruta encontrado como su directorio de trabajo actual y que{}
contendrá el nombre de base del nombre de ruta encontrado sin su ruta (pero GNUfind
seguirá prefijando el nombre de base con./
BSD).find
No haré eso).Ejemplo:
Esto moverá cada
*.txt
archivo encontrado a undone-texts
subdirectorio preexistente en el mismo directorio donde se encontró el archivo . El archivo también será renombrado añadiéndole el sufijo.done
.Esto sería un poco más complicado de hacer,
-exec
ya que tendríamos que obtener el nombre base del archivo encontrado{}
para formar el nuevo nombre del archivo. También necesitamos el nombre del directorio de{}
para localizar eldone-texts
directorio correctamente.Con
-execdir
, algunas cosas como estas se vuelven más fáciles.La operación correspondiente usando en
-exec
lugar de-execdir
tendría que emplear un shell hijo:o,
fuente
-exec
toma un programa y argumentos y lo ejecuta; algunos comandos de shell consisten solo en un programa y argumentos, pero muchos no. Un comando de shell puede incluir redirección y canalización;-exec
no puede (aunque todofind
se puede redirigir). Un comando de shell puede usar,; && if
etc;-exec
no puede, aunque-a -o
puede hacer algo. Un comando de shell puede ser un alias o una función de shell, o integrado;-exec
no puedo. Un comando de shell puede expandir vars;-exec
no puede (aunque la capa externa que ejecuta lafind
lata). Un comando de shell puede sustituir de manera$(command)
diferente cada vez;-exec
no puedo. ...-exec
no puede, aunquefind
puede iterar sobre los archivos de la misma manera que la mayoría de los globs, por lo que rara vez se desea.sh
mismo, que es capaz de hacer todas esas cosasfind -exec cmd arg \;
no invoca un shell para interpretar una línea de comando de shell, se ejecutaexeclp("cmd", "arg")
directamente, noexeclp("sh", "-c", "cmd arg")
(para lo cual el shell terminaría haciendo el equivalente deexeclp("cmd", "arg")
sicmd
no estuviera integrado).find
argumentos después-exec
y hasta;
o+
compensar el comando a ejecutar junto con sus argumentos, con cada instancia de un{}
argumento reemplaza con el archivo actual (con;
), y{}
como último argumento antes+
reemplazado con una lista de archivos como argumentos separados (en el{} +
caso). IOW-exec
toma varios argumentos, terminados en a;
o{}
+
.