Dalam beberapa shell (termasuk bash
):
IFS=: command eval 'p=($PATH)'
(dengan bash
, Anda dapat menghilangkan command
emulasi jika tidak dalam sh / POSIX). Namun berhati-hatilah bahwa ketika menggunakan variabel yang tidak dikutip, Anda juga umumnya perlu set -f
, dan tidak ada cakupan lokal untuk itu di sebagian besar shell.
Dengan zsh, Anda dapat melakukan:
(){ local IFS=:; p=($=PATH); }
$=PATH
adalah untuk memaksa pemisahan kata yang tidak dilakukan secara default di zsh
(globbing pada ekspansi variabel tidak dilakukan sehingga Anda tidak perlu set -f
kecuali dalam emulasi sh).
(){...}
(atau function {...}
) disebut fungsi anonim dan biasanya digunakan untuk mengatur cakupan lokal. dengan shell lain yang mendukung lingkup fungsi lokal, Anda bisa melakukan sesuatu yang mirip dengan:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Untuk menerapkan lingkup lokal untuk variabel dan opsi dalam cangkang POSIX, Anda juga dapat menggunakan fungsi yang disediakan di https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh . Maka Anda dapat menggunakannya sebagai:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(Ngomong-ngomong, tidak sah untuk memecah $PATH
seperti itu di atas kecuali dalam zsh
seperti pada shell lainnya, IFS adalah pembatas lapangan, bukan pemisah bidang).
IFS=$'\n' a=($str)
Apakah hanya dua tugas, satu demi satu sama suka a=1 b=2
.
Catatan penjelasan tentang var=value cmd
:
Di:
var=value cmd arg
Shell dieksekusi /path/to/cmd
dalam proses baru dan melewati cmd
dan arg
masuk argv[]
dan var=value
masuk envp[]
. Itu bukan benar-benar tugas variabel, tetapi lebih banyak mengirimkan variabel lingkungan ke perintah yang dieksekusi . Dalam shell Bourne atau Korn, dengan set -k
, Anda bahkan dapat menulisnya cmd var=value arg
.
Sekarang, itu tidak berlaku untuk builtin atau fungsi yang tidak dijalankan . Dalam cangkang Bourne, dalam var=value some-builtin
, var
akhirnya ditetapkan setelahnya, sama seperti dengan var=value
sendirian. Itu berarti misalnya bahwa perilaku var=value echo foo
(yang tidak berguna) bervariasi tergantung pada apakah echo
builtin atau tidak.
POSIX dan / atau ksh
mengubahnya dalam perilaku Bourne yang hanya terjadi untuk kategori builtin yang disebut builtin khusus . eval
adalah builtin khusus, read
bukan. Untuk builtin non khusus, var=value builtin
set var
hanya untuk eksekusi builtin yang membuatnya berperilaku mirip dengan ketika perintah eksternal dijalankan.
The command
perintah dapat digunakan untuk menghapus khusus atribut yang builtin khusus . Apa POSIX diabaikan adalah bahwa untuk eval
dan .
builtin, itu berarti bahwa shell harus mengimplementasikan tumpukan variabel (meskipun itu tidak menentukan perintah local
atau typeset
ruang lingkup membatasi), karena Anda bisa melakukan:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Atau bahkan:
a=1 command eval myfunction
dengan myfunction
menjadi fungsi menggunakan atau pengaturan $a
dan berpotensi memanggil command eval
.
Itu benar-benar diabaikan karena ksh
(yang sebagian besar didasarkan pada spesifikasi) tidak mengimplementasikannya (dan AT&T ksh
dan zsh
masih tidak), tetapi saat ini, kecuali kedua, sebagian besar shell mengimplementasikannya. Perilaku bervariasi di antara cangkang meskipun dalam hal-hal seperti:
a=0; a=1 command eval a=2; echo "$a"
meskipun. Menggunakan local
shell yang mendukungnya adalah cara yang lebih dapat diandalkan untuk mengimplementasikan cakupan lokal.
IFS=: command eval …
menetapkanIFS
untuk durasieval
, seperti yang diamanatkan oleh POSIX, di dash, pdksh dan bash, tetapi tidak di ksh 93u. Tidak biasa melihat ksh menjadi orang aneh-tidak-patuh.Hemat-dan-pengembalian standar diambil dari "Lingkungan Pemrograman Unix" oleh Kernighan dan Pike:
sumber
$IFS
dengan benar jika sebelumnya tidak disetel.$'\t\n'' '
, sebagaimana dijelaskan di sini: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. ruang harus menjadi yang pertama seperti yang digunakan untuk"$*"
. Perhatikan bahwa itu sama di semua kerang mirip Bourne.Masukkan skrip Anda ke dalam suatu fungsi dan aktifkan fungsi yang meneruskan argumen commandline ke dalamnya. Karena IFS didefinisikan lokal, perubahan itu tidak mempengaruhi IFS global.
sumber
Untuk perintah ini:
Ada solusi alternatif: untuk memberikan tugas pertama (
IFS=$'\n'
) perintah untuk dieksekusi (fungsi):Itu akan menempatkan IFS di lingkungan untuk memanggil split, tetapi tidak akan dipertahankan di lingkungan saat ini.
Ini juga menghindari penggunaan eval yang selalu berisiko.
sumber
$IFS
diatur$'\n'
setelah itu seperti yang disyaratkan oleh POSIX.Jawaban yang diajukan dari @helpermethod tentu saja merupakan pendekatan yang menarik. Tapi itu juga sedikit jebakan karena dalam BASH lingkup variabel lokal meluas dari pemanggil ke fungsi yang dipanggil. Oleh karena itu, pengaturan IFS di main (), akan menghasilkan nilai yang bertahan untuk fungsi yang dipanggil dari main (). Ini sebuah contoh:
Dan hasilnya ...
Jika IFS dideklarasikan di main () tidak masih dalam lingkup di func (), maka array tidak akan diuraikan dengan benar di func () B. Batalkan komentar pada baris pertama di func () dan Anda mendapatkan output ini:
Yang harus Anda dapatkan jika IFS keluar dari ruang lingkup.
Solusi IMHO yang jauh lebih baik, adalah melepaskan perubahan atau mengandalkan IFS di tingkat global / lokal. Alih-alih, buat shell baru dan bermain-main dengan IFS di sana. Misalnya, jika Anda memanggil func () di main () sebagai berikut, meneruskan array sebagai string dengan pemisah bidang garis miring:
... bahwa perubahan ke IFS tidak akan tercermin dalam func (). Array akan diteruskan sebagai string:
... tetapi di dalam func () IFS masih akan menjadi "/" (sebagaimana diatur di main ()) kecuali diubah secara lokal di func ().
Informasi lebih lanjut tentang mengisolasi perubahan pada IFS dapat dilihat di tautan berikut:
Bagaimana cara mengonversi variabel array bash ke string yang dibatasi dengan baris baru?
Bash string to array dengan IFS
Petunjuk dan Tip untuk pemrograman skrip shell umum - Lihat "Perhatikan penggunaan sub-shell ..."
sumber
IFS=$'\n' declare -a astr=(...)
terima kasih sempurna!Cuplikan ini dari pertanyaan:
diinterpretasikan sebagai dua penugasan variabel global terpisah yang dievaluasi dari kiri ke kanan, dan setara dengan:
atau
Ini menjelaskan mengapa global
IFS
diubah, dan mengapa pemisahan kata$str
menjadi elemen array dilakukan menggunakan nilai baruIFS
.Anda mungkin tergoda untuk menggunakan subkulit untuk membatasi efek
IFS
modifikasi seperti ini:tetapi Anda akan segera melihat bahwa modifikasi
a
juga terbatas pada subkulit:Selanjutnya, Anda akan tergoda untuk menyimpan / mengembalikan IFS menggunakan solusi dari jawaban sebelumnya oleh @msw atau mencoba dan menggunakan
local IFS
fungsi di dalam seperti yang disarankan oleh @helpermethod. Tapi segera, Anda melihat Anda berada dalam segala macam masalah, terutama jika Anda adalah seorang penulis perpustakaan yang harus kuat terhadap perilaku yang salah dalam menjalankan skrip:IFS
awalnya tidak disetel?set -u
(aliasset -o nounset
)?IFS
dibuat hanya-baca melaluideclare -r IFS
?trap
handler`)?Tolong jangan simpan / pulihkan IFS. Sebaliknya, tetap gunakan modifikasi sementara:
Untuk membatasi modifikasi variabel menjadi satu perintah, pemanggilan fungsi atau built-in, gunakan
IFS="value" command
.Untuk membaca ke beberapa variabel dengan memisahkan karakter tertentu (
:
digunakan sebagai contoh di bawah), gunakan:Untuk membaca penggunaan array (lakukan ini alih-alih
array_var=( $str )
):Batasi efek dari memodifikasi variabel ke subkulit.
Untuk menampilkan elemen array yang dipisahkan oleh koma:
Untuk menangkapnya menjadi string:
sumber
Solusi yang paling lurus ke depan adalah mengambil salinan aslinya
$IFS
, seperti pada misalnya jawaban msw. Namun, solusi ini tidak membedakan antara setIFS
danIFS
set yang tidak sama dengan string kosong, yang penting untuk banyak aplikasi. Berikut adalah solusi yang lebih umum yang menangkap perbedaan ini:sumber