Saya telah melihat panduan skrip Bash menyarankan penggunaan array untuk bekerja dengan nama file yang mengandung spasi. Namun DashAsBinSh menyarankan bahwa array tidak portabel sehingga saya mencari cara yang sesuai dengan POSIX untuk bekerja dengan daftar nama file yang mungkin mengandung spasi.
Saya ingin memodifikasi skrip contoh di bawah ini agar dapat echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Ini skripnya
#!/usr/bin/env sh
INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps
dostuffwith() { echo $1; };
F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)
for f in $ALL_FILES
do
fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
dostuffwith $fpath
done
shell-script
filenames
quoting
posix
whitespace
Eero Aaltonen
sumber
sumber
Jawaban:
POSIX kerang memiliki satu array: parameter posisi (
$1
,$2
, dll, secara kolektif disebut sebagai"$@"
).Ini tidak nyaman karena hanya ada satu, dan itu merusak penggunaan parameter posisi lainnya. Parameter posisi bersifat lokal untuk suatu fungsi, yang terkadang merupakan berkah dan terkadang kutukan.
Jika nama file Anda dijamin tidak mengandung baris baru, Anda dapat menggunakan baris baru sebagai pemisah. Saat Anda memperluas variabel, pertama-tama matikan globbing dengan
set -f
dan atur daftar karakter pemisah bidang yangIFS
hanya berisi baris baru.Dengan item dalam daftar Anda dipisahkan oleh baris baru, Anda dapat menggunakan banyak perintah pemrosesan teks yang bermanfaat, khususnya
sort
.Ingatlah untuk selalu menempatkan tanda kutip ganda di sekitar substitusi variabel, kecuali ketika Anda secara eksplisit ingin pemisahan bidang terjadi (juga globbing, kecuali Anda mematikannya).
sumber
sort | uniq
langkah awal berfungsi sebagaimana dimaksud.Karena
$INPUT
variabel Anda menggunakan baris baru sebagai pemisah, saya akan menganggap bahwa file Anda tidak akan memiliki baris baru dalam namanya. Karena itu, ya, ada cara sederhana untuk mengulangi file dan menjaga spasi.Idenya adalah menggunakan
read
shell builtin. Biasanyaread
akan terpecah pada spasi putih mana saja, sehingga ruang akan memecahnya. Tapi Anda bisa mengaturIFS=$'\n'
dan itu akan terpecah hanya pada baris baru. Jadi Anda dapat mengulangi setiap baris dalam daftar Anda.Inilah solusi terkecil yang bisa saya temukan:
Pada dasarnya ia mengirimkan "$ INPUT" ke
awk
mana deduplicate berdasarkan nama file (itu terpecah/
dan kemudian mencetak baris jika item terakhir belum terlihat sebelumnya). Kemudian setelah awk membuat daftar path file, kita gunakanwhile read
untuk mengulangi daftar.sumber
while
loop, dan dengan demikiandostuffwith
dijalankan dalam subkulit. Jadi setiap variabel atau perubahan yang dilakukan pada shell yang berjalan akan hilang ketika loop selesai. Satu-satunya alternatif adalah menggunakan heredoc penuh, yang bukan tidak menyenangkan, tetapi saya pikir ini akan lebih baik.IFS="\n"
membagi karakter backslash dan n. Namunread file
, tidak ada pemisahan.IFS="\n"
masih berguna karena menghapus karakter kosong dari $ IFS yang jika tidak akan dilucuti di awal dan akhir input. Untuk membaca garis, sintaks kanonik adalahIFS= read -r line
, meskipunIFS=anything read -r line
(disediakan apapun tidak mengandung kosong) akan bekerja dengan baik.