Perhatikan bahwa file juga dapat memiliki baris baru dalam nama file mereka. Itu sebabnya ada find -print0dan xargs -0.
Daniel Beck
Jawaban:
12
Idealnya Anda tidak melakukannya dengan cara sama sekali, karena mengurai nama file dengan benar dalam skrip shell selalu sulit (perbaiki untuk spasi, Anda masih akan mengalami masalah dengan karakter yang disematkan lainnya, khususnya baris baru). Ini bahkan terdaftar sebagai entri pertama di halaman BashPitfalls.
Yang mengatakan, ada cara untuk hampir melakukan apa yang Anda inginkan:
oIFS=$IFS
IFS=$'\n'
find . -name '*.txt' | while read -r i; do
# use "$i" with whatever you're doing
done
IFS=$oIFS
Ingatlah untuk juga mengutip $iketika menggunakannya, untuk menghindari hal-hal lain menafsirkan spasi nanti. Juga ingat untuk mengatur $IFSkembali setelah menggunakannya, karena tidak melakukan hal itu akan menyebabkan kesalahan membingungkan nantinya.
Ini memang memiliki satu peringatan lain yang terlampir: apa yang terjadi di dalam whileloop dapat terjadi dalam subkulit, tergantung pada shell yang tepat Anda gunakan, sehingga pengaturan variabel mungkin tidak bertahan. Versi forloop menghindari itu tetapi dengan harga itu, bahkan jika Anda menerapkan $IFSsolusi untuk menghindari masalah dengan spasi, Anda kemudian akan mendapat masalah jika findpengembalian terlalu banyak file.
Pada titik tertentu perbaikan yang benar untuk semua ini menjadi melakukannya dalam bahasa seperti Perl atau Python, bukan shell.
Saya suka gagasan hanya menggunakan Python untuk menghindari semua ini.
Scott C Wilson
12
Gunakan find -print0dan pipa untuk xargs -0, atau menulis program C kecil Anda sendiri dan pipa ke program C kecil Anda. Ini untuk apa -print0dan -0diciptakan untuk.
Script shell bukan cara terbaik untuk menangani nama file dengan spasi di dalamnya: Anda bisa melakukannya, tetapi itu menjadi kikuk.
Anda dapat mengatur "pemisah bidang internal" ( IFS) ke sesuatu selain ruang untuk pemisahan argumen loop, misalnya
ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
IFS=${ORIGIFS}
#do stuff
done
IFS=${ORIGIFS}
Saya reset IFSsetelah penggunaannya di find, sebagian besar karena tampilannya bagus, saya pikir. Saya belum melihat ada masalah dalam mengaturnya ke baris baru, tapi saya pikir ini "bersih".
Metode lain, tergantung pada apa yang ingin Anda lakukan dengan output dari find, adalah menggunakan langsung -execdengan findperintah, atau menggunakan -print0dan menyalurkannya ke xargs -0. Dalam kasus pertama findmenangani nama file yang melarikan diri. Dalam hal -print0ini, findcetak hasilnya dengan pemisah nol, dan kemudian xargsbagi ini. Karena tidak ada nama file yang dapat mengandung karakter itu (apa yang saya ketahui), ini selalu aman juga. Ini sebagian besar berguna dalam kasus-kasus sederhana; dan biasanya bukan pengganti yang bagus untuk forloop penuh .
Menggunakan find -print0dikombinasikan dengan xargs -0benar-benar kuat terhadap nama file hukum, dan merupakan salah satu metode yang paling dapat dikembangkan Misalnya, Anda menginginkan daftar setiap file PDF dalam direktori saat ini. Anda bisa menulis
Ini akan menemukan setiap PDF (via -iname '*.pdf') di direktori saat ini ( .) dan setiap sub-direktori, dan meneruskannya sebagai argumen ke echoperintah. Karena kami menentukan -n 1opsi, xargshanya akan melewati satu argumen pada satu waktu untuk echo. Seandainya kita menghilangkan opsi itu, xargsakan melewati sebanyak mungkin echo. (Anda dapat echo short input | xargs --show-limitsmelihat berapa byte yang diizinkan dalam baris perintah.)
Apa yang xargsdilakukan, tepatnya?
Kita dapat dengan jelas melihat efek xargspada inputnya - dan efek -nkhususnya - dengan menggunakan skrip yang menggemakan argumennya dengan cara yang lebih tepat daripada echo.
Saya tidak setuju dengan bashbashers, karena bash, bersama dengan set alat * nix, cukup mahir dalam menangani file (termasuk yang namanya memiliki spasi putih).
Sebenarnya, findmemberi Anda kendali butir yang baik untuk memilih file mana yang akan diproses ... Di sisi bash, Anda benar-benar hanya perlu menyadari bahwa Anda harus membuat Anda merangkai bash words; biasanya dengan menggunakan "tanda kutip ganda", atau mekanisme lain seperti menggunakan IFS, atau temukan{}
Perhatikan bahwa dalam sebagian besar / banyak situasi Anda tidak perlu mengatur dan mengatur ulang IFS; cukup gunakan IFS secara lokal seperti ditunjukkan dalam contoh di bawah ini. Ketiganya menangani ruang putih dengan baik. Anda juga tidak memerlukan struktur loop "standar", karena find \;secara efektif adalah loop; cukup masukkan logika loop Anda ke fungsi bash (jika Anda tidak memanggil alat standar).
Ada beberapa validitas untuk kedua perspektif. Ketika saya hanya mengerjakan file saya sendiri, saya hanya akan menggunakan find dan tidak perlu khawatir, karena file saya tidak memiliki spasi (atau carriage return!) Dalam nama mereka. Tetapi ketika Anda mulai bekerja dengan file orang lain, Anda harus menggunakan teknik yang lebih kuat.
find -print0
danxargs -0
.Jawaban:
Idealnya Anda tidak melakukannya dengan cara sama sekali, karena mengurai nama file dengan benar dalam skrip shell selalu sulit (perbaiki untuk spasi, Anda masih akan mengalami masalah dengan karakter yang disematkan lainnya, khususnya baris baru). Ini bahkan terdaftar sebagai entri pertama di halaman BashPitfalls.
Yang mengatakan, ada cara untuk hampir melakukan apa yang Anda inginkan:
Ingatlah untuk juga mengutip
$i
ketika menggunakannya, untuk menghindari hal-hal lain menafsirkan spasi nanti. Juga ingat untuk mengatur$IFS
kembali setelah menggunakannya, karena tidak melakukan hal itu akan menyebabkan kesalahan membingungkan nantinya.Ini memang memiliki satu peringatan lain yang terlampir: apa yang terjadi di dalam
while
loop dapat terjadi dalam subkulit, tergantung pada shell yang tepat Anda gunakan, sehingga pengaturan variabel mungkin tidak bertahan. Versifor
loop menghindari itu tetapi dengan harga itu, bahkan jika Anda menerapkan$IFS
solusi untuk menghindari masalah dengan spasi, Anda kemudian akan mendapat masalah jikafind
pengembalian terlalu banyak file.Pada titik tertentu perbaikan yang benar untuk semua ini menjadi melakukannya dalam bahasa seperti Perl atau Python, bukan shell.
sumber
Gunakan
find -print0
dan pipa untukxargs -0
, atau menulis program C kecil Anda sendiri dan pipa ke program C kecil Anda. Ini untuk apa-print0
dan-0
diciptakan untuk.Script shell bukan cara terbaik untuk menangani nama file dengan spasi di dalamnya: Anda bisa melakukannya, tetapi itu menjadi kikuk.
sumber
Anda dapat mengatur "pemisah bidang internal" (
IFS
) ke sesuatu selain ruang untuk pemisahan argumen loop, misalnyaSaya reset
IFS
setelah penggunaannya di find, sebagian besar karena tampilannya bagus, saya pikir. Saya belum melihat ada masalah dalam mengaturnya ke baris baru, tapi saya pikir ini "bersih".Metode lain, tergantung pada apa yang ingin Anda lakukan dengan output dari
find
, adalah menggunakan langsung-exec
denganfind
perintah, atau menggunakan-print0
dan menyalurkannya kexargs -0
. Dalam kasus pertamafind
menangani nama file yang melarikan diri. Dalam hal-print0
ini,find
cetak hasilnya dengan pemisah nol, dan kemudianxargs
bagi ini. Karena tidak ada nama file yang dapat mengandung karakter itu (apa yang saya ketahui), ini selalu aman juga. Ini sebagian besar berguna dalam kasus-kasus sederhana; dan biasanya bukan pengganti yang bagus untukfor
loop penuh .sumber
Menggunakan
find -print0
denganxargs -0
Menggunakan
find -print0
dikombinasikan denganxargs -0
benar-benar kuat terhadap nama file hukum, dan merupakan salah satu metode yang paling dapat dikembangkan Misalnya, Anda menginginkan daftar setiap file PDF dalam direktori saat ini. Anda bisa menulisIni akan menemukan setiap PDF (via
-iname '*.pdf'
) di direktori saat ini (.
) dan setiap sub-direktori, dan meneruskannya sebagai argumen keecho
perintah. Karena kami menentukan-n 1
opsi,xargs
hanya akan melewati satu argumen pada satu waktu untukecho
. Seandainya kita menghilangkan opsi itu,xargs
akan melewati sebanyak mungkinecho
. (Anda dapatecho short input | xargs --show-limits
melihat berapa byte yang diizinkan dalam baris perintah.)Apa yang
xargs
dilakukan, tepatnya?Kita dapat dengan jelas melihat efek
xargs
pada inputnya - dan efek-n
khususnya - dengan menggunakan skrip yang menggemakan argumennya dengan cara yang lebih tepat daripadaecho
.Perhatikan bahwa ia menangani spasi dan baris baru dengan sangat baik,
yang akan sangat menyusahkan dengan solusi umum berikut:
Catatansumber
Saya tidak setuju dengan
bash
bashers, karenabash
, bersama dengan set alat * nix, cukup mahir dalam menangani file (termasuk yang namanya memiliki spasi putih).Sebenarnya,
find
memberi Anda kendali butir yang baik untuk memilih file mana yang akan diproses ... Di sisi bash, Anda benar-benar hanya perlu menyadari bahwa Anda harus membuat Anda merangkaibash words
; biasanya dengan menggunakan "tanda kutip ganda", atau mekanisme lain seperti menggunakan IFS, atau temukan{}
Perhatikan bahwa dalam sebagian besar / banyak situasi Anda tidak perlu mengatur dan mengatur ulang IFS; cukup gunakan IFS secara lokal seperti ditunjukkan dalam contoh di bawah ini. Ketiganya menangani ruang putih dengan baik. Anda juga tidak memerlukan struktur loop "standar", karena find
\;
secara efektif adalah loop; cukup masukkan logika loop Anda ke fungsi bash (jika Anda tidak memanggil alat standar).Dan, dua contoh lagi
'temukan
also allows you to pass multiple filenames as args to you script ..(if it suits your need: use
+instead
\; `)sumber