Q1. Pemisahan bidang.
Apakah pemisahan bidang sama dengan pemisahan kata?
Ya, keduanya menunjuk ke ide yang sama.
Apakah pengaturannya IFS=''
sama dengan null, sama seperti string kosong juga?
Ya, ketiganya berarti sama: Tidak ada pemisahan bidang / kata yang harus dilakukan. Juga, ini memengaruhi bidang pencetakan (seperti halnya echo "$*"
) semua bidang akan digabungkan bersama tanpa ruang.
T3: (bagian a) Batalkan IFS.
Dalam spesifikasi POSIX, saya membaca yang berikut :
Jika IFS tidak disetel, shell akan berperilaku seolah-olah nilai IFS adalah <spasi> <tab> <newline> .
Yang persis sama dengan:
Dengan unset IFS
, shell akan berperilaku seolah-olah IFS adalah default.
Itu berarti 'Field splitting' akan persis sama dengan nilai IFS default, atau tidak disetel.
Itu TIDAK berarti bahwa IFS akan bekerja dengan cara yang sama di semua kondisi. Menjadi lebih spesifik, mengeksekusi OldIFS=$IFS
akan mengatur var OldIFS
menjadi nol , bukan default. Dan mencoba untuk mengatur IFS kembali, karena ini, IFS=OldIFS
akan mengatur IFS ke nol, tidak membiarkannya tetap seperti sebelumnya. Awas !!.
T3: (bagian b) Kembalikan IFS.
Bagaimana saya bisa mengembalikan nilai IFS ke default. Katakanlah saya ingin mengembalikan nilai default IFS. Bagaimana aku melakukan itu? (lebih khusus, bagaimana cara merujuk ke <tab> dan <newline> ?)
Untuk zsh, ksh, dan bash (AFAIK), IFS dapat diatur ke nilai default sebagai:
IFS=$' \t\n' # works with zsh, ksh, bash.
Selesai, Anda tidak perlu membaca yang lain.
Tetapi jika Anda perlu mengatur ulang IFS untuk sh, itu mungkin menjadi kompleks.
Mari kita lihat dari yang termudah hingga selesai tanpa kekurangan (kecuali kompleksitas).
1.- Batalkan IFS.
Kita bisa saja unset IFS
(Baca bagian Q3 a, di atas.).
2.- Tukar karakter.
Sebagai solusinya, menukar nilai tab dan baris baru membuatnya lebih mudah untuk mengatur nilai IFS, dan kemudian bekerja dengan cara yang setara.
Setel IFS ke <spasi><newline> <tab> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Sederhana? larutan:
Jika ada skrip anak yang memerlukan IFS diatur dengan benar, Anda selalu bisa menulis secara manual:
IFS = '
'
Di mana urutan yang diketik secara manual adalah:, IFS=
'spacetabnewline'urutan yang sebenarnya telah diketik dengan benar di atas (Jika Anda perlu mengonfirmasi, edit jawaban ini). Tetapi copy / paste dari browser Anda akan rusak karena browser akan menekan / menyembunyikan spasi. Itu membuatnya sulit untuk membagikan kode seperti yang ditulis di atas.
4.- Solusi lengkap.
Untuk menulis kode yang dapat disalin dengan aman biasanya melibatkan jalan keluar yang jelas.
Kami membutuhkan beberapa kode yang "menghasilkan" nilai yang diharapkan. Tetapi, meskipun secara konsepsi benar, kode ini TIDAK akan menetapkan trailing \n
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
Itu terjadi karena, di sebagian besar shell, semua baris baru $(...)
atau `...`
pergantian perintah dihapus pada ekspansi.
Kita perlu menggunakan trik untuk sh:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
Cara alternatif mungkin untuk menetapkan IFS sebagai nilai lingkungan dari bash (misalnya) dan kemudian memanggil sh (versi yang menerima IFS untuk diatur melalui lingkungan), karena ini:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
Singkatnya, sh membuat mengatur ulang IFS ke default cukup petualangan yang aneh.
Q4: Dalam kode aktual:
Akhirnya, bagaimana kode ini:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
berperilaku jika kita mengubah baris pertama ke
while read -r line # Use the default IFS value
atau untuk:
while IFS=' ' read -r line
Pertama: Saya tidak tahu apakah echo $line
(dengan var TIDAK dikutip) ada di porpouse, atau tidak. Ini memperkenalkan level kedua 'field splitting' yang tidak dimiliki read. Jadi saya akan menjawab keduanya. :)
Dengan kode ini (jadi Anda bisa mengonfirmasi). Anda membutuhkan xxd yang berguna :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Saya mendapat:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
Nilai pertama adalah nilai yang benar IFS=
'spacetabnewline'
Baris berikutnya adalah semua nilai hex yang dimiliki var $a
, dan baris baru '0a' di akhir karena akan diberikan untuk setiap perintah baca.
Baris berikutnya, yang IFS-nya nol, tidak melakukan 'pemisahan bidang', tetapi baris baru dihapus (seperti yang diharapkan).
Tiga baris berikutnya, karena IFS berisi spasi, hapus spasi awal dan atur garis var ke sisa saldo.
Empat baris terakhir menunjukkan apa yang akan dilakukan oleh variabel yang tidak dikutip. Nilai akan dibagi pada (beberapa) spasi dan akan dicetak sebagai:bar,baz,qux,
IFS
dan tidak disetelIFS
sangat berbeda. Jawaban untuk Q4 sebagian salah: pemisah bagian dalam tidak disentuh di sini, hanya yang mengarah dan yang tertinggal.