Cosa fa "xargs grep"?


25

Conosco il grepcomando e sto imparando le funzionalità di xargs, quindi ho letto questa pagina che fornisce alcuni esempi su come usare il xargscomando.

Sono confuso dall'ultimo esempio, esempio 10. Dice "Il comando xargs esegue il comando grep per trovare tutti i file (tra i file forniti dal comando find) che contenevano una stringa 'stdlib.h'"

$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...

Tuttavia, qual è la differenza semplicemente usando

$ find . -name '*.c' | grep 'stdlib.h'

?

Ovviamente, sto ancora lottando con quello che sta facendo esattamente xargs, quindi ogni aiuto è apprezzato!


2
Questa domanda può essere utile: quando è necessario XArgs?
TheOdd

Risposte:


30
$ find . -name '*.c' | grep 'stdlib.h'

Questo convoglia l'output (stdout) * da finda (stdin of) * grep 'stdlib.h' come testo (ovvero i nomi dei file sono trattati come testo). grepfa la sua solita cosa e trova le righe corrispondenti in questo testo (tutti i nomi di file che a loro volta contengono il modello). Il contenuto dei file non viene mai letto.

$ find . -name '*.c' | xargs grep 'stdlib.h'

Questo costruisce un comando grep 'stdlib.h' dal quale ogni risultato findè un argomento - quindi questo cercherà corrispondenze all'interno di ogni file trovato da find(si xargspuò pensare che trasformi il suo stdin in argomenti ai comandi dati) *

Utilizzare -type fnel comando find oppure si otterranno errori dalle grepdirectory corrispondenti. Inoltre, se i nomi dei file hanno spazi, xargssi rovinano male, quindi usa il separatore null aggiungendo -print0e xargs -0per risultati più affidabili:

find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

* aggiunto questi punti esplicativi aggiuntivi come suggerito nel commento di @cat


2
potresti considerare di notare (perché sembra che tu abbia omesso) il punto chiave che |convoglia lo stdout allo stdin di grep che non è lo stesso degli argomenti di grep e dà risultati confusi.
gatto,

1
Oppure usa GNU find's find -name '*.c' -exec grep stdlib.h {} +. Praticamente non uso mai xargs. Inoltre, nessuno ha detto che xargs ha uno scopo simile a grep $(find)comandare la sostituzione, quindi ho scritto una mia risposta. Spiegare gli xarg come sostituzione dei comandi con meno limiti e problemi sembra naturale.
Peter Cordes,

Una situazione su cui uso xargs è se sto eliminando molti file come risultato di find. Se fai solo -exec rm, verrà eseguito rm su ogni file uno alla volta, il che è molto inefficiente. Il piping su xargs li farà tutti in una volta con una rm. Limitare con dire -n50 (esegui 50 alla volta) potrebbe impedire l'overflow della riga di comando (problema con molti file).
lsd

1
@lsd: perché non find -deleteper quel caso speciale? O per comandi diversi da rm, se hai GNU find, -exec some_command {} +raggruppa in batch come xargs, invece il \;comportamento di eseguire il comando separatamente per ciascuno.
Peter Cordes,

@lsd findesegue il comando su ciascun file se e solo se sta utilizzando -exec command \;Entrambi xargse -exec command \+chiamerà il comando con il numero massimo di argomenti consentiti dal sistema. In altre parole, sono equivalenti
Sergiy Kolodyazhnyy

6

xargs prende il suo input standard e lo trasforma in arg della riga di comando.

find . -name '*.c' | xargs grep 'stdlib.h' è molto simile a

grep 'stdlib.h' $(find . -name '*.c')  # UNSAFE, DON'T USE

E fornirà gli stessi risultati purché l'elenco dei nomi dei file non sia troppo lungo per una singola riga di comando. (Linux supporta megabyte di testo su una singola riga di comando, quindi di solito non hai bisogno di xargs.)


Ma entrambi fanno schifo, perché si rompono se i nomi dei file contengono spazi . Invece, find -print0 | xargs -0funziona, ma anche così

find . -name '*.c' -exec grep 'stdlib.h' {} +

Ciò non convoglia mai i nomi dei file da nessuna parte: findli raggruppa in una grande riga di comando e viene eseguito grepdirettamente.

\;invece di +eseguire grep separatamente per ogni file, il che è molto più lento. Non farlo. Ma +è un'estensione GNU, quindi devi xargsfarlo in modo efficiente se non puoi assumere GNU find.


Se lo lasci fuori xargs, il find | grepsuo motivo si abbina all'elenco dei nomi di file che findstampa.

Quindi a quel punto, potresti anche farlo find -name stdlib.h. Ovviamente, con -name '*.c' -name stdlib.h, non otterrai alcun output perché quei modelli non possono entrambi corrispondere e il comportamento predefinito di find è AND le regole insieme.

Sostituire lessin qualsiasi momento del processo per vedere quale output produce qualsiasi parte della pipeline.


Ulteriori letture: http://mywiki.wooledge.org/BashFAQ ha alcune cose fantastiche.


1
GNU xargs deve anche -dimpostare il separatore, quindi puoi usarlo -d'\n'per gestire un elenco separato da newline, che può essere utile se gestisci un elenco di nomi di file in un file, ecc. (Purché i nomi dei file non abbiano newline in loro, cioè.)
ilkkachu,

@ilkkachu: sì, le nuove righe nei nomi dei file sono molto più rare degli spazi, poiché interrompono la maggior parte degli script. myfunc(){ local IFS=$'\n'; fgrep stdlib.h` $ (find) ; }funziona anche con lo stesso effetto. O come linea singola, una (IFS=...; cmd...)subshell funziona anche per contenere la modifica a IFS senza doverla salvare / ripristinare.
Peter Cordes,

@PeterCordes Per favore, non fare command $( find )cose del genere. Nomi di file problematici con spazi e caratteri speciali possono interrompere questo tipo di cose. Almeno la doppia citazione della sostituzione del comando.
Sergiy Kolodyazhnyy,

@SergiyKolodyazhnyy: Grazie per aver sottolineato che sembra che io stia davvero raccomandando di farlo. Le persone che scremano potrebbero averlo copiato / incollato invece di leggere la sezione successiva. Aggiornato per risolverlo.
Peter Cordes,

@SergiyKolodyazhnyy: O stavi rispondendo al mio commento? Si noti che ho impostato IFSquindi equivale a utilizzare xargs '-d\n'. L'espansione globale e l'elaborazione del metacarattere della shell avvengono prima degli effetti di sostituzione dei comandi, quindi penso che sia sicuro anche con nomi di file che contengono $()o >. Concordato sul fatto che l'uso della suddivisione delle parole sulla sostituzione dei comandi non è una buona pratica, tranne per l'uso interattivo una tantum in cui si sa qualcosa sui nomi dei file. Ma command "$(find)"è utile solo se ti aspetti che produca esattamente 1 nome file ...
Peter Cordes,

5

In generale, xargsviene utilizzato per i casi in cui è possibile reindirizzare (con il simbolo |) qualcosa da un comando all'altro ( Command1 | Command2), ma l'output del primo comando non viene ricevuto correttamente come input per il secondo comando.

Ciò si verifica in genere quando il secondo comando non gestisce correttamente l'immissione dei dati tramite Standard In (stdin) (ad esempio: più linee come input, il modo in cui le linee sono impostate, i caratteri utilizzati come input, più parametri come input, il tipo di dati ricevuto come input, ecc.). Per darti un rapido esempio, prova quanto segue:

Esempio 1:

ls | echo- Questo non farà nulla poiché echonon sa come gestire l'input che sta ricevendo. Ora in questo caso, se lo utilizziamo xargs, elaborerà l'input in un modo che può essere gestito correttamente da echo(ad esempio: come una singola riga di informazioni)

ls | xargs echo- Questo produrrà tutte le informazioni da lsun'unica riga

Esempio 2:

Diciamo che ho più file goLang in una cartella chiamata go. Vorrei cercarli con qualcosa del genere:

find go -name *.go -type f | echo- Ma se il simbolo del tubo lì e echoalla fine, non funzionerebbe.

find go -name *.go -type f | xargs echo- Qui funzionerebbe grazie a xargsma se volessi ogni risposta dal findcomando in una sola riga, farei quanto segue:

find go -name *.go -type f | xargs -0 echo- In questo caso, lo stesso output findverrà mostrato da echo.

Comandi come cp, echo, rm, lesse altri che necessitano di un modo migliore per gestire l'input ottengono un vantaggio se usati con xargs.


4

xargs viene utilizzato per generare automaticamente argomenti della riga di comando in base (di solito) a un elenco di file.

Quindi, considerando alcune alternative all'utilizzo del xargscomando seguente:

find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'

Esistono diversi motivi per usarlo invece di altre opzioni che non erano originariamente menzionate in altre risposte:

  1. find . -name '*.c' -exec grep 'stdlib.h' {}\;genererà un grepprocesso per ogni file: questa è generalmente considerata una cattiva pratica e potrebbe caricare molto il sistema se vengono trovati molti file.
  2. Se ci sono molti file, un grep 'stdlib.h' $(find . -name '*.c')comando probabilmente fallirà, perché l'output $(...)dell'operazione supererà la lunghezza massima della riga di comando della shell

Come menzionato in altre risposte, la ragione per usare l' -print0argomento findin questo scenario e l' -0argomento su xargs è che i nomi di file con determinati caratteri (ad esempio virgolette, spazi o persino nuove righe) vengono ancora gestiti correttamente.

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.