Por ejemplo, en este momento estoy usando lo siguiente para cambiar un par de archivos cuyas rutas de Unix escribí en un archivo:
cat file.txt | while read in; do chmod 755 "$in"; done
¿Existe una forma más elegante y segura?
Esto se debe a que no solo hay 1 respuesta ...
shell
expansión de línea de comandoxargs
herramienta dedicadawhile read
con algunas observacioneswhile read -u
utilizando dedicado fd
, para procesamiento interactivo (muestra)En cuanto a la solicitud OP: correr chmod
en todos los objetivos que figuran en el expediente , xargs
es la herramienta indicada. Pero para algunas otras aplicaciones, pequeña cantidad de archivos, etc.
Si su archivo no es demasiado grande y todos los archivos están bien nombrados (sin espacios u otros caracteres especiales como comillas), puede usar la shell
expansión de línea de comando . Simplemente:
chmod 755 $(<file.txt)
Para una pequeña cantidad de archivos (líneas), este comando es el más ligero.
xargs
es la herramienta correctaPara una mayor cantidad de archivos, o casi cualquier número de líneas en su archivo de entrada ...
Para muchos binutils herramientas, como chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Si tiene caracteres especiales y / o muchas líneas en file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
si su comando necesita ejecutarse exactamente 1 vez por entrada:
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Esto no es necesario para esta muestra, ya que chmod
acepta varios archivos como argumento, pero coincide con el título de la pregunta.
Para algún caso especial, incluso podría definir la ubicación del argumento del archivo en los comandos generados por xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
como entradaPrueba esto:
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
Donde el comando se realiza una vez por línea .
while read
y variantes.Como sugiere OP cat file.txt | while read in; do chmod 755 "$in"; done
funcionará, pero hay 2 problemas:
cat |
es un tenedor inútil , y
| while ... ;done
se convertirá en una subshell donde el entorno desaparecerá después ;done
.
Entonces esto podría estar mejor escrito:
while read in; do chmod 755 "$in"; done < file.txt
Pero,
Se le puede advertir $IFS
y read
banderas:
help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
En algunos casos, es posible que deba usar
while IFS= read -r in;do chmod 755 "$in";done <file.txt
Para evitar problemas con nombres de archivos extraños. Y tal vez si encuentra problemas con UTF-8
:
while LANG=C IFS= read -r in ; do chmod 755 "$in";done <file.txt
Mientras usa STDIN
para leer file.txt
, su script no puede ser interactivo (ya no puede usarlo STDIN
).
while read -u
, utilizando dedicado fd
.Sintaxis: while read ...;done <file.txt
redirigirá STDIN
a file.txt
. Eso significa que no podrá lidiar con el proceso hasta que finalicen.
Si planea crear una herramienta interactiva , debe evitar el uso STDIN
y utilizar algún descriptor de archivo alternativo .
Los descriptores de archivos constantes son: 0
para STDIN , 1
para STDOUT y 2
para STDERR . Podrías verlos por:
ls -l /dev/fd/
o
ls -l /proc/self/fd/
A partir de ahí, debe elegir el número no utilizado, entre 0
y 63
(más, de hecho, dependiendo de la sysctl
herramienta de superusuario) como descriptor de archivo :
Para esta demostración, usaré fd 7
:
exec 7<file.txt # Without spaces between `7` and `<`!
ls -l /dev/fd/
Entonces podría usar de read -u 7
esta manera:
while read -u 7 filename;do
ans=;while [ -z "$ans" ];do
read -p "Process file '$filename' (y/n)? " -sn1 foo
[ "$foo" ]&& [ -z "${foo/[yn]}" ]&& ans=$foo || echo '??'
done
if [ "$ans" = "y" ] ;then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
done
Cerrar fd/7
:
exec 7<&- # This will close file descriptor 7.
ls -l /dev/fd/
Nota: Dejé la versión marcada porque esta sintaxis podría ser útil, al hacer muchas E / S con procesos paralelos:
mkfifo sshfifo
exec 7> >(ssh -t user@host sh >sshfifo)
exec 6<sshfifo
xargs
se creó inicialmente para responder a este tipo de necesidad, algunas características, como construir el comando durante el mayor tiempo posible en el entorno actual para invocarchmod
en este caso lo menos posible, reducir las horquillas aseguran la eficiencia.while ;do..done <$file
implica ejecutar 1 fork para 1 archivo.xargs
podría ejecutar 1 fork por mil archivos ... de manera confiable.cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755
tr \\n \\0 <file.txt |xargs -0 [command]
es aproximadamente un 50% más rápido que el método que describió.Si.
De esta manera puede evitar un
cat
proceso.cat
casi siempre es malo para un propósito como este. Puede leer más sobre el uso inútil del gato.fuente
cat
es una buena idea, pero en este caso, el comando indicado esxargs
chmod
(es decir, ejecutar realmente un comando para cada línea del archivo).read -r
lee una sola línea desde la entrada estándar (read
sin-r
interpreta barras invertidas, no quiere eso)".si tiene un buen selector (por ejemplo, todos los archivos .txt en un directorio) puede hacer:
bash for loop
o una variante tuya:
fuente
Si sabe que no tiene ningún espacio en blanco en la entrada:
Si puede haber espacios en blanco en las rutas, y si tiene xargs GNU:
fuente
xargs
es robusto. Esta herramienta es muy antigua y su código se revisa fuertemente . Su objetivo era inicialmente construir líneas con respecto a las limitaciones de shell (64kchar / line o algo así). Ahora esta herramienta podría funcionar con archivos muy grandes y puede reducir mucho el número de fork a comando final. Ver mi respuesta y / oman xargs
.brew install findutils
), y luego invocar GNU xargs congxargs
, por ejemplo,gxargs chmod 755 < file.txt
Si desea ejecutar su comando en paralelo para cada línea, puede usar GNU Parallel
Cada línea de su archivo se pasará al programa como argumento. De forma predeterminada,
parallel
ejecuta tantos subprocesos como su CPU cuenta. Pero puedes especificarlo con-j
fuente
Veo que etiquetaste bash, pero Perl también sería una buena manera de hacer esto:
También puede aplicar una expresión regular para asegurarse de obtener los archivos correctos, por ejemplo, para procesar solo archivos .txt:
Para "previsualizar" lo que está sucediendo, simplemente reemplace las comillas con comillas dobles y anteponga
print
:fuente
chmod
funciónperl -lpe 'chmod 0755, $_' file.txt
- utilizar-l
para la función "auto-chomp"También puede usar AWK, que puede darle más flexibilidad para manejar el archivo
si su archivo tiene un separador de campo como:
Para obtener solo el primer campo que haces
Puede consultar más detalles sobre la documentación de GNU https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple
fuente
La lógica se aplica a muchos otros objetivos. ¿Y cómo leer .sh_history de cada usuario desde / home / filesystem? ¿Qué pasa si hay miles de ellos?
Aquí está el script https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh
fuente
Sé que es tarde pero aún así
Si por casualidad se encuentra con un archivo de texto guardado de Windows en
\r\n
lugar de\n
, puede confundirse con la salida si su comando tiene algo después de leer la línea como argumento. Entonces elimine\r
, por ejemplo:fuente