Beberapa utas berikut di situs ini dan StackOverflow sangat membantu untuk memahami cara IFS
kerjanya:
- Apa IFS dalam konteks untuk pengulangan?
- Cara mengulangi garis-garis file
- Bash, baca baris demi baris dari file, dengan IFS
Tetapi saya masih memiliki beberapa pertanyaan pendek. Saya memutuskan untuk bertanya kepada mereka di pos yang sama karena saya pikir itu dapat membantu pembaca masa depan yang lebih baik:
Q1. IFS
biasanya dibahas dalam konteks "field splitting". Apakah pemisahan bidang sama dengan pemisahan kata ?
Q2: Spesifikasi POSIX mengatakan :
Jika nilai IFS adalah nol, tidak ada pemisahan bidang yang harus dilakukan.
Apakah pengaturan IFS=
sama dengan pengaturan IFS
ke nol? Apakah ini yang dimaksud dengan mengaturnya empty string
juga?
Q3: Dalam spesifikasi POSIX, saya membaca yang berikut:
Jika IFS tidak disetel, shell akan berperilaku seolah-olah nilai IFS adalah
<space>, <tab> and <newline>
Katakanlah saya ingin mengembalikan nilai default IFS
. Bagaimana aku melakukan itu? (lebih khusus, bagaimana saya merujuk <tab>
dan <newline>
?)
T4: 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
IFS
dan tidak disetelIFS
sangat berbeda. Jawaban untuk Q4 sebagian salah: pemisah bagian dalam tidak disentuh di sini, hanya yang mengarah dan yang tertinggal.IFS
, semuanya berartiIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Er, apa? Harus ada beberapa pembatas ruang di sana, mesin SO terus melucuti mereka).read
; variabel terakhir mengambil semua yang tersisa kecuali untuk pemisah terakhir dan meninggalkan pemisah dalam.Q1: Ya. "Field splitting" dan "word splitting" adalah dua istilah untuk konsep yang sama.
T2: Ya. Jika
IFS
tidak disetel (yaitu setelahunset IFS
), itu samaIFS
dengan disetel ke$' \t\n'
(spasi, tab, dan baris baru). JikaIFS
diatur ke nilai kosong (itulah yang artinya "null" di sini) (yaitu setelahIFS=
atauIFS=''
atauIFS=""
), tidak ada pemisahan bidang yang dilakukan sama sekali (dan$*
, yang biasanya menggunakan karakter pertama$IFS
, menggunakan karakter spasi).T3: Jika Anda ingin memiliki
IFS
perilaku default , Anda dapat menggunakanunset IFS
. Jika Anda ingin menetapkanIFS
secara eksplisit ke nilai default ini, Anda dapat menempatkan spasi karakter spasi, tab, baris baru dalam tanda kutip tunggal. Di ksh93, bash atau zsh, Anda bisa menggunakanIFS=$' \t\n'
. Mudah-mudahan, jika Anda ingin menghindari memiliki karakter tab literal dalam file sumber Anda, Anda dapat menggunakannyaQ4: Dengan
IFS
set ke nilai kosong,read -r line
setelline
ke seluruh baris kecuali baris yang mengakhiri. DenganIFS=" "
, spasi di awal dan di ujung garis dipangkas. Dengan nilai defaultIFS
, tab dan spasi dipangkas.sumber
$@
, ada beberapa variasi antara shell dalam konteks non-daftar sepertiIFS=; var=$@
). Perlu dicatat bahwa ketika IFS kosong, tidak ada pemisahan kata dilakukan tetapi $ var masih memperluas tidak ada argumen, bukan argumen kosong ketika $ var kosong, dan globbing masih berlaku, jadi Anda masih perlu mengutip variabel (bahkan jika Anda disable globbing)Q1. Pemisahan bidang.
Ya, keduanya menunjuk ke ide yang sama.
T2: Kapan IFS nol ?
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.
Yang persis sama dengan:
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 varOldIFS
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.
Untuk zsh, ksh, dan bash (AFAIK), IFS dapat diatur ke nilai default sebagai:
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> :
3.- Sederhana? larutan:
Jika ada skrip anak yang memerlukan IFS diatur dengan benar, Anda selalu bisa menulis secara manual:
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
:Itu terjadi karena, di sebagian besar shell, semua baris baru
$(...)
atau`...`
pergantian perintah dihapus pada ekspansi.Kita perlu menggunakan trik untuk sh:
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:
Singkatnya, sh membuat mengatur ulang IFS ke default cukup petualangan yang aneh.
Q4: Dalam kode aktual:
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 :
Saya mendapat:
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,
sumber
unset IFS
tidak menghapus IFS, bahkan jika IFS setelah itu dianggap "\ t \ n":Diuji pada versi bash 4.2.45 dan 3.2.25 dengan perilaku yang sama.
sumber
unset
dariIFS
, seperti yang dijelaskan dalam komentar-komentar dari jawaban yang diterima di sini.