Saya relatif baru di Bash dan saya sedang mencoba melakukan sesuatu yang pada permukaannya tampak cukup mudah - jalankan find melalui hierarki direktori untuk mendapatkan semua file * .wma, pipa yang di-output ke perintah di mana saya mengkonversikannya ke mp3 dan simpan file yang dikonversi sebagai .mp3. Pemikiran saya adalah bahwa perintah tersebut harus terlihat seperti berikut (Saya telah meninggalkan perintah konversi audio dan saya menggunakan gema untuk ilustrasi):
$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3
Seperti yang saya pahami, argumen -print0 akan membiarkan saya menangani nama file yang memiliki spasi (yang banyak di antaranya dilakukan sebagai file musik). Saya kemudian mengharapkan (sebagai hasil dari xargs) bahwa setiap path file dari find ditangkap dalam f, dan bahwa menggunakan pencocokan substring / delete dari akhir string, bahwa saya harus menggemakan path file asli dengan mp3 ekstensi bukan wma. Namun, alih-alih hasil ini, saya melihat yang berikut:
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...
Jadi pertanyaan saya (selain dari spesifik 'apa yang saya lakukan salah di sini'), apakah ini - apakah nilai - nilai yang merupakan hasil dari operasi pipa perlu diperlakukan secara berbeda dalam operasi manipulasi string daripada yang merupakan hasil dari penugasan variabel ?
xargs
denganfind
. Muncul dengan-exec
opsi. Bisakah Anda menambahkan perintah yang akan Anda gunakan untuk pertanyaan Anda, dan seseorang dapat menunjukkan kepada Andafind
perintah yang benar ?{}
anggota)xargs
lebih cocok daripadaexec
. Lihat tumpukan-tumpukan ini stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs untuk sebuah case in point.Jawaban:
Seperti jawaban lain telah diidentifikasi,
${f%.*}
diperluas oleh shell sebelum menjalankanxargs
perintah. Anda perlu ekspansi ini terjadi sekali untuk setiap nama file, dengan variabel shellf
diatur ke nama file (lewat-I f
tidak melakukan itu:xargs
tidak memiliki gagasan variabel shell, itu mencari stringf
dalam perintah, jadi jika Anda ingin digunakan misalnyaxargs -I e echo …
akan mengeksekusi perintah seperti./somedir/somefile.wmacho .mp3
).Terus menggunakan pendekatan ini, beri tahu
xargs
untuk memanggil shell yang dapat melakukan ekspansi. Lebih baik, katakanfind
-xargs
adalah alat yang sebagian besar sudah usang dan sulit digunakan dengan benar, karena versi modern find memiliki konstruk yang melakukan pekerjaan yang sama (dan lebih banyak lagi) dengan lebih sedikit kesulitan pipa. Alih-alihfind … -print0 | xargs -0 command …
, jalankanfind … -exec command … {} +
.Argumennya
_
ada$0
di shell; nama file dilewatkan sebagai argumen posisional yangfor f; do …
berulang. Versi sederhana dari perintah ini mengeksekusi shell terpisah untuk setiap file, yang setara tetapi sedikit lebih lambat:Anda sebenarnya tidak perlu menggunakan di
find
sini, dengan asumsi Anda menjalankan shell yang cukup baru (ksh93, bash ≥4.0, atau zsh). Di bash, masukkanshopt -s globstar
Anda.bashrc
untuk mengaktifkan**/
pola glob untuk muncul kembali di subdirektori (dalam ksh, ituset -o globstar
). Maka Anda bisa lari(Jika Anda memiliki direktori yang dipanggil
*.wma
, tambahkan[ -f "$f" ] || continue
di awal loop.)sumber
Dalam hal evaluasi solusi Anda sebesar $ {f%. *}. Mp3 dilakukan di shell yang Anda gunakan untuk menjalankan seluruh perintah, bukan di shell yang dikerjakan oleh xargs . Dan di shell Anda tidak ada variabel f , itu diganti dengan string kosong.
Solusi menggunakan xargs dengan -I f :
Tetapi saya akan melakukannya dengan cara ini:
sumber