Situasinya adalah, saya memiliki pemutar MP3 mpg321
yang menerima daftar file sebagai argumen. Saya menyimpan musik saya di direktori bernama "musik", di mana ada beberapa direktori lagi. Saya hanya ingin memainkan semuanya, jadi saya menjalankan program dengan
mpg321 $(find /music -iname "*\.mp3")
. Masalahnya adalah, beberapa nama file memiliki spasi di dalamnya, dan program memecah nama-nama itu menjadi bagian-bagian yang lebih kecil dan mengeluh tentang file yang hilang. Membungkus hasil find
dalam tanda kutip
mpg321 "$(find /music -iname "*\.mp3")"
tidak membantu karena semua akan menjadi satu "nama file" besar, yang jelas tidak ditemukan.
Bagaimana saya bisa melakukan ini? Jika itu penting, saya menggunakan bash
, tetapi akan segera beralih zsh
.
sumber
mpg321 $(find /music -iname "*\.mp3" -print0)
tidak?mpg321
tidak ada hubungannya dengan itu, itu shell yang memecah outputfind
menjadi argumen yang terpisah. Dan-print0 | xargs -0
akan bekerja dengan setiap kemungkinan nama file.Dengan GNU find, Anda juga dapat menggunakan
-print0
danxargs -0
, tetapi tidak ada gunanya mempelajari alat lain. The-exec ... {} +
sintaks mendapat sedikit menyebutkan karena Linux memperolehnya lambat-print0
, tapi tidak ada alasan untuk tidak menggunakannya sekarang.Dengan zsh atau bash 4, ini jauh lebih sederhana:
Hanya di zsh, Anda dapat membuat (bagian dari) pola tidak sensitif-huruf:
sumber
Saya pikir solusi Steven adalah yang terbaik, tetapi cara lain adalah dengan menggunakan
-I
flag xargs , yang memungkinkan Anda menentukan string yang kemudian akan diganti dalam perintah dengan argumen (bukan hanya menambahkan argumen ke akhir perintah). Anda dapat menggunakannya untuk mengutip argumen:sumber
-0
flag xargs tanpa menemukan-print0
. Juga,-I
bendera xargs memiliki sejumlah konsekuensi. Tidak seperti sekadar polosxargs
yang akan memampatkan semua baris dari stdin menjadi satu perintah,xargs -I
akan mengeksekusimpg321
berpotensi ratusan atau ribuan kali (satu kali untuk setiap file) yang tentunya bukan maksudnya. Juga perlu diingat bahwa mengutip foo tidak perlu sepertixargs
halnya ini secara internal. Perhatikan jika Anda mengompres semua nama file ke baris baris dan mengirimkannyaxargs -I
, mereka tidak akan terbuka.Biasanya yang terbaik adalah langsung
-exec ${tgt_process} \{\} +
tetapi jika Anda memang perlu mendapatkan daftar nama file yang dibatasi dengan andal dalam file atau streaming darifind
alasan apa pun, maka Anda dapat melakukan ini:Apa yang Anda dapatkan dari itu adalah dua string unik . Di bagian atas setiap nama file adalah string
\n///
dan di bagian belakang setiap nama file adalah string///\n
. Dua string ini tidak terjadi di tempat lain difind
output kecuali untuk posisi-posisi itu terlepas dari karakter apa pun yang terdapat pada nama file.Selain itu penggunaan di atas adalah POSIX portabel awal dan dapat diandalkan untuk bekerja pada hampir semua sistem unix. Ini tidak benar dari penggunaan pembatas byte nol - meskipun nyaman - direkomendasikan oleh beberapa orang lain.
Tetapi, sekali lagi, ini hanya perlu jika Anda tidak dapat langsung
-exec
Anda$tgt_process
untuk alasan apa pun, karena itu harus menjadi tujuan Anda. Untuk satu hal, metode di atas masih membutuhkan parsing. Sebagai contoh, jika Anda ingin setiap shell nama file dikutip, Anda harus terlebih dahulu memastikan setiap hard-quote dalam nama file tersebut lolos:Itu menghasilkan array yang benar shell-lolos nama file terlepas dari apa pun karakter penyusunnya. Sekarang Anda hanya perlu berharap bahwa aplikasi Anda di sisi penerima tidak akan memotongnya.
sumber
Cara lain untuk melakukan ini adalah keluar dari semua karakter khusus yang ada dalam nama file Anda. Misalnya:
Ini pada dasarnya akan meneruskan nama file yang diloloskan dengan benar ke xargs untuk dieksekusi dan tidak akan ada masalah.
sumber
sed 's|.|\\&|g'
- itulah yang direkomendasikan POSIX.