Come applicare il comando shell a ciascuna riga di un output del comando?


192

Supponiamo di avere un output da un comando (come ls -1):

a
b
c
d
e
...

Voglio applicare un comando (diciamo echo) a ciascuno, a sua volta. Per esempio

echo a
echo b
echo c
echo d
echo e
...

Qual è il modo più semplice per farlo in bash?


3
ls -1può essere un esempio qui, ma è importante ricordare che non è utile analizzare l'output di ls. Vedi: mywiki.wooledge.org/ParsingLs
codeforester

Risposte:


217

Probabilmente è più facile da usare xargs. Nel tuo caso:

ls -1 | xargs -L1 echo

Il -Lflag assicura che l'ingresso sia letto correttamente. Dalla pagina man di xargs:

-L number
    Call utility for every number non-empty lines read. 
    A line ending with a space continues to the next non-empty line. [...]

24
lsfa automaticamente -1in un tubo.
In pausa fino a nuovo avviso.

5
@Dennis, non sembra così: ls | xargs -L2 echoe dai ls -1 | xargs -L2 echodue uscite diverse. Il primo è tutto su una linea.
Alex Budovski,

2
@Alex: ottengo lo stesso output.
In pausa fino a nuovo avviso.

6
xargspuò eseguire solo file eseguibili non funzioni di shell o comandi incorporati della shell. Per il primo la soluzione migliore è probabilmente quella con readin un ciclo.
pabouk,

12
Vorrei che questa risposta includesse una spiegazione di cosa -L1serve.
Wyck,

164

È possibile utilizzare un'operazione anteposta di base su ciascuna riga:

ls -1 | while read line ; do echo $line ; done

Oppure puoi reindirizzare l'output a sed per operazioni più complesse:

ls -1 | sed 's/^\(.*\)$/echo \1/'

1
Il comando sed non sembra funzionare: sh: cho: not found a sh: cho: not foundsembra che l' eeco in sia un comando sed o qualcosa del genere.
Alex Budovski,

+1 per il whileciclo. cmd1 | while read line; do cmd2 $line; done. O while read line; do cmd2 $line; done < <(cmd1)che non crea una subshell. Questa è la versione semplificata del tuo sedcomando:sed 's/.*/echo &/'
In pausa fino a nuovo avviso.

1
@Alex: cambia le virgolette doppie in virgolette singole.
In pausa fino a nuovo avviso.

5
Cita il "$line"ciclo while, per evitare la divisione delle parole.
ignis,

3
Prova a usare read -r lineper evitare di readscherzare con i caratteri di escape. Ad esempio echo '"a \"nested\" quote"' | while read line; do echo "$line"; done"a "nested" quote", che ha perso la sua fuga. Se lo facciamo echo '"a \"nested\" quote"' | while read -r line; do echo "$line"; doneotteniamo "a \"nested\" quote"come previsto. Vedi wiki.bash-hackers.org/commands/builtin/read
Warbo

9

Puoi usare un ciclo for :

per file in *; fare
   echo "$ file"
fatto

Si noti che se il comando in questione accetta più argomenti, l'utilizzo di xargs è quasi sempre più efficiente in quanto deve generare l'utilità in questione solo una volta anziché più volte.


1
Vale la pena descrivere l'uso corretto / sicuro di xargs, ad es. printf '%s\0' * | xargs -0 ...- altrimenti, non è abbastanza sicuro con nomi di file con spazi bianchi, virgolette, ecc.
Charles Duffy,

8

In realtà puoi usare sed per farlo, a condizione che sia GNU sed.

... | sed 's/match/command \0/e'

Come funziona:

  1. Sostituisci partita con comando corrisponde
  2. Alla sostituzione eseguire il comando
  3. Sostituisci riga sostituita con output di comando.

Fantastico. Ho dimenticato di mettere il comando e alla fine, ma l'ho fatto dopo aver visto la tua risposta e ha funzionato. Stavo cercando di aggiungere un ID casuale tra 1000 e 15000, quando SED corrisponde a una riga. cat /logs/lfa/Modified.trace.log.20150904.pw | sed -r 's/^(.*)(\|006\|00032\|)(.*)$/echo "\1\2\3 - ID `shuf -i 999-14999 -n 1`"/e'
sgsi,


2

xargs fallisce con barre rovesciate, virgolette. Deve essere qualcosa di simile

ls -1 |tr \\n \\0 |xargs -0 -iTHIS echo "THIS is a file."

xargs -0 opzione:

-0, --null
          Input  items are terminated by a null character instead of by whitespace, and the quotes and backslash are
          not special (every character is taken literally).  Disables the end of file string, which is treated  like
          any  other argument.  Useful when input items might contain white space, quote marks, or backslashes.  The
          GNU find -print0 option produces input suitable for this mode.

ls -1termina gli elementi con caratteri di nuova riga, quindi trli traduce in caratteri null.

Questo approccio è circa 50 volte più lento dell'iterazione manuale con for ...(vedi la risposta di Michael Aaron Safyan ) (3.55s contro 0.066s). Ma per altri comandi di input come individuare, trovare, leggere da un file ( tr \\n \\0 <file) o simili, devi lavorare in xargsquesto modo.


1

Risultato migliore per me:

ls -1 | xargs -L1 -d "\n" CMD

Meglio, ma non perfetto. find . -mindepth 1 -maxdepth 1 -print0 | xargs -0 commandgestirà i casi in cui l'output di ls -1è ambiguo; usa -printf '%P\0'piuttosto che -print0se non vuoi un vantaggio ./su ciascuno.
Charles Duffy,

1

mi piace usare gawk per eseguire più comandi in un elenco, ad esempio

ls -l | gawk '{system("/path/to/cmd.sh "$1)}'

tuttavia la fuga dei personaggi sfuggenti può diventare un po 'pelosa.

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.