¿Cómo ignorar los comandos xargs si la entrada stdin está vacía?

Considera este comando:

ls /mydir/*.txt | xargs chown root 

La intención es cambiar los propietarios de todos los archivos de texto en mydir para mydir

El problema es que si no hay archivos .txt en mydir , xargs genera un error que indica que no hay una ruta especificada. Este es un ejemplo inofensivo porque se lanza un error, pero en algunos casos, como en el script que necesito usar aquí, se supone que una ruta en blanco es el directorio actual. Entonces, si ejecuto ese comando desde /home/tom/ luego, si no hay ningún resultado para ls /mydir/*.txt y todos los archivos en /home/tom/ tienen sus propietarios cambiados a raíz.

Entonces, ¿cómo puedo hacer que xargs ignore un resultado vacío?

Para GNU xargs , puede usar la --no-run-if-empty -r o --no-run-if-empty :

--no-run-if-empty
-r
Si la entrada estándar no contiene ningún nonblanks, no ejecute el comando. Normalmente, el comando se ejecuta una vez, incluso si no hay entrada. Esta opción es una extensión de GNU.

man xargs dice --no-run-if-empty .

Los usuarios de -L <#lines> no sean de GNU pueden aprovechar -L <#lines> , -n <#args> , -i , y -I :

 ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args or fewer ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines or fewer ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder 

Tenga en cuenta que xargs divide la línea en el espacio en blanco, pero están disponibles las citas y los escapes; RTFM para más detalles.

En términos de xargs , puede usar -r como se sugiere, sin embargo, no es compatible con BSD xargs .

Así que, como solución, puede pasar algún archivo temporal adicional, por ejemplo:

 find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root $(mktemp) 

o redirigir su stderr a nulo ( 2> /dev/null ), por ejemplo

 find /mydir -type f -name "*.txt" -print0 | xargs -0 chown root 2> /dev/null || true 

Otro enfoque mejor es iterar sobre los archivos encontrados usando while loop:

 find /mydir -type f -name "*.txt" -print0 | while IFS= read -r -d '' file; do chown -v root "$file" done 

Consulte también: Ignorar resultados vacíos para xargs en Mac OS X


También tenga en cuenta que su método para cambiar los permisos no es bueno y está desaconsejado. Definitivamente, no debería analizar la salida del comando ls (consulte: Por qué no debe analizar el resultado de ls ). Especialmente cuando ejecuta su comando por root, porque sus archivos pueden consistir en caracteres especiales que pueden ser interpretados por el intérprete de comandos o imaginar que el archivo tiene un carácter de espacio alrededor de / , los resultados pueden ser terribles.

Por lo tanto, debe cambiar su enfoque y usar el comando find lugar, por ejemplo

 find /mydir -type f -name "*.txt" -execdir chown root {} ';' 

En OSX: Bash reimplementation de xargs tratan con el argumento -r , pon eso, por ejemplo, en $HOME/bin y agrégalo a la PATH :

 #!/bin/bash stdin=$(cat <&0) if [[ $1 == "-r" ]] || [[ $1 == "--no-run-if-empty" ]] then # shift the arguments to get rid of the "-r" that is not valid on OSX shift # wc -l return some whitespaces, let's get rid of them with tr linecount=$(echo $stdin | grep -v "^$" | wc -l | tr -d '[:space:]') if [ "x$linecount" = "x0" ] then exit 0 fi fi # grep returns an error code for no matching lines, so only activate error checks from here set -e set -o pipefail echo $stdin | /usr/bin/xargs $@