temukan -exec dengan banyak perintah

430

Saya mencoba menggunakan find -exec dengan banyak perintah tanpa hasil. Adakah yang tahu jika perintah seperti berikut ini dimungkinkan?

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

Pada dasarnya, saya mencoba untuk mencetak baris terakhir dari setiap file txt di direktori saat ini dan mencetak di akhir baris, koma diikuti oleh nama file.

Andy
sumber
4
superuser.com/questions/236601/…
Ignacio Vazquez-Abrams
1
Sejauh memeriksa kemungkinan perintah, apakah Anda tidak mencobanya di sistem Anda?
Sriram
1
Dari findhalaman manual: 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
@ JVE999 tautan rusak, alternatif di ss64.com/bash/find.html
Keith M

Jawaban:

659

findmenerima beberapa -execbagian dari perintah. Sebagai contoh:

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

Perhatikan bahwa dalam kasus ini perintah kedua hanya akan berjalan jika yang pertama kembali dengan sukses, seperti yang disebutkan oleh @Caleb. Jika Anda ingin kedua perintah dijalankan terlepas dari keberhasilan atau kegagalannya, Anda bisa menggunakan konstruk ini:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;
Menggerumit
sumber
bagaimana cara grep dua kali? ini gagal: find ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "my_string" {} \;
rajeev
51
@rajeev Eksekutif kedua hanya akan berjalan jika kode pengembalian untuk pengembalian pertama berhasil, jika tidak maka akan dilewati. Ini mungkin harus dicatat dalam jawaban ini.
Caleb
1
Ini akan menjadi jawabannya
pylover
1
Perhatikan penggunaan -ndi beberapa jawaban lain untuk menekan baris baru yang dihasilkan oleh gema, yang berguna jika perintah kedua Anda hanya menghasilkan satu baris output dan Anda ingin mereka lebih mudah dibaca.
William Turrell
Solusi bagus Bekerja seperti pesona.
Philippe Delteil
98
find . -type d -exec sh -c "echo -n {}; echo -n ' x '; echo {}" \;
Avari
sumber
3
Jika Anda ingin menjalankan Bash bukan Bourne Anda juga dapat menggunakan ... -exec bash -c ...bukan ... -exec sh -c ....
Kenny Evitt
9
Jangan pernah menyematkan {}kode shell. Lihat unix.stackexchange.com/questions/156008/…
Kusalananda
3
+1 @Kusalananda Menyuntikkan nama file yang rapuh dan tidak aman. Gunakan parameter. lihat SC2156
pambda
52

Satu dari berikut:

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"' _ {} \;
Dijeda sampai pemberitahuan lebih lanjut.
sumber
12
Untuk apa garis bawah sebelum {}?
qed
2
@qed: Ini adalah nilai membuang yang memegang tempat $0. Coba ini dengan "foobar" bukan "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- output: "[foobar] [/ usr / bin / find]".
Dijeda sampai pemberitahuan lebih lanjut.
1
@ XuWang: Ya, saya akan mengatakan itu masalahnya. Seperti yang Anda tahu, $0biasanya nama program ( ARGV[0]).
Dijeda sampai pemberitahuan lebih lanjut.
3
Sangat penting, untuk metode ini, bahwa skrip yang diteruskan sh -cadalah dalam tanda kutip tunggal, bukan ganda. Kalau tidak, $1ada dalam ruang lingkup yang salah.
Nick
1
@ Kutipan Tidak ada hubungannya dengan itu - Anda dapat menulis '$1'dengan tanda kutip ganda selama Anda lolos dari variabel ( "\$1"). Anda dapat keluar dari karakter lain juga ( "\"").
Camilo Martin
22

Cara lain adalah seperti ini:

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

dalam satu baris

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • " multiple_cmd()" - adalah sebuah fungsi
  • " export -f multiple_cmd" - akan mengekspornya sehingga subkulit lain dapat melihatnya
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - temukan yang akan menjalankan fungsi pada contoh Anda

Dengan cara ini multiple_cmd dapat menjadi panjang dan kompleks, seperti yang Anda butuhkan.

Semoga ini membantu.

al3x2ndru
sumber
sempurna, hanya apa yang saya butuhkan!
Anentropic
tidak berfungsi untuk saya di OSX
Thomas
@ Thomas tidak mencoba liner yang satu ini, diuji di osx. Saya membuat direktori yang disebut 'aaa' dengan beberapa file / dir di sana dan CDD untuk itu. Lalu, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop
@barlop, saya akan mencobanya; Terima kasih!
Thomas
14

Ada cara yang lebih mudah:

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

