2011-11-28 7 views
99

Considere este comando:Ignorar resultado vacío para xargs

ls /mydir/*.txt | xargs chown root 

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

La cuestión es que si no hay .txt archivos en mydir entonces xargs lanza un error que dice 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/, si no hay ningún resultado para ls /mydir/*.txt y todos los archivos en /home/tom/ tienen sus dueños cambiados a raíz.

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

+4

Aparte: Nunca salida de la tubería de 'ls' para uso programático; vea http://mywiki.wooledge.org/ParsingLs –

Respuesta

179

para GNU xargs, puede utilizar la opción -r o --no-run-if-empty:

--no-run-if-empty
-r
Si la entrada estándar no contiene ningún Sin espacios, 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.

+6

Honestamente, busqué en Google primero y encontré esto (pero no lo habría preguntado sin haber leído el manual). Creo que este debería ser el comportamiento predeterminado, no necesitando un interruptor para habilitarlo. – JonnyJD

+20

Desafortunadamente esto no funciona en una Mac. Ni la opción corta ni la larga. – wedi

+4

@wedi - OSX tiene espacio de usuario BSD, por lo que este tipo de cosas sucederá con frecuencia. Puede solucionarlo utilizando homebrew para instalar los fileutils de GNU. – edk750

10

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

+0

Si está en OSX no lo hará. edk750 ha explicado esto en su comentario si tiene curiosidad por qué. – jasonleonhard

7

Los usuarios de GNU xargs no pueden tomar ventaja de -L <#lines>, -n <#args>, -i y -I <string>:

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 citando a escapar y están disponibles; RTFM para más detalles.

+0

Parece que no responde la pregunta? –

+1

@Nicolas: es la manera de evitar la ejecución si su versión de 'xargs' no admite el modificador' --no-run-if-empty' e intenta ejecutar el argumento del comando sin la entrada STDIN (este es el caso en Solaris 10). Las versiones en otros unixes pueden simplemente ignorar una lista vacía (por ejemplo, AIX). – arielCo

+3

¡Sí! En MAC OSX, 'echo '' | xargs -n1 echo blah' no hace nada; mientras que 'echo 'x' | xargs -n1 echo blah' imprime "blah". – carlosayam

6

En términos de xargs, puede usar -r según lo sugerido, sin embargo, no es compatible con BSD xargs.

Así como solución alternativa que 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 en 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 bucle:

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

Ver también: Ignore empty results for xargs in Mac OS X


También tenga en cuenta que el método de cambiar la los permisos no son geniales y no se recomiendan. Definitivamente no debe analizar la salida de ls command (vea: Why you shouldn't parse the output of ls). Especialmente cuando ejecuta su comando por root, porque sus archivos pueden consistir en caracteres especiales que pueden ser interpretados por el intérprete 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 en su lugar, p.

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

Disculpe, no entiendo cómo es válido el archivo 'while IFS = read -r -d ''. ¿Puedes explicar? – Adrian

1

en OSX: reimplementación de Bash xargs tratar con el argumento -r, puesto que, por ejemplo, en $HOME/bin y añadirlo 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 [email protected]