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 -uutilizando dedicado fd, para procesamiento interactivo (muestra)En cuanto a la solicitud OP: correr chmoden todos los objetivos que figuran en el expediente , xargses 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 shellexpansió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.txtSi 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 chmodacepta 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 5como 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"; donefuncionará, pero hay 2 problemas:
cat |es un tenedor inútil , y
| while ... ;donese 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.txtPero,
Se le puede advertir $IFSy readbanderas:
help readread: 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.txtPara 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.txtMientras usa STDINpara 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.txtredirigirá STDINa file.txt. Eso significa que no podrá lidiar con el proceso hasta que finalicen.
Si planea crear una herramienta interactiva , debe evitar el uso STDINy utilizar algún descriptor de archivo alternativo .
Los descriptores de archivos constantes son: 0para STDIN , 1para STDOUT y 2para 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 0y 63(más, de hecho, dependiendo de la sysctlherramienta 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 7esta 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.txtdoneCerrar 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
xargsse 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 invocarchmoden este caso lo menos posible, reducir las horquillas aseguran la eficiencia.while ;do..done <$fileimplica ejecutar 1 fork para 1 archivo.xargspodría ejecutar 1 fork por mil archivos ... de manera confiable.cat file.txt | tr \\n \\0 | xargs -0 -n1 chmod 755tr \\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
catproceso.catcasi siempre es malo para un propósito como este. Puede leer más sobre el uso inútil del gato.fuente
cates una buena idea, pero en este caso, el comando indicado esxargschmod(es decir, ejecutar realmente un comando para cada línea del archivo).read -rlee una sola línea desde la entrada estándar (readsin-rinterpreta 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
xargses 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.txtSi 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,
parallelejecuta tantos subprocesos como su CPU cuenta. Pero puedes especificarlo con-jfuente
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
chmodfunciónperl -lpe 'chmod 0755, $_' file.txt- utilizar-lpara 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\nlugar 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