Make xargs esegue il comando una volta per ogni riga di input
-
11-07-2019 - |
Domanda
Come posso fare in modo che xargs esegua il comando esattamente una volta per ogni riga di input fornita? Il suo comportamento predefinito è di tagliare le linee ed eseguire il comando una volta, passando più linee a ciascuna istanza.
Da http://en.wikipedia.org/wiki/Xargs :
find / path -type f -print0 | xargs -0 rm
In questo esempio, find alimenta l'input di xargs con un lungo elenco di nomi di file. xargs quindi suddivide questo elenco in elenchi secondari e chiama rm una volta per ciascun elenco secondario. Questo è più efficiente di questa versione funzionalmente equivalente:
find / path -type f -exec rm '{}' \;
So che find ha il " exec " bandiera. Sto solo citando un esempio illustrativo da un'altra risorsa.
Soluzione
Quanto segue funzionerà solo se non hai spazi nel tuo input:
xargs -L 1
xargs --max-lines=1 # synonym for the -L option
dalla pagina man:
-L max-lines
Use at most max-lines nonblank input lines per command line.
Trailing blanks cause an input line to be logically continued on
the next input line. Implies -x.
Altri suggerimenti
Mi sembra che tutte le risposte esistenti su questa pagina siano sbagliate, inclusa quella contrassegnata come corretta. Ciò deriva dal fatto che la domanda è formulata in modo ambiguo.
Riepilogo: & nbsp; Se vuoi eseguire il comando " esattamente una volta per ogni riga di input fornita, " passando l'intera riga (senza la nuova riga) al comando come argomento singolo, allora questo è il miglior modo compatibile UNIX per farlo:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
può o meno avere estensioni utili che ti consentono di eliminare tr
, ma non sono disponibili su OS X e altri sistemi UNIX.
Ora per la lunga spiegazione & # 8230;
Ci sono due problemi da tenere in considerazione quando si usa xargs:
- come divide l'input in " argomenti " ;; e
- quanti argomenti per passare il comando figlio alla volta.
Per testare il comportamento di xargs, abbiamo bisogno di un'utilità che mostri quante volte viene eseguita e con quanti argomenti. Non so se esiste un'utilità standard per farlo, ma possiamo codificarlo abbastanza facilmente in bash:
#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo
Supponendo di salvarlo come show
nella directory corrente e renderlo eseguibile, ecco come funziona:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
Ora, se la domanda originale riguarda davvero il punto 2. sopra (come penso, dopo averlo letto più volte) e deve essere letta in questo modo (cambia in grassetto):
Come posso fare in modo che xargs esegua il comando esattamente una volta per ogni argomento dell'input fornito? Il suo comportamento predefinito è di inserire input negli argomenti ed eseguire il comando il minor numero di volte possibile , passando più argomenti a ciascuna istanza.
quindi la risposta è -n 1
.
Confrontiamo il comportamento predefinito di xargs, che divide l'input attorno agli spazi bianchi e chiama il comando il minor numero di volte possibile:
$ echo one two 'three and four' | xargs ./show
-> "one" "two" "three" "and" "four"
e il suo comportamento con -n 1
:
$ echo one two 'three and four' | xargs -n 1 ./show
-> "one"
-> "two"
-> "three"
-> "and"
-> "four"
Se, d'altra parte, la domanda originale riguardava il punto 1. input split e doveva essere letta in questo modo (molte persone che vengono qui sembrano pensare che sia così, o stanno confondendo i due problemi):
Come posso fare in modo che xargs esegua il comando con esattamente un argomento per ogni riga di input fornita? Il suo comportamento predefinito è tagliare le linee attorno agli spazi bianchi .
allora la risposta è più sottile.
Si potrebbe pensare che -L 1
potrebbe essere di aiuto, ma si scopre che non cambia l'analisi degli argomenti. Esegue il comando solo una volta per ogni riga di input, con tutti gli argomenti presenti su quella riga di input:
$ echo
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
$ echo
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
$ echo
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
$ echo one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*>
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
<*>
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
<*>one \ntwo\nthree and four' | xargs -L 1 ./show
-> "one" "two"
-> "three" "and" "four"
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
<*>
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
<*>one\ntwo\nthree and four' | xargs -L 1 ./show
-> "one"
-> "two"
-> "three" "and" "four"
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*>
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
<*>
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
<*>one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show
-> "one " "two" "three and four"
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Non solo, ma se una riga termina con spazi bianchi, viene aggiunta alla successiva:
<*> Chiaramente, -L
non riguarda il modo in cui xargs suddivide l'input in argomenti.
L'unico argomento che lo fa in modo multipiattaforma (escluse le estensioni GNU) è -0
, che divide l'input in byte NUL.
Quindi, si tratta solo di tradurre nuove righe in NUL con l'aiuto di tr
:
Ora l'analisi dell'argomento sembra a posto, incluso lo spazio bianco finale.
Infine, se combini questa tecnica con -n 1
, otterrai esattamente un'esecuzione di comando per riga di input, qualunque input tu abbia, che potrebbe essere un altro modo per esaminare la domanda originale ( forse il più intuitivo, dato il titolo):
Se vuoi eseguire il comando per ogni riga (es. risultato) proveniente da find
, allora di cosa hai bisogno xargs
per ?
Prova:
find
path -type f -exec
your-command {} \;
dove il {}
letterale viene sostituito dal nome del file e il \;
letterale è necessario per find
per sapere che il comando personalizzato termina lì.
EDIT:
(dopo la modifica della tua domanda chiarendo che conosci -exec
)
Da man xargs
:
-L linee massime
Utilizzare al massimo linee massime linee di input non vuote per riga di comando. trailing gli spazi vuoti fanno sì che una riga di input continui logicamente sulla riga di input successiva. Implica -x.
Nota che i nomi di file che terminano con spazi vuoti potrebbero causare problemi se usi xargs
:
$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\ b c\ c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory
Quindi, se non ti interessa l'opzione -exec
, è meglio usare -print0
e -0
:
$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
Come posso fare in modo che xargs esegua il comando esattamente una volta per ogni riga di input fornita?
Non utilizzo la risposta -L 1
perché non gestisce i file con spazi all'interno che è una funzione chiave di find -print0
.
echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: space.txt: No such file or directory
ls: with: No such file or directory
Una soluzione migliore è usare tr
per convertire i newline in caratteri null ( \ 0
), e quindi usare l'argomento xargs -0
.
echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt
Se è quindi necessario limitare il numero di chiamate, è possibile utilizzare l'argomento -n 1
per effettuare una chiamata al programma per ciascun input:
echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls
Ciò consente anche di filtrare l'output di find prima convertendo le interruzioni in valori null.
find . -name \*.xml | grep -v /workspace/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar
Un'altra alternativa ...
find /path -type f | while read ln; do echo "processing $ln"; done
find path -type f | xargs -L1 command
è tutto ciò di cui hai bisogno.
Questi due modi funzionano anche e funzioneranno con altri comandi che non usano find!
xargs -I '{}' rm '{}'
xargs -i rm '{}'
esempio di utilizzo:
find . -name "*.pyc" | xargs -i rm '{}
eliminerà tutti i file pyc in questa directory anche se i file pyc contengono spazi.
Il seguente comando troverà tutti i file (-type f) in / path
e poi li copierà usando cp
nella cartella corrente. Si noti l'uso se -I%
per specificare un carattere segnaposto nella riga di comando cp
in modo che gli argomenti possano essere inseriti dopo il nome del file.
find / path -type f -print0 | xargs -0 -I% cp%.
Testato con xargs (GNU findutils) 4.4.0
Puoi limitare il numero di righe o argomenti (se ci sono spazi tra ogni argomento) usando rispettivamente i flag --max-lines o --max-args.
-L max-lines Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input line. Implies -x. --max-lines[=max-lines], -l[max-lines] Synonym for the -L option. Unlike -L, the max-lines argument is optional. If max-args is not specified, it defaults to one. The -l option is deprecated since the POSIX standard specifies -L instead. --max-args=max-args, -n max-args Use at most max-args arguments per command line. Fewer than max-args arguments will be used if the size (see the -s option) is exceeded, unless the -x option is given, in which case xargs will exit.
Sembra che non abbia abbastanza reputazione per aggiungere un commento a la risposta di Tobia sopra , quindi sto aggiungendo questo " risposta " per aiutare quelli di noi che desiderano sperimentare xargs
allo stesso modo sulle piattaforme Windows.
Ecco un file batch di Windows che fa la stessa cosa di Tobia con la codifica rapida " show " script:
@echo off
REM
REM cool trick of using "set" to echo without new line
REM (from: http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
exit /b
)
<nul set /p=Args: "%~1"
shift
:start
if not "%~1" == "" (
<nul set /p=, "%~1"
shift
goto start
)
echo.
Le risposte di @Draemon sembrano essere corrette con " -0 " anche con spazio nel file.
Stavo provando il comando xargs e ho scoperto che " -0 " funziona perfettamente con " -L " ;. anche gli spazi vengono trattati (se l'input è stato terminato con null). il seguente è un esempio:
#touch "file with space"
#touch "file1"
#touch "file2"
Quanto segue dividerà i valori null ed eseguirà il comando su ciascun argomento nell'elenco:
#find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2
quindi -L1
eseguirà l'argomento su ciascun carattere nullo se usato con " -0 " ;. Per vedere la differenza prova:
#find . -name 'file*' -print0 | xargs -0 | xargs -L1
./file with space ./file1 ./file2
anche questo verrà eseguito una volta:
#find . -name 'file*' -print0 | xargs -0 | xargs -0 -L1
./file with space ./file1 ./file2
Il comando verrà eseguito una volta come " -L " ora non si divide su byte null. devi fornire sia " -0 " e " -L " lavorare.
Nel tuo esempio, il punto di reindirizzare l'output di find a xargs è che il comportamento standard dell'opzione -exec di find è quello di eseguire il comando una volta per ogni file trovato. Se stai usando find e vuoi il suo comportamento standard, allora la risposta è semplice: non usare xargs per cominciare.
esegue il task di formica clean-all su ogni build.xml nella cartella corrente o in quella secondaria.
find . -name 'build.xml' -exec ant -f {} clean-all \;