trova -exec con più comandi


430

Sto cercando di usare find -exec con più comandi senza successo. Qualcuno sa se sono possibili comandi come i seguenti?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

Fondamentalmente, sto cercando di stampare l'ultima riga di ogni file txt nella directory corrente e stampare alla fine della riga, una virgola seguita dal nome del file.



1
Per quanto riguarda la verifica della possibilità del comando, non l'hai provato sul tuo sistema?
Sriram,

1
Dalla findpagina del manuale: There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999


Il link @ JVE999 è interrotto, alternativa all'indirizzo ss64.com/bash/find.html
Keith M

Risposte:


659

findaccetta più -execporzioni al comando. Per esempio:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

Si noti che in questo caso il secondo comando verrà eseguito solo se il primo restituisce correttamente, come indicato da @Caleb. Se si desidera eseguire entrambi i comandi indipendentemente dal loro esito positivo o negativo, è possibile utilizzare questo costrutto:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

come grep due volte? questo non riesce: find ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "my_string" {} \;
rajeev,

51
@rajeev Il secondo exec verrà eseguito solo se il codice di ritorno per il primo restituisce esito positivo, altrimenti verrà ignorato. Questo dovrebbe probabilmente essere notato in questa risposta.
Caleb,

1
Questa sarebbe la risposta
pylover il

1
Nota l'uso di -nalcune delle altre risposte per sopprimere la nuova riga generata da echo, il che è utile se il tuo secondo comando produce solo una riga di output e vuoi che siano più facili da leggere.
William Turrell,

Ottima soluzione Funziona come un fascino.
Philippe Delteil,


52

Uno dei seguenti:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
A cosa serve il carattere di sottolineatura prima di {}?
qed

2
@qed: è un valore usa e getta che detiene il posto di $0. Prova questo con "foobar" invece di "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- l'output: "[foobar] [/ usr / bin / find]".
In pausa fino a nuovo avviso.

1
@XuWang: Sì, direi che è il caso. Come sai, di $0solito è il nome del programma ( ARGV[0]).
In pausa fino a nuovo avviso.

3
È fondamentale, per questo metodo, che lo script passato sh -csia tra virgolette singole, non doppie. Altrimenti $1è nell'ambito sbagliato.
Nick,

1
Le virgolette di @Nick non hanno nulla a che fare con esso: puoi scrivere '$1'con virgolette doppie purché sfugga alla variabile ( "\$1"). Puoi sfuggire anche ad altri personaggi ( "\"").
Camilo Martin,

22

Un altro modo è come questo:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

in una riga

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • "multiple_cmd() " - è una funzione
  • "export -f multiple_cmd " - lo esporterà in modo che qualsiasi altra subshell possa vederlo
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - trova che eseguirà la funzione sul tuo esempio

In questo modo multiple_cmd può essere lungo e complesso, secondo le tue necessità.

Spero che sia di aiuto.


perfetto, proprio quello di cui avevo bisogno!
Anentropico

non funziona per me su OSX
Thomas,

@Thomas lo fa, ma prova questa fodera, testata in osx. Ho creato una directory chiamata 'aaa' con alcuni file / directory e CDd. Quindi, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop

@barlop, ci proverò; Grazie!
Thomas,

14

C'è un modo più semplice:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

In alternativa:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
i nomi dei file possono avere nuove righe, ecco perché find ha l'argomento -print0 e xargs ha l'argomento -0
abasterfield

@abasterfield Spero sempre di non trovare mai quelli nel selvaggio lol
Camilo Martin il

1
quello che volevo fare era "trova ... -exec zcat {} | wc -l \;" che non ha funzionato. Tuttavia, trova ... | durante la lettura del file -r; echo "$ file: zcat $file | wc -l"; fatto funziona, quindi grazie!
Greg Dougherty,

Nel commento sopra ho "back ticks" intorno a "zcat $ file | wc -l". Sfortunatamente SO li trasforma in formattazione, quindi l'ho aggiunto come risposta effettiva con il codice corretto visibile
Greg Dougherty,

1
@GregDougherty Puoi sfuggire ai backtick `per fare in modo che tu usi le barre rovesciate in questo modo: \​`(tuttavia, questa è un'altra buona ragione da usare $()invece).
Camilo Martin,

8

Non so se puoi farlo con find, ma una soluzione alternativa sarebbe quella di creare uno script di shell ed eseguirlo con find.

lastline.sh:

echo $(tail -1 $1),$1

Rendi eseguibile lo script

chmod +x lastline.sh

Utilizzare find:

find . -name "*.txt" -exec ./lastline.sh {} \;

7
i backtick sono obsoleti, si prega di incoraggiare l'uso di $ (...) che è meglio leggibile, indipendente dal font e facile da annidare. Grazie.
utente sconosciuto

4

La prima risposta di Denis è la risposta per risolvere il problema. Ma in realtà non è più una ricerca con diversi comandi in una sola esecuzione come suggerisce il titolo. Per rispondere all'ex dirigente con diversi comandi, dovremo cercare qualcos'altro da risolvere. Ecco un esempio:

Mantieni le ultime 10000 righe di file .log che sono state modificate negli ultimi 7 giorni usando il comando 1 exec usando i riferimenti {}

1) guarda cosa farà il comando su quali file:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2) Fallo: (nota non più "\>" ma solo ">" questo è voluto)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


Ma questo si romperà se uno dei nomi di file ha uno spazio, credo.
Camilo Martin,

4

Grazie a Camilo Martin, sono stato in grado di rispondere a una domanda correlata:

Quello che volevo fare era

find ... -exec zcat {} | wc -l \;

che non ha funzionato. Però,

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

funziona, quindi grazie!


2

Estendere la risposta di @ Tinker,

Nel mio caso, dovevo creare un command | command | commandinterno -execper stampare sia il nome del file sia il testo trovato nei file contenenti un determinato testo.

Sono stato in grado di farlo con:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

il risultato è:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
Puoi anche stampare il nome del file e il contenuto grep'd su una sola riga passando /dev/nullcome secondo argomento al grepcomando con un -execparametro:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

dovrebbe usare xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

un altro (lavorando su osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
Questo trascura un caso d'uso importante per find- situazioni in cui il numero di file corrispondenti è troppo grande per una riga di comando. -execè un modo per aggirare questo limite. La connessione a un'utilità non ne beneficia.
Chris Johnson,

xargs -nesiste per scegliere il numero di corrispondenze per invocazione. xargs -n 1 foocmdverrà eseguito foocmd {}per ogni partita.
AndrewF,

0

Una find+xargsrisposta

L'esempio seguente trova tutti i .htmlfile e crea una copia con l' .BAKestensione aggiunta (ad es. 1.html> 1.html.BAK).

Singolo comando con più segnaposto

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

Più comandi con più segnaposto

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

Questo comando funzionerà anche con file che iniziano con un trattino o contengono spazi come -my file.htmlgrazie alla quotazione dei parametri e al --dopo cpche segnala cpla fine dei parametri e l'inizio dei nomi dei file effettivi.

-print0 convoglia i risultati con terminatori a byte nullo.


per xargs il -I {}parametro definisce {}come segnaposto; puoi usare qualsiasi segnaposto che ti piace; -0indica che gli elementi di input sono separati da null.

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.