2011-11-28 15 views
99

Considerate questo comando:Ignora risultato vuoto per xargs

ls /mydir/*.txt | xargs chown root 

L'intento è quello di cambiare i proprietari di tutti i file di testo in mydir di radicare

Il problema è che se non ci sono file .txt in mydir poi xargs genera un errore nel dire che non è specificato alcun percorso. Questo è un esempio innocuo perché viene generato un errore, ma in alcuni casi, come nello script che ho bisogno di usare qui, si presume che un percorso vuoto sia la directory corrente. Quindi, se eseguo questo comando da /home/tom/, se non ci sono risultati per ls /mydir/*.txt e tutti i file sotto /home/tom/ hanno i loro proprietari modificati in root.

Quindi, come posso ignorare un risultato vuoto in xargs?

+4

parte: Mai uscita tubo dal 'ls' per l'utilizzo programmatico; vedi http://mywiki.wooledge.org/ParsingLs –

risposta

179

per GNU xargs, è possibile utilizzare il -r o --no-run-if-empty opzione:

--no-run-if-empty
-r
Se lo standard input non contiene Nonblanks, non eseguire il comando. Normalmente, il comando viene eseguito una volta anche se non vi è alcun input. Questa opzione è un'estensione GNU.

+6

Ho cercato onestamente su google e ho trovato questo (ma non avrei chiesto senza leggere il manuale). Ho pensato che questo sarebbe stato il comportamento predefinito, non avendo bisogno di un interruttore per abilitarlo. – JonnyJD

+20

Sfortunatamente questo non funziona su un Mac. Né l'opzione breve né quella lunga. – wedi

+4

@wedi - OSX ha uno spazio utente BSD, quindi questo genere di cose accadranno frequentemente. Puoi aggirare il problema usando homebrew per installare i fileutils GNU. – edk750

10

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

+0

Se sei su OSX non lo farà. edk750 ha spiegato questo nel loro commento se sei curioso del perché. – jasonleonhard

7

Gli utenti di xargs non-GNU possono usufruire di -L <#lines>, -n <#args>, -i, e -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 

Tenete a mente che xargs divide la linea a spazi bianchi, ma citando e fuggire sono disponibili; RTFM per i dettagli.

+0

Non sembra rispondere alla domanda? –

+1

@Nicolas: è il modo per prevenire l'esecuzione se la tua versione di 'xargs' non supporta l'opzione' --no-run-if-empty' e tenta di eseguire l'argomento comando senza input STDIN (questo è il caso in Solaris 10). Le versioni in altri Unix possono semplicemente ignorare una lista vuota (ad esempio AIX). – arielCo

+3

Sì! Su MAC OSX, 'echo '' | xargs -n1 echo blah' non fa nulla; mentre 'echo' x '| xargs -n1 echo blah' stampa "blah". – carlosayam

6

In termini di xargs, è possibile utilizzare -r come suggerito, tuttavia non è supportato da BSD xargs.

Quindi, come soluzione alternativa si può passare un po 'di extra file temporanei, ad esempio:

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

o reindirizzare il suo stderr in null (2> /dev/null), ad esempio,

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

Un altro approccio migliore è quello di iterare i file trovati usando while ciclo:

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

Vedi anche: Ignore empty results for xargs in Mac OS X


Inoltre si prega di notare che il metodo di cambiare il le autorizzazioni non sono eccezionali e sono scoraggiate. Sicuramente non si dovrebbe analizzare l'output di ls command (vedi: Why you shouldn't parse the output of ls). Soprattutto quando esegui il comando da root, perché i tuoi file possono contenere caratteri speciali che possono essere interpretati dalla shell o immagina che il file abbia un carattere di spazio intorno a /, quindi i risultati possono essere terribili.

Pertanto, è necessario modificare l'approccio e utilizzare invece il comando find, ad es.

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

Spiacente, non capisco come sia valido il file 'while IFS = read -r -d ''. Puoi spiegare? – Adrian

1

su OSX: Bash reimplementazione di xargs che fare con l'argomento -r, che mettere per esempio in $HOME/bin e aggiungerlo alla 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] 
Problemi correlati