Kalau tidak:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"
Camilo Martin
sumber
1
nama file dapat memiliki baris baru di dalamnya, inilah mengapa find memiliki argumen -print0 dan xargs memiliki argumen -0
abasterfield
@ abasterfield Saya selalu berharap untuk tidak menemukan yang di lol liar
Camilo Martin
1
yang ingin saya lakukan adalah "menemukan ... -exec zcat {} | wc -l \;" yang tidak berhasil. Namun, temukan ... | saat membaca file -r; lakukan echo "$ file: zcat $file | wc -l"; selesai tidak bekerja, jadi terima kasih!
Greg Dougherty
Dalam komentar di atas saya memiliki "kutu kembali" di sekitar "zcat $ file | wc -l". Sayangnya SO mengubahnya menjadi pemformatan, jadi saya telah menambahkannya sebagai jawaban aktual dengan kode yang benar terlihat
Greg Dougherty
1
@GregDougherty Anda dapat menghindari backticks `untuk melakukan itu Anda menggunakan backslash seperti: \​`(masih, itu alasan lain yang baik untuk digunakan $()sebagai gantinya).
Camilo Martin
8

Saya tidak tahu apakah Anda bisa melakukan ini dengan find, tetapi solusi alternatifnya adalah membuat skrip shell dan menjalankannya dengan find.

lastline.sh:

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

Jadikan skrip dapat dieksekusi

chmod +x lastline.sh

Gunakan find:

find . -name "*.txt" -exec ./lastline.sh {} \;
Andrea Spadaccini
sumber
7
backticks sudah usang, harap dorong penggunaan $ (...) yang lebih baik dibaca, tergantung font, dan mudah untuk bersarang. Terima kasih.
pengguna tidak diketahui
4

Jawaban pertama Denis adalah jawaban untuk menyelesaikan masalah. Tetapi sebenarnya itu tidak lebih menemukan dengan beberapa perintah hanya dalam satu exec seperti judulnya. Untuk menjawab satu eksekutif dengan beberapa perintah, kita harus mencari sesuatu yang lain untuk diselesaikan. Berikut ini sebuah contoh:

Menyimpan 10.000 baris terakhir file .log yang telah dimodifikasi dalam 7 hari terakhir menggunakan 1 perintah exec menggunakan beberapa {} referensi

1) lihat apa yang akan dilakukan perintah pada file mana:

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

2) Lakukan: (perhatikan tidak ada lagi "\>" tetapi hanya ">" ini yang diinginkan)

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

Eric Duruisseau
sumber
Tapi ini akan pecah jika salah satu nama file memiliki ruang, saya percaya.
Camilo Martin
4

Berkat Camilo Martin, saya bisa menjawab pertanyaan terkait:

Yang ingin saya lakukan adalah

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

yang tidak berhasil. Namun,

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

berhasil, jadi terima kasih!

Greg Dougherty
sumber
2

Memperluas jawaban Tinker,

Dalam kasus saya, saya perlu membuat bagian command | command | commanddalam -execuntuk mencetak nama file dan teks yang ditemukan dalam file yang mengandung teks tertentu.

Saya bisa melakukannya dengan:

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

hasilnya adalah:

    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
pengguna9869932
sumber
1
Anda juga dapat mencetak nama file dan konten grep'd pada satu baris dengan mengirimkan /dev/nullsebagai argumen kedua ke grepperintah dengan satu -execparameter:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth
1

harus menggunakan xargs :)

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

satu lagi (bekerja di osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'
smapira
sumber
3
Ini mengabaikan kasus penggunaan utama untuk find- situasi di mana jumlah file yang cocok terlalu besar untuk baris perintah. -execadalah cara untuk melewati batas ini. Menyalurkan ke utilitas melewatkan manfaat itu.
Chris Johnson
xargs -nada untuk memilih jumlah kecocokan per doa. xargs -n 1 foocmdakan mengeksekusi foocmd {}untuk setiap pertandingan.
AndrewF
0

Sebuah find+xargsjawaban.

Contoh di bawah ini menemukan semua .htmlfile dan membuat salinan dengan .BAKekstensi yang ditambahkan (misalnya 1.html> 1.html.BAK).

Perintah tunggal dengan beberapa tempat penampung

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

Banyak perintah dengan beberapa tempat penampung

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

Perintah ini juga akan bekerja dengan file yang dimulai dengan tanda hubung atau berisi spasi seperti -my file.htmlterima kasih kepada parameter mengutip dan --setelah cpitu memberi sinyal ke cpakhir parameter dan awal nama file yang sebenarnya.

-print0 menyalurkan hasilnya dengan terminator null-byte.


untuk xargs , -I {}parameter mendefinisikan {}sebagai placeholder; Anda dapat menggunakan placeholder mana pun yang Anda suka; -0menunjukkan bahwa item input dipisahkan nol.

ccpizza
sumber