Daftar elemen dengan spasi di zsh

10

Saya telah mempelajari scripting zsh untuk semua 2 jam pada saat ini dan saya telah menabrak dinding. Saya ingin melihat daftar file yang mungkin memiliki ruang di dalamnya. Saya terbuka untuk pendekatan yang sama sekali berbeda dari contoh berikut selama mereka zsh karena zsh adalah apa yang saya pelajari, bukan tugas yang saya coba skrip.

files=(`ls`)
for f in $files; do
    print $f
done

Saya jelas tidak hanya menciptakan ulang ls, saya hanya ingin $fmenangkap seluruh nama file setiap iterasi dari loop.

Felix
sumber

Jawaban:

12

Pertama, saya berasumsi bahwa penggunaan lshanyalah sebuah contoh. Anda tidak dapat mem-parsing output lsdi shell apa pun, karena ini ambigu. Baca Mengapa Anda tidak perlu menguraikan output ls (1) jika ini berita untuk Anda. Dalam shell apa pun, untuk mendapatkan daftar file, gunakan wildcard, mis files=(*).

Di zsh, seperti di shell lain, hasil substitusi perintah dibagi menjadi kata-kata di karakter spasi (lebih tepatnya, sesuai dengan nilai IFS). (Tidak seperti shell lain, hasil substitusi perintah tidak mengalami globbing di zsh.) Jadi jika output dari lsperintah adalah

hello world
wibble

kemudian files=($(ls))menetapkan filesarray yang mengandung 3 unsur: hello, worlddan wibble.

Jika substitusi perintah dalam tanda kutip ganda, maka tidak ada pemisahan yang dilakukan. Anda dapat melakukan pemisahan kustom dengan flag ekspansi parameter . Gunakan @bendera untuk menunjukkan bahwa hasil pemisahan adalah menjadi array (anehnya, Anda perlu menjaga ekspansi dalam tanda kutip ganda, yaitu "${(@)…}", meskipun string yang dikutip ganda akan diperluas ke beberapa kata). Untuk pemisahan, gunakan sbendera, misalnya "${(@s:,:)…}"untuk membelah koma; yang fbendera perpecahan di baris saja.

files=("${(@f)$(ls)}")

Perhatikan bahwa cara yang tepat untuk beralih pada array secara umum adalah for f in $files[@], seperti $filesmenghapus elemen kosong (di sini, tidak masalah karena elemen tidak akan kosong).

print $fditafsirkan $fsebagai saklar jika dimulai dengan -dan memperluas garis miring terbalik di $f. Gunakan print -r -- $f, atau print -rn -- $fjika Anda tidak ingin menambahkan baris baru setelah string.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Terima kasih. Ya, itu hanya sebuah contoh. Tidak ada tujuan khusus dalam pikiran saat ini, saya hanya ingin tahu cara mengutip atau melarikan diri elemen daftar dari perintah apa pun yang mungkin memiliki hasil individu dengan ruang putih di dalamnya.
Felix
Satu hal yang saya anggap membingungkan adalah mengapa ekspresi luar () di file Anda = ("$ {(@ f) $ (ls)}") diperlukan dengan @f? Dan hanya bermain-main (f) tampaknya berfungsi dengan baik selama () muncul di luar.
Koobz
@ Koobz 1. Bagian luar (…)diperlukan sehingga filesmerupakan array dan bukan string (yang akan berisi gabungan dari baris-baris dari perintah, membatalkan pemisahan yang dilakukan oleh (f)). 2. Dengan foo=$'one\n\nthree', contrast print -rl $ {(f) foo} `dan print "${(@f)foo}". Kutipan ganda diperlukan untuk mempertahankan baris kosong, yang tidak akan Anda dapatkan dari lstetapi dapat terjadi dengan perintah lain.
Gilles 'SANGAT berhenti menjadi jahat'
Sangat dihargai. Adakah sajak atau alasan kegilaan ini? Bahkan di sana, yang membingungkan adalah mengapa tidak bagian luar () hanya membagi ulang string pada kata-kata individual lagi jika nilai tengahnya adalah string gabungan saya. Ini terlihat seperti interpolasi string dalam ruby ​​atau php misalnya.
Koobz
@Koobz Alasan perlakuan khusus terhadap kata-kata kosong adalah kompatibilitas historis dengan zsh versi lama yang lebih lama dilupakan. Alasan untuk tidak secara otomatis berpisah adalah bahwa itu adalah perilaku yang mengejutkan dan seringkali tidak diinginkan. Pemisahan otomatis adalah perilaku shell Bourne yang tidak ditiru zsh kecuali Anda menyuruhnya.
Gilles 'SO- stop being evil'
2

Di zsh Anda dapat menggunakan ekspansi shell yang tidak melakukan pemisahan kata secara default. Mencoba

for f in /path/to/files/*; do
    print ${f}
done
kamu-punkt
sumber