Quando è necessario xargs?


134

Il xargscomando mi confonde sempre. C'è una regola generale per questo?

Considera i due esempi seguenti:

$ \ls | grep Cases | less

stampa i file che corrispondono a "Casi", ma cambiando il comando in touchsarà richiesto xargs:

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

Risposte:


143

La differenza sta nei dati che il programma target accetta.

Se si utilizza semplicemente una pipe, riceve i dati su STDIN (il flusso di input standard) come una pila di dati grezzi che può ordinare su una riga alla volta. Tuttavia, alcuni programmi non accettano i loro comandi su standard in, si aspettano che sia spiegato negli argomenti del comando. Per esempio touchprende un nome di file come parametro sulla riga di comando in questo modo: touch file1.txt.

Se si dispone di un programma che genera i nomi dei file sullo standard out e desidera utilizzarli come argomenti a touch, è necessario utilizzare xargsche legge i dati di flusso stdin e converte ogni riga nello spazio argomenti separati al comando.

Queste due cose sono equivalenti:

# touch file1.txt
# echo file1.txt | xargs touch

Non usare xargsse non sai esattamente cosa sta facendo e perché è necessario. Abbastanza spesso accade che esiste un modo migliore per fare il lavoro che usare xargsper forzare la conversione. Il processo di conversione è anche pieno di potenziali insidie ​​come la fuga e l'espansione delle parole, ecc.


2
L'avvertimento mi sembra una piccola stringa. Delle due opzioni comuni per ottenere uno stream su una riga di comando ( xargse $(...)), xargs è molto più sicuro della sostituzione dei comandi. E non ricordo di essermi mai imbattuto in un nome di file legittimo con una nuova riga al suo interno. I problemi di fuga e di espansione delle parole non riguardano la sostituzione dei comandi, non gli xargs?
Camh

6
@camh: sono potenziali insidie ​​con entrambi. Nella shell, devi preoccuparti che i nomi dei file vengano divisi su spazi, schede e nuove righe. In xargs, devi solo preoccuparti delle newline. In xargs, se l'output è formattato correttamente, puoi invece dividere parole / nomi di file sul carattere NUL ( xargs -0), che è utile in combinazione con find -print0.
Ken Bloom,

Non xargschiamare il programma tramite la shell con separate da uno spazio args, o ha effettivamente costruire l'elenco di argomenti internamente (ad es. Per l'uso con execv/ execp)?
detenere il

1
Lo costruisce internamente e usa execvp, quindi è sicuro. Inoltre, gli xarg GNU (usati su Linux e pochi altri) consentono di specificare newline come delimitatore -d \n, sebbene gli xargs BSD (OSX et al) non sembrino supportare questa opzione.
soffice

72

Espandere le risposte già fornite, xargspuò fare una cosa interessante che sta diventando sempre più importante nel panorama informatico distribuito e multicore di oggi: può elaborare processi paralleli.

Per esempio:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

codificherà * .wav => * .flac, usando tre processi contemporaneamente ( -P 3).


Wow. Avrei dovuto saperlo una settimana fa quando stavo facendo esattamente la stessa cosa (tranne usare OGG) con 50GiB di WAV. :)
Alois Mahdal,

perché non usare il parametro -exec che trova ha?
Evgeny,

3
@Evgeny Il -execparametro non elabora processi paralleli.
anfetamachina,

Va notato che l' -0argomento perxargs farlo considera il NULLcarattere come delimitatore dell'elemento di input. find -print0output articoli delimitati da NULL. Questa è un'ottima pratica per i nomi di file che possono contenere spazi, virgolette o altri caratteri speciali.
Dan Dascalescu il

24

xargs è particolarmente utile quando hai un elenco di percorsi di file su stdin e vuoi fare qualcosa con loro. Per esempio:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Esaminiamo questo passo per passo:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

In altre parole, il nostro input è un elenco di percorsi a cui vogliamo fare qualcosa.

Per scoprire cosa fa xargs con questi percorsi, un bel trucco è aggiungere echoprima del tuo comando, in questo modo:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

L' -n 1argomento farà sì che xargs trasformi ogni riga in un comando a sé stante. Il sed -i "s/color/colour/g"comando sostituirà tutte le occorrenze di colorcon colourper il file specificato.

Nota che funziona solo se non hai spazi nei tuoi percorsi. In tal caso, è necessario utilizzare percorsi con terminazione null come input per xargs passando il -0flag. Un esempio di utilizzo sarebbe:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

Che fa lo stesso di quello che abbiamo descritto sopra, ma funziona anche se uno dei percorsi contiene uno spazio.

Funziona con qualsiasi comando che produce nomi di file come output come findo locate. Se ti capita di usarlo in un repository git con molti file, potrebbe essere più efficiente usarlo git grep -linvece di git ls-files, in questo modo:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

Il git grep -l "color" "*.tex"comando fornirà un elenco di file "* .tex" contenente la frase "color".


1
È vero, ma se hai imparato questo dovresti anche imparare Perché il looping sull'output di find è una cattiva pratica?
Carattere jolly

6

Il tuo primo argomento illustra abbastanza bene la differenza.

\ls | grep Cases | lessconsente di sfogliare l'elenco dei nomi di file prodotti da lse grep. Non importa che si tratti di nomi di file, sono solo del testo.

\ls | grep Cases | xargs lessconsente di sfogliare i file i cui nomi sono prodotti dalla prima parte del comando. xargsprende un elenco di nomi di file come input e un comando sulla sua riga di comando ed esegue il comando con i nomi dei file sulla sua riga di comando.

Quando si pensa di utilizzare xargs, tenere a mente che si aspetta di input formattato in un modo strano: da spazi delimitati, con \, 'e "utilizzato per la citazione (in un modo insolito, perché \non è citazioni speciali all'interno). Utilizzare solo xargsse i nomi dei file non contengono spazi bianchi o \'".


@Gilles: xargs ha la -0, --nullpossibilità di aggirare il problema degli spazi (è molto probabile che io l'abbia imparato da te :), quindi presumo che ti riferisca a una xargchiamata senza opzioni , ma sono perplesso dal tuo riferimento alle virgolette. Hai un link o un esempio al riguardo? .. (ps. | xargs lessè un pratico "trucco" +1 .. grazie ..
Peter.O

4

Nel tuo esempio non è necessario utilizzare xargsaffatto poiché findfarà esattamente e in modo sicuro ciò che si desidera fare.

Esattamente quello che vuoi usare findè:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

In questo esempio -maxdepth 1significa solo cercare nella directory corrente, non scendere in nessuna sottodirectory; per impostazione predefinita find cercherà in tutte le sottodirectory (che spesso è ciò che vuoi) a meno che non lo vincoli con maxdepth. Il {}è il nome del file che andranno sostituito al suo posto e +è uno dei due marcatori di fine di comando, l'altro essere ;. La differenza tra loro è che ;significa eseguire il comando su ciascun file uno alla volta, mentre +significa eseguire il comando su tutti i file contemporaneamente. Nota, tuttavia, che la tua shell probabilmente proverà ad interpretare ;se stessa, quindi dovrai evitarlo con uno \;o ';'. Sì, findha una serie di piccoli fastidi come questo, ma il suo potere è più che compensato.

Entrambi finde xargssono difficili da imparare all'inizio. Per aiutarti a imparare xargsprova a usare l' opzione -po --interactiveche ti mostrerà il comando che sta per eseguire e ti chiederà se vuoi eseguirlo o meno.

Allo stesso modo findè possibile utilizzare -okal posto di -execper richiedere se si desidera eseguire il comando o meno.

Ci sono volte, tuttavia, in cui findnon sarai in grado di fare tutto quello che vuoi ed è qui che xargsentra in gioco. Il -execcomando accetterà solo un'istanza di {}apparire, quindi se avessi un errore con find -type f -exec cp {} {}.bak \;potresti invece farlo così :find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

Puoi saperne di più sui comandi di esecuzione nel manuale di GNU Findutils .

Inoltre, ho detto che findfa sicuro quello che vuoi perché quando hai a che fare con file incontrerai spazi e altri caratteri che causeranno problemi a xargsmeno che tu non usi l' opzione -0o --nullinsieme a qualcosa che genera elementi di input terminati da un carattere null di spazi bianchi.



I nomi di file @Wildcard con spazi o caratteri come 'o "possono essere problematici, mentre findgestiranno questi casi senza problemi.
aculich,

Si, lo so. Vedi la mia risposta alla domanda collegata . Probabilmente avrei dovuto riformulare quella domanda in una dichiarazione nel commento sopra, o aggiungere la frase "Vedi la domanda ..." di fronte. : D
Wildcard il

1

xargs(insieme find, sort, du, uniq, perle pochi altri) accetta un'opzione della riga di comando per dire "STDIN ha un elenco di file, separati da un byte NUL (0x00)". Ciò semplifica la gestione di nomi di file con spazi e altri personaggi divertenti al loro interno. I nomi dei file non contengono NUL.


2
Penso che intendi "i nomi dei file non possono contenere valori null".
anfetamachina,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.