Kapan saya bisa menggunakan IFS sementara untuk pemisahan bidang?


19

Dalam bash, katakan Anda miliki var=a.b.c., maka:

$ IFS=. printf "%s\n" $var
a.b.c

Namun, penggunaan seperti IFSitu berlaku saat membuat array:

$ IFS=. arr=($var)
$ printf "%s\n" "${arr[@]}"
a
b
c

Ini sangat nyaman, tentu saja, tetapi di mana ini didokumentasikan? Pembacaan cepat bagian pada Array atau Word Splitting di dokumentasi Bash tidak memberikan indikasi apa pun. Pencarian IFSmelalui dokumentasi satu halaman tidak memberikan petunjuk tentang efek ini.

Saya tidak yakin kapan saya bisa melakukan:

IFS=x do something

Dan berharap itu IFSakan mempengaruhi pemisahan bidang.

Jawaban:


28

Ide dasarnya adalah bahwa VAR=VALUE some-commandset VARuntuk VALUEuntuk pelaksanaan some-commandketika some-commandadalah perintah eksternal, dan tidak mendapatkan lebih mewah dari itu. Jika Anda menggabungkan intuisi ini dengan beberapa pengetahuan tentang cara kerja shell, Anda harus menemukan jawaban yang tepat dalam banyak kasus. Referensi POSIX adalah "Perintah Sederhana" di bab "Bahasa Perintah Shell" .

Jika some-commandmerupakan perintah eksternal , VAR=VALUE some-commandsama dengan env VAR=VALUE some-command. VARdiekspor di lingkungan some-command, dan nilainya (atau kurangnya nilai) di shell tidak berubah.

Jika some-commandadalah fungsi , maka VAR=VALUE some-commandsetara dengan VAR=VALUE; some-command, yaitu sisa-sisa tugas di tempat setelah fungsi telah kembali, dan variabel tersebut tidak diekspor ke lingkungan. Alasan untuk itu berkaitan dengan desain shell Bourne (dan kemudian dengan kompatibilitas ke belakang): ia tidak memiliki fasilitas untuk menyimpan dan mengembalikan nilai variabel di sekitar pelaksanaan fungsi. Tidak mengekspor variabel masuk akal karena fungsi dijalankan di shell itu sendiri. Namun, ksh (termasuk ATT ksh93 dan pdksh / mksh), bash dan zsh mengimplementasikan perilaku yang lebih bermanfaat di mana VARhanya diatur selama eksekusi fungsi (itu juga diekspor). Dalam ksh , ini dilakukan jika fungsi didefinisikan dengan sintaks kshfunction NAME …, tidak jika itu didefinisikan dengan sintaks standar NAME (). Dalam bash , ini dilakukan hanya dalam mode bash, bukan dalam mode POSIX (saat dijalankan dengan POSIXLY_CORRECT=1). Di zsh , ini dilakukan jika posix_builtinsopsi tidak disetel; opsi ini tidak diatur secara default tetapi dihidupkan oleh emulate shatau emulate ksh.

Jika some-commandbuiltin, perilaku tergantung pada tipe builtin. Builtin khusus berperilaku seperti fungsi. Built-in khusus adalah yang harus diimplementasikan di dalam shell karena mereka mempengaruhi shell negara (misalnya breakmemengaruhi aliran kontrol, cdmemengaruhi direktori saat ini, setmemengaruhi parameter dan opsi posisi ...). Builtin lainnya hanya built-in untuk kinerja dan kenyamanan (kebanyakan - misalnya fitur bash printf -vhanya dapat diimplementasikan oleh builtin), dan mereka berperilaku seperti perintah eksternal.

Penugasan berlangsung setelah ekspansi alias, jadi jika some-commandmerupakan alias , perluas terlebih dahulu untuk menemukan apa yang terjadi.

Perhatikan bahwa dalam semua kasus, penugasan dilakukan setelah baris perintah diuraikan, termasuk substitusi variabel apa pun pada baris perintah itu sendiri. Jadi var=a; var=b echo $varcetak a, karena $vardievaluasi sebelum penugasan berlangsung. Dan dengan demikian IFS=. printf "%s\n" $varmenggunakan nilai lama IFSuntuk membagi $var.

Saya sudah membahas semua jenis perintah, tetapi ada satu kasus lagi: ketika tidak ada perintah untuk dieksekusi , yaitu jika perintah hanya terdiri dari tugas (dan mungkin pengalihan). Dalam hal itu, penugasan tetap di tempatnya . VAR=VALUE OTHERVAR=OTHERVALUEsetara dengan VAR=VALUE; OTHERVAR=OTHERVALUE. Jadi setelah IFS=. arr=($var), IFStetap diatur ke .. Karena Anda dapat menggunakan $IFSdalam penugasan arrdengan harapan bahwa itu sudah memiliki nilai baru, masuk akal bahwa nilai baru IFSdigunakan untuk ekspansi $var.

Singkatnya, Anda dapat menggunakan hanya IFSuntuk pemisahan bidang sementara :

  • dengan memulai shell baru atau subkulit (mis. third=$(IFS=.; set -f; set -- $var; echo "$3")adalah cara yang rumit untuk dilakukan third=${var#*.*.}kecuali bahwa mereka berperilaku berbeda ketika nilai varmengandung kurang dari dua .karakter);
  • di ksh, dengan IFS=. some-functionmana some-functiondidefinisikan dengan sintaks ksh function some-function …;
  • dalam bash dan zsh, dengan IFS=. some-functionselama mereka beroperasi dalam mode asli yang bertentangan dengan mode kompatibilitas.

" IFStetap diatur ke ." Eek. Setelah membaca bagian pertama, itu masuk akal, tetapi sebelum saya memposting Q ini, saya tidak akan mengharapkan itu.
muru

1
Ini adalah jawaban untuk pertanyaan yang berbeda
schily

Beberapa penjelasan tambahan dalam jawaban ini dari beberapa tahun yang lalu .
Ti Strga

6

Jawaban @Gilles benar-benar hebat, ia menjelaskan (secara terperinci) masalah yang kompleks.

Namun, saya yakin itulah jawaban mengapa perintah ini:

$ IFS=. printf "%s\n" $var
a.b.c

berfungsi sebagaimana adanya adalah gagasan sederhana bahwa seluruh baris perintah diuraikan sebelum dieksekusi. Dan setiap "kata" diproses satu kali oleh shell.
The tugas, seperti IFS=., tertunda (langkah 4 adalah yang terakhir):

4.- Setiap tugas variabel harus diperluas ...

sampai sebelum perintah dieksekusi dan semua ekspansi dalam argumen diproses terlebih dahulu untuk membangun baris yang dapat dieksekusi ini:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

Nilai $vardiperluas dengan IFS "lama" a.b.csebelum perintah printfdiberikan argumen "%s\n"dan a.b.c.

Eval

Satu tingkat keterlambatan dapat diperkenalkan oleh eval:

$ IFS=. eval printf "'%s\n'" \$var
a
b
c

Baris diurai (pertama kali) dan 'IFS =.' diatur ke lingkungan karena ini:

$ printf '%s\n' $var

Kemudian diuraikan lagi untuk ini:

$ printf '%s\n' a b c

Dan dieksekusi untuk ini:

a
b
c

Nilai $var(abc) dibagi dengan nilai IFS digunakan: ..

Lingkungan Hidup

Bagian yang rumit dan rumit adalah apa yang valid di lingkungan kapan !!!

Itu dijelaskan dengan sangat baik di bagian pertama jawaban Gilles.

Dengan detail tambahan.

Ketika perintah ini dijalankan:

$ IFS=. arr=($var)

Nilai IFS dipertahankan di lingkungan saat ini, ya:

$ printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  <.> 

IFS untuk satu pernyataan.

Tapi itu bisa dihindari: Mengatur IFS untuk satu pernyataan

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 

2

Pertanyaan Anda tentang

var=a.b.c
IFS=. printf "%s\n" $var

adalah kasus sudut.

Ini karena macro expansiondalam perintah terjadi sebelum variabel shell IFS=.diatur.

Dengan kata lain: ketika $vardiperluas, nilai sebelumnya IFSaktif, kemudian IFSdiatur ke '.'.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.