Bash Ejecutando comandos contra un archivo encontrado


Ejemplo

A veces necesitaremos ejecutar comandos contra muchos archivos. Esto se puede hacer usando xargs .

find . -type d -print | xargs -r chmod 770

El comando anterior buscará recursivamente todos los directorios ( -type d ) relativos a . (que es su directorio de trabajo actual), y ejecute chmod 770 en ellos. La opción -r especifica a xargs para no ejecutar chmod si find no encontró ningún archivo.

Si los nombres de sus archivos o directorios tienen un carácter de espacio en ellos, este comando puede bloquearse; una solución es usar lo siguiente

find . -type d -print0 | xargs -r -0 chmod 770

En el ejemplo anterior, las -print0 y -0 especifican que los nombres de los archivos se separarán mediante un byte null , y permite el uso de caracteres especiales, como espacios, en los nombres de los archivos. Esta es una extensión GNU y puede que no funcione en otras versiones de find y xargs .


La forma preferida de hacerlo es omitir el comando xargs y dejar que find llame al subproceso en sí:

find . -type d -exec chmod 770 {} \;

Aquí, {} es un marcador de posición que indica que desea utilizar el nombre del archivo en ese punto. find ejecutará chmod en cada archivo individualmente.

Alternativamente, puede pasar todos los nombres de archivos a una sola llamada de chmod , usando

find . -type d -exec chmod 770 {} +

Este es también el comportamiento de los fragmentos de xargs anteriores. (Para llamar a cada archivo individualmente, puede usar xargs -n1 ).


Una tercera opción es dejar que bash se desplace por la lista de nombres de archivos para find resultados:

find . -type d | while read -r d; do chmod 770 "$d"; done

Esto es sintácticamente el más torpe, pero conveniente cuando quiere ejecutar múltiples comandos en cada archivo encontrado. Sin embargo, esto no es seguro en vista de los nombres de archivos con nombres impares.

find . -type f | while read -r d; do mv "$d" "${d// /_}"; done

que reemplazará todos los espacios en los nombres de archivo con guiones bajos. (Este ejemplo tampoco funcionará si hay espacios en los nombres de directorio principales).

El problema con lo anterior es que, while read -r espera una entrada por línea, los nombres de los archivos pueden contener nuevas líneas (y también, la read -r perderá cualquier espacio en blanco al final). Puedes arreglar esto cambiando las cosas:

find . -type d -exec bash -c 'for f; do mv "$f" "${f// /_}"; done' _ {} +

De esta manera, el -exec recibe los nombres de los archivos en una forma que es completamente correcta y portátil; bash -c recibe como una serie de argumentos, que se encontrarán en $@ , correctamente citados, etc. (El script deberá manejar estos nombres correctamente, por supuesto; cada variable que contenga un nombre de archivo debe estar en doble citas.)

El misterioso _ es necesario porque el primer argumento para bash -c 'script' se usa para rellenar $0 .