Dalam kasus-kasus mengejutkan yang sering terjadi di mana apa yang sebenarnya perlu Anda lakukan adalah memproses semua baris yang tidak kosong di dalam suatu variabel dengan beberapa cara (termasuk menghitungnya), Anda dapat mengatur IFS menjadi hanya baris baru dan kemudian menggunakan mekanisme pemisahan kata shell untuk memecah baris yang tidak kosong terpisah.
Misalnya, inilah fungsi shell kecil yang menjumlahkan baris-baris tidak kosong di dalam semua argumen yang disediakan:
lines() (
IFS='
'
set -f #disable pathname expansion
set -- $*
echo $#
)
Tanda kurung, bukan kawat gigi, digunakan di sini untuk membentuk perintah majemuk untuk fungsi tubuh. Ini membuat fungsi dieksekusi dalam subkulit sehingga tidak mencemari pengaturan variabel IFS dan pathname dunia luar pada setiap panggilan.
Jika Anda ingin mengulang lebih dari baris yang tidak kosong, Anda dapat melakukannya dengan cara yang sama:
IFS='
'
set -f
for line in $lines
do
printf '[%s]\n' $line
done
Memanipulasi IFS dengan cara ini adalah teknik yang sering diabaikan, juga berguna untuk melakukan hal-hal seperti parsing nama path yang dapat berisi spasi dari input kolom-dibatasi tab. Namun, Anda perlu menyadari bahwa dengan sengaja menghapus karakter spasi yang biasanya termasuk dalam pengaturan default space-tab-newline IFS dapat akhirnya menonaktifkan pemisahan kata di tempat-tempat di mana Anda biasanya berharap melihatnya.
Misalnya, jika Anda menggunakan variabel untuk membangun baris perintah yang rumit untuk sesuatu seperti ffmpeg
, Anda mungkin ingin memasukkan -vf scale=$scale
hanya ketika variabel scale
diatur ke sesuatu yang tidak kosong. Biasanya Anda bisa mencapainya dengan ${scale:+-vf scale=$scale}
tetapi jika IFS tidak menyertakan karakter spasi biasanya pada saat ekspansi parameter ini dilakukan, ruang antara -vf
dan scale=
tidak akan digunakan sebagai pemisah kata dan ffmpeg
akan dilewati -vf scale=$scale
sebagai argumen tunggal, yang tidak akan mengerti.
Untuk memperbaiki itu, Anda akan lebih baik perlu memastikan IFS didirikan lebih normal sebelum melakukan ${scale}
ekspansi, atau melakukan dua ekspansi: ${scale:+-vf} ${scale:+scale=$scale}
. Kata pemisahan yang dilakukan shell dalam proses penguraian awal baris perintah, berbeda dengan pemisahan yang dilakukan selama fase ekspansi pemrosesan baris perintah tersebut, tidak bergantung pada IFS.
Hal lain yang bisa bernilai saat Anda akan melakukan hal semacam ini akan menciptakan dua variabel global shell untuk memegang hanya tab dan hanya baris baru:
t=' '
n='
'
Dengan begitu Anda bisa memasukkan $t
dan $n
dalam ekspansi di mana Anda membutuhkan tab dan baris baru, daripada membuang semua kode Anda dengan spasi kosong yang dikutip. Jika Anda lebih suka menghindari spasi yang dikutip sama sekali dalam cangkang POSIX yang tidak memiliki mekanisme lain untuk melakukannya, printf
dapat membantu meskipun Anda memang perlu sedikit mengutak-atik untuk menghilangkan trailing baris baru dalam ekspansi perintah:
nt=$(printf '\n\t')
n=${nt%?}
t=${nt#?}
Kadang-kadang pengaturan IFS seolah-olah itu variabel lingkungan per-perintah berfungsi dengan baik. Misalnya, ini adalah loop yang membaca nama path yang diizinkan mengandung spasi dan faktor penskalaan dari setiap baris file input yang dibatasi-tab:
while IFS=$t read -r path scale
do
ffmpeg -i "$path" ${scale:+-vf scale=$scale} "${path%.*}.out.mkv"
done <recode-queue.txt
Dalam kasus ini, read
builtin melihat IFS diatur menjadi hanya tab, sehingga tidak akan membagi jalur input yang dibaca di spasi juga. Tapi IFS=$t set -- $lines
tidak berhasil: shell mengembang $lines
saat membangun set
argumen builtin sebelum mengeksekusi perintah, sehingga pengaturan sementara IFS dengan cara yang hanya berlaku selama eksekusi builtin sendiri terlambat. Inilah sebabnya cuplikan kode yang saya berikan di atas semuanya mengatur IFS dalam langkah terpisah, dan mengapa mereka harus berurusan dengan masalah melestarikannya.
wc -l
persis sama dengan aslinya:<<<$foo
menambahkan baris baru ke nilai$foo
(meskipun$foo
kosong). Saya menjelaskan dalam jawaban saya mengapa ini mungkin bukan yang diinginkan, tetapi itulah yang ditanyakan.