Apa arti dari IFS = $ '\ n' dalam skrip bash?

163

Di awal skrip bash shell adalah baris berikut:

IFS=$'\n'

Apa arti di balik koleksi simbol ini?

Abdul Al Hazred
sumber
3
Lihat juga unix.stackexchange.com/questions/26784/understanding-ifs dan pertanyaan yang dikutipnya.
Gilles
sintaks vim menyoroti ini sebagai kesalahan untuk saya; memperbaiki?
theonlygusti
IFS=$'\n'adalah bashism (+ cangkang lainnya, gunakan ANSI-C Quoting , untuk penyelesaian masalah, lihat stackoverflow.com/questions/10748703/…
pevik

Jawaban:

199

IFSsingkatan dari "pemisah bidang internal". Ini digunakan oleh shell untuk menentukan bagaimana melakukan pemisahan kata, yaitu bagaimana mengenali batasan kata.

Coba ini di shell seperti bash (shell lain mungkin menangani ini secara berbeda, misalnya zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

Nilai default untuk IFSterdiri dari karakter spasi putih (tepatnya: spasi, tab, dan baris baru). Setiap karakter dapat menjadi batas kata. Jadi, dengan nilai default IFS, loop di atas akan mencetak:

Word: foo:bar
Word: baz
Word: rab

Dengan kata lain, shell berpikir bahwa spasi putih adalah batas kata.

Sekarang, coba pengaturan IFS=:sebelum menjalankan loop. Kali ini, hasilnya adalah:

Word: foo
Word: bar baz rab

Sekarang, shell terbagi mystringmenjadi kata-kata juga - tetapi sekarang, itu hanya memperlakukan titik dua sebagai batas kata.

Karakter pertama IFSadalah spesial: Digunakan untuk membatasi kata-kata dalam output saat menggunakan $*variabel khusus (contoh diambil dari Advanced Bash Scripting Guide , di mana Anda juga dapat menemukan informasi lebih lanjut tentang variabel-variabel khusus seperti itu):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

Dibandingkan dengan:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Perhatikan bahwa dalam kedua contoh, shell masih akan memperlakukan semua karakter :, -dan ;sebagai batas kata. Satu-satunya hal yang berubah adalah perilaku $*.

Hal penting lain yang perlu diketahui adalah bagaimana apa yang disebut "ruang putih IFS" diperlakukan . Pada dasarnya, segera setelah IFSmemasukkan karakter spasi putih, spasi spasi awal dan jejak dilucuti dari string untuk dipisah sebelum memprosesnya dan urutan karakter spasi putih berturut-turut juga membatasi bidang. Namun, ini hanya berlaku untuk karakter spasi putih yang sebenarnya ada di IFS.

Sebagai contoh, mari kita lihat string "a:b:: c d "(spasi spasi dan dua karakter spasi antara cdan d).

  1. Dengan IFS=:itu akan dibagi menjadi empat bidang: "a", "b", ""(string kosong) dan " c d "(sekali lagi, dua ruang antara cdan d). Perhatikan spasi putih terkemuka dan tertinggal di bidang terakhir.
  2. Dengan IFS=' :', itu akan dibagi menjadi lima bidang: "a", "b", ""(string kosong), "c"dan "d". Tidak ada spasi putih terkemuka dan tertinggal di mana pun.

Perhatikan bagaimana beberapa, karakter spasi putih berturut-turut membatasi dua bidang pada contoh kedua, sedangkan banyak, karakter titik dua berturut-turut tidak (karena mereka bukan karakter spasi putih).

Adapun IFS=$'\n', yang merupakan ksh93sintaks juga didukung oleh bash, zsh, mkshdan FreeBSD sh(dengan variasi antara semua kerang). Mengutip manual bash:

Kata-kata dalam bentuk $ 'string' diperlakukan secara khusus. Kata diperluas ke "string", dengan karakter backslash-escape diganti seperti yang ditentukan oleh standar ANSI C.

\nadalah urutan pelarian untuk baris baru, sehingga IFSakhirnya ditetapkan ke karakter baris baru.

Tblue
sumber
3
Ini bagus, tetapi, menurut saya, Anda akan jauh lebih baik membaca dan memahami spesifikasi POSIX daripada bashpanduan penulisan skrip, atau apa pun. Pada intinya, informasi yang tersedia di tautan seperti itu kurang penting. Lagi pula, dengan demikian melewatkan dua poin penting tentang shell splitting - globbing dan IFS whitespace.
mikeserv
@ mikeserv Terima kasih, saya menambahkan beberapa informasi tentang ruang kosong IFS. Tidak tahu tentang itu. :)
Tblue
4
Bukan sebagai relevan, tetapi jika Anda penasaran, Anda mungkin ingin melihat bagaimana unset IFSmembuat shell berperilaku sangat berbeda IFS=. Juga byte pertama di IFS juga istimewa "${named_array[*]}"- tetapi tidak masalah ketika ekspansi tidak
dikutip
Beberapa poin lagi: pemisahan 1 kata, diatur oleh $IFSadalah salah satu dari dua hal utama yang dilakukan saat memperluas variabel yang tidak dikutip dalam konteks daftar (ini adalah splitbagian dari split+globoperator). Yang lainnya adalah globbing. Saat menggunakan pemisahan pekerjaan, Anda biasanya perlu mengeluarkan set -funtuk menonaktifkan globbagian.
Stéphane Chazelas
3
3- $IFSjuga digunakan oleh readperintah builtin
Stéphane Chazelas
22

Di dalam kutipan tunggal boneka, beberapa karakter dievaluasi secara khusus. Misalnya, \nditerjemahkan ke baris baru.

Jadi, baris khusus ini memberikan baris baru ke variabel IFS. IFS, pada gilirannya, adalah variabel khusus di bash: Pemisah Bidang Internal. Seperti yang man bashdikatakan, itu

digunakan untuk pemisahan kata setelah ekspansi dan untuk memecah baris menjadi kata-kata dengan readperintah builtin. Nilai standarnya adalah <space><tab><newline>.

choroba
sumber
5
+1 untuk menyebutkan dollared single quotesyang berbeda dari kutipan tunggal sederhana.
Snowcrash
2
@Snowcrash +1 untuk mengatakan +1 karena menyebutkan tanda kutip tunggal yang dibuat boneka yang berbeda dari tanda kutip tunggal sederhana . Maaf, tidak bisa menahannya :) Tetapi sebenarnya ini adalah hal yang sangat baik untuk ditunjukkan karena ini penting!
Pryftan
1
@Pryftan +1 untuk +1 untuk +1 ... Anda tahu ... ini sangat penting.
0xc0de
@ 0xc0de Setuju! Terima kasih untuk itu! :)
Pryftan
15

Singkatnya, IFS=$'\n'tetapkan baris baru \nke variabel IFS.

$'string'construct adalah mekanisme penawaran yang digunakan untuk mendekode ANSI C seperti escape sequence. Sintaks ini berasal dari ksh93, dan portabel untuk shell modern seperti bash, zsh, pdksh, busybox sh.

Sintaks ini tidak didefinisikan oleh POSIX, tetapi diterima untuk SUS edisi 7 .

cuonglm
sumber
-1

Saya lebih suka menjelaskan $IFSmelalui contoh:
Jika Anda ingin cp atau mv atau pemrosesan file lain, IFS kosong oleh defualt, Ketika file Anda memiliki meta char atau ruang seperti:
Linux Administration.pdfatau Free Software Fundation.ogg, Tentu Anda akan memiliki masalah, Karena: Linux mempertimbangkan terpisah param dan Administartion mempertimbangkan param terpisah. Jadi bash memiliki built-in variable, Kemudian Anda dapat menginisialisasi IFS==$(echo -en "\n\b"), Kemudian bash membuang meta char dan spasi di antara nama file, Misalnya:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
Teluk Persia
sumber