Saya memiliki skrip bash shell yang melakukan loop melalui semua direktori anak (tetapi bukan file) dari direktori tertentu. Masalahnya adalah beberapa nama direktori mengandung spasi.
Berikut adalah isi direktori pengujian saya:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
Dan kode yang melewati direktori:
for f in `find test/* -type d`; do
echo $f
done
Berikut hasilnya:
tes / Baltimore tes / Cherry Bukit tes / Edison tes / Baru York Kota tes / Philadelphia
Cherry Hill dan New York City diperlakukan sebagai 2 atau 3 entri terpisah.
Saya mencoba mengutip nama file, seperti ini:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
tapi tidak berhasil.
Pasti ada cara sederhana untuk melakukan ini.
Jawaban di bawah ini bagus. Tetapi untuk membuatnya lebih rumit - saya tidak selalu ingin menggunakan direktori yang terdaftar di direktori pengujian saya. Terkadang saya ingin memasukkan nama direktori sebagai parameter baris perintah.
Saya mengambil saran Charles untuk mengatur IFS dan menghasilkan yang berikut:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
dan ini berfungsi dengan baik kecuali jika ada spasi di argumen baris perintah (bahkan jika argumen tersebut dikutip). Misalnya, memanggil skrip seperti ini: test.sh "Cherry Hill" "New York City"
menghasilkan keluaran berikut:
ceri Bukit Baru York Kota
sumber
list="$@"
membuang sepenuhnya daftar nilai asli, menciutkannya menjadi string. Harap ikuti praktik dalam jawaban saya persis seperti yang diberikan - tugas seperti itu tidak dianjurkan di mana pun di dalamnya; jika Anda ingin meneruskan daftar argumen baris perintah ke program, Anda harus mengumpulkannya ke dalam larik, dan memperluas larik itu secara langsung.Jawaban:
Pertama, jangan lakukan seperti itu. Pendekatan terbaik adalah menggunakan
find -exec
dengan benar:Pendekatan aman lainnya adalah menggunakan daftar yang diakhiri NUL, meskipun ini memerlukan dukungan find Anda
-print0
:Anda juga dapat mengisi array dari find, dan meneruskan array itu nanti:
Jika temuan Anda tidak mendukung
-print0
, maka hasilnya tidak aman - di bawah ini tidak akan berperilaku seperti yang diinginkan jika ada file yang berisi baris baru dalam namanya (yang, ya, legal):Jika seseorang tidak akan menggunakan salah satu cara di atas, pendekatan ketiga (kurang efisien dalam hal penggunaan waktu dan memori, karena ia membaca seluruh output dari subproses sebelum melakukan pemisahan kata) adalah dengan menggunakan
IFS
variabel yang tidak tidak mengandung karakter spasi. Matikan globbing (set -f
) untuk mencegah string yang berisi karakter glob seperti[]
,*
atau?
diperluas:Terakhir, untuk kasus parameter baris perintah, Anda harus menggunakan array jika shell Anda mendukungnya (yaitu ksh, bash atau zsh):
akan mempertahankan pemisahan. Perhatikan bahwa kutipan (dan penggunaan
$@
daripada$*
) itu penting. Array juga dapat diisi dengan cara lain, seperti ekspresi glob:sumber
Namun, tidak berfungsi jika nama file berisi baris baru. Di atas adalah satu-satunya solusi yang saya tahu ketika Anda benar-benar ingin memiliki nama direktori dalam variabel. Jika Anda hanya ingin menjalankan beberapa perintah, gunakan xargs.
sumber
find . -type d | while read file; do ls "$file"; done
Berikut adalah solusi sederhana yang menangani tab dan / atau spasi di nama file. Jika Anda harus berurusan dengan karakter aneh lainnya di nama file seperti baris baru, pilih jawaban lain.
Direktori pengujian
Kode untuk masuk ke direktori
Nama file harus dikutip (
"$f"
) jika digunakan sebagai argumen. Tanpa tanda kutip, spasi bertindak sebagai pemisah argumen dan beberapa argumen diberikan ke perintah yang dipanggil.Dan hasilnya:
sumber
alias duc='ls -d * | while read D; do du -sh "$D"; done;'
alias duc='du -sh *(/)'
-print0
danIFS='' read -r -d '' f
.Ini sangat rumit dalam Unix standar, dan sebagian besar solusi melanggar baris baru atau karakter lain. Namun, jika Anda menggunakan kumpulan alat GNU, Anda dapat mengeksploitasi
find
opsi-print0
dan menggunakanxargs
opsi yang sesuai-0
(minus-nol). Ada dua karakter yang tidak dapat muncul dalam nama file sederhana; itu adalah garis miring dan NUL '\ 0'. Jelas sekali, garis miring muncul di nama jalur, jadi solusi GNU dengan menggunakan NUL '\ 0' untuk menandai akhir dari nama itu cerdik dan sangat mudah.sumber
Mengapa tidak dimasukkan saja
di depan untuk perintah? Ini mengubah pemisah bidang dari <Space> <Tab> <Newline> menjadi hanya <Newline>
sumber
sumber
-d $'\0'
persis sama dengan-d ''
- karena bash menggunakan string yang diakhiri NUL, karakter pertama dari string kosong adalah NUL, dan untuk alasan yang sama, NUL tidak dapat direpresentasikan sama sekali di dalam string C.saya menggunakan
Bukankah itu cukup?
Ide diambil dari http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
sumber
$(echo ...)
), tidak menangani nama file dengan ekspresi glob dengan benar, tidak menangani nama file yang berisi$'\b'
atau $ '\ n' karakter dengan benar, dan terlebih lagi mengubah beberapa spasi kosong menjadi karakter spasi tunggal di sisi keluaran karena kutipan salah.Jangan menyimpan daftar sebagai string; menyimpannya sebagai array untuk menghindari semua kebingungan pembatas ini. Berikut ini contoh skrip yang akan beroperasi di semua subdirektori pengujian, atau daftar yang disediakan pada baris perintahnya:
Sekarang mari kita coba ini di direktori pengujian dengan satu atau dua kurva dilemparkan:
sumber
"$@"
array, menambahkannya denganset -- "$@" "$f"
.Anda dapat menggunakan IFS (pemisah bidang internal) untuk sementara menggunakan:
sumber
ps jika hanya tentang spasi di input, maka beberapa tanda kutip ganda bekerja dengan lancar untuk saya ...
sumber
Untuk menambah apa yang dikatakan Jonathan : gunakan
-print0
opsi untukfind
sehubungan denganxargs
sebagai berikut:Itu akan menjalankan perintah
command
dengan argumen yang tepat; direktori dengan spasi di dalamnya akan dikutip dengan benar (artinya, direktori tersebut akan diteruskan sebagai satu argumen).sumber
Kode di atas akan mengonversi file .mov menjadi .avi. File .mov berada di folder berbeda dan nama folder juga memiliki spasi . Skrip saya di atas akan mengonversi file .mov ke file .avi di folder yang sama. Saya tidak tahu apakah itu membantu Anda.
Kasus:
Bersulang!
sumber
echo "$name" | ...
tidak berfungsi jikaname
ada-n
, dan bagaimana perilakunya dengan nama dengan urutan backslash-escape bergantung pada implementasi Anda - POSIX membuat perilakuecho
dalam kasus itu secara eksplisit tidak ditentukan (sedangkan POSIX yang diperluas XSI membuat perluasan urutan backslash-escape yang ditentukan standar perilaku , dan sistem GNU - termasuk bash - tanpaPOSIXLY_CORRECT=1
melanggar standar POSIX dengan mengimplementasikan-e
(sedangkan spesifikasi membutuhkanecho -e
untuk mencetak-e
pada keluaran).printf '%s\n' "$name" | ...
lebih aman.Harus berurusan dengan spasi putih di nama jalur juga. Apa yang akhirnya saya lakukan adalah menggunakan rekursi dan
for item in /path/*
:sumber
function
kata kunci - ini membuat kode Anda tidak kompatibel dengan POSIX sh, tetapi tidak memiliki tujuan berguna lainnya. Anda bisa mendefinisikan sebuah fungsi denganrecursedir() {
, menambahkan dua parens dan menghapus kata kunci fungsi, dan ini akan kompatibel dengan semua shell yang sesuai dengan POSIX.Ubah daftar file menjadi array Bash. Ini menggunakan pendekatan Matt McClure untuk mengembalikan larik dari fungsi Bash: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html Hasilnya adalah cara untuk mengonversi input multi-baris menjadi array Bash.
Pendekatan ini tampaknya berfungsi bahkan ketika ada karakter buruk, dan merupakan cara umum untuk mengonversi input apa pun ke array Bash. Kerugiannya adalah jika inputnya panjang, Anda dapat melebihi batas ukuran baris perintah Bash, atau menggunakan memori dalam jumlah besar.
Pendekatan di mana loop yang pada akhirnya bekerja pada daftar juga memiliki daftar yang disalurkan memiliki kelemahan bahwa membaca stdin tidak mudah (seperti meminta input pengguna), dan loop adalah proses baru sehingga Anda mungkin bertanya-tanya mengapa variabel Anda mengatur di dalam loop tidak tersedia setelah loop selesai.
Saya juga tidak suka pengaturan IFS, itu dapat mengacaukan kode lain.
sumber
IFS='' read
, pada baris yang sama, pengaturan IFS hanya ada untuk perintah baca, dan tidak menghindarinya. Tidak ada alasan untuk tidak menyukai pengaturan IFS dengan cara ini.Ya, saya melihat terlalu banyak jawaban yang rumit. Saya tidak ingin melewatkan output dari utilitas find atau menulis loop, karena find memiliki opsi "exec" untuk ini.
Masalah saya adalah saya ingin memindahkan semua file dengan ekstensi dbf ke folder saat ini dan beberapa di antaranya berisi ruang putih.
Saya mengatasinya begitu:
Terlihat sangat sederhana bagi saya
sumber
baru saja menemukan ada beberapa kesamaan antara pertanyaan saya dan pertanyaan Anda. Aparrently jika Anda ingin meneruskan argumen ke dalam perintah
untuk mencetaknya secara berurutan
perhatikan $ @ dikelilingi oleh tanda kutip ganda, beberapa catatan di sini
sumber
Saya membutuhkan konsep yang sama untuk memampatkan beberapa direktori atau file secara berurutan dari folder tertentu. Saya telah memecahkan menggunakan awk untuk mem-parsel daftar dari ls dan untuk menghindari masalah ruang kosong di nama.
Bagaimana menurut anda?
sumber
sumber
Bagi saya ini berhasil, dan cukup "bersih":
sumber
Baru saja mengalami masalah varian sederhana ... Konversikan file dari .flv yang diketik ke .mp3 (menguap).
menemukan secara rekursif semua file flash pengguna Macintosh dan mengubahnya menjadi audio (salin, tanpa transcode) ... seperti yang disebutkan di atas, mencatat bahwa membaca dan bukan hanya 'untuk file masuk
' akan keluar.
sumber
read
setelahin
satu kata lebih dalam daftar Anda iterasi. Apa yang Anda posting adalah versi yang sedikit rusak dari apa yang dimiliki penanya, yang tidak berfungsi. Anda mungkin bermaksud untuk memposting sesuatu yang berbeda, tetapi mungkin saja itu tercakup oleh jawaban lain di sini.