GNU/Linux >> Belajar Linux >  >> Linux

Memahami "jika =Baca -r Baris"?

Saya jelas mengerti bahwa seseorang dapat menambahkan nilai ke variabel pemisah bidang internal. Misalnya:

$ IFS=blah
$ echo "$IFS"
blah
$ 

Saya juga mengerti bahwa read -r line akan menyimpan data dari stdin ke variabel bernama line :

$ read -r line <<< blah
$ echo "$line"
blah
$ 

Namun, bagaimana sebuah perintah dapat menetapkan nilai variabel? Dan apakah itu pertama kali menyimpan data dari stdin ke variabel line lalu beri nilai line ke IFS ?

Jawaban yang Diterima:

Dalam shell POSIX, read , tanpa opsi apa pun tidak membaca baris , itu membaca kata dari baris (mungkin backslash-continued), dengan kata-kata $IFS delimited dan backslash dapat digunakan untuk menghindari pembatas (atau melanjutkan baris).

Sintaks umum adalah:

read word1 word2... remaining_words

read membaca stdin satu byte pada satu waktu¹ sampai menemukan karakter baris baru yang tidak terhapus (atau akhir input), membaginya menurut aturan yang kompleks dan menyimpan hasil pemisahan itu menjadi $word1 , $word2$remaining_words .

Misalnya pada input seperti:

  <tab> foo bar baz   blah   blah
whatever whatever

dan dengan nilai default $IFS , read a b c akan menetapkan:

  • $a foo
  • $b bar baz
  • $c blah blahwhatever whatever

Sekarang jika hanya melewati satu argumen, itu tidak menjadi read line . Masih read remaining_words . Pemrosesan garis miring terbalik masih dilakukan, karakter spasi putih IFS² masih dihapus dari awal dan akhir.

-r opsi menghapus pemrosesan garis miring terbalik. Jadi perintah yang sama di atas dengan -r sebagai gantinya akan menetapkan

  • $a foo
  • $b bar
  • $c baz blah blah

Sekarang, untuk bagian pemisahan, penting untuk disadari bahwa ada dua kelas karakter untuk $IFS :karakter spasi putih IFS² (termasuk spasi dan tab (dan baris baru, meskipun di sini tidak masalah kecuali Anda menggunakan -d), yang juga merupakan nilai default $IFS ) dan yang lainnya. Perlakuan untuk kedua kelas karakter tersebut berbeda.

Dengan IFS=: (: bukan karakter spasi putih IFS), masukan seperti :foo::bar:: akan dipecah menjadi "" , "foo" , "" , bar dan "" (dan tambahan "" dengan beberapa implementasi meskipun itu tidak masalah kecuali untuk read -a ). Sedangkan jika kita mengganti : dengan spasi, pemisahan dilakukan menjadi hanya foo dan bar . Yang memimpin dan yang tertinggal diabaikan, dan urutannya diperlakukan seperti satu. Ada aturan tambahan ketika karakter spasi dan non-spasi digabungkan dalam $IFS . Beberapa implementasi dapat menambah/menghapus perlakuan khusus dengan menggandakan karakter di IFS (IFS=:: atau IFS=' ' ).

Jadi di sini, jika kita tidak ingin karakter spasi putih awal dan akhir yang tidak lolos dihilangkan, kita perlu menghapus karakter spasi putih IFS tersebut dari IFS.

Bahkan dengan karakter IFS-non-spasi, jika baris input berisi satu (dan hanya satu) karakter tersebut dan itu adalah karakter terakhir dalam baris (seperti IFS=: read -r word pada input seperti foo: ) dengan shell POSIX (bukan zsh atau beberapa pdksh versi), masukan itu dianggap sebagai satu foo kata karena di shell itu, karakter $IFS dianggap sebagai terminator , jadi word akan berisi foo , bukan foo: .

Jadi, cara kanonik untuk membaca satu baris input dengan read bawaannya adalah:

IFS= read -r line

(perhatikan bahwa untuk sebagian besar read implementasi, yang hanya berfungsi untuk baris teks karena karakter NUL tidak didukung kecuali di zsh ).

Terkait:Linux – Bagikan file antara host Linux dan tamu Windows?

Menggunakan var=value cmd sintaks memastikan IFS hanya disetel secara berbeda selama cmd itu perintah.

Catatan sejarah

read builtin diperkenalkan oleh shell Bourne dan sudah membaca kata , bukan garis. Ada beberapa perbedaan penting dengan shell POSIX modern.

read shell Bourne tidak mendukung -r opsi (yang diperkenalkan oleh shell Korn), jadi tidak ada cara untuk menonaktifkan pemrosesan garis miring terbalik selain memproses input sebelumnya dengan sesuatu seperti sed 's/\/&&/g' di sana.

Shell Bourne tidak memiliki gagasan tentang dua kelas karakter (yang sekali lagi diperkenalkan oleh ksh). Di Bourne Shell semua karakter menjalani perlakuan yang sama seperti karakter spasi putih IFS di ksh, yaitu IFS=: read a b c pada input seperti foo::bar akan menetapkan bar ke $b , bukan string kosong.

Di shell Bourne, dengan:

var=value cmd

Jika cmd adalah built-in (seperti read adalah), var tetap disetel ke value setelah cmd telah selesai. Itu sangat penting dengan $IFS karena di shell Bourne, $IFS digunakan untuk membagi segalanya, tidak hanya ekspansi. Juga, jika Anda menghapus karakter spasi dari $IFS di shell Bourne, "[email protected]" tidak lagi berfungsi.

Di shell Bourne, mengarahkan perintah gabungan menyebabkannya berjalan dalam subkulit (dalam versi paling awal, bahkan hal-hal seperti read var < file atau exec 3< file; read var <&3 tidak bekerja), jadi di shell Bourne jarang menggunakan read untuk apa pun kecuali input pengguna di terminal (di mana penanganan kelanjutan baris itu masuk akal)

Beberapa Unice (seperti HP/UX, ada juga di util-linux ) masih memiliki line perintah untuk membaca satu baris input (yang dulunya merupakan perintah UNIX standar hingga Spesifikasi UNIX Tunggal versi 2).

Itu pada dasarnya sama dengan head -n 1 kecuali ia membaca satu byte pada satu waktu untuk memastikan tidak membaca lebih dari satu baris. Pada sistem tersebut, Anda dapat melakukan:

line=`line`

Tentu saja, itu berarti memunculkan proses baru, menjalankan perintah, dan membaca outputnya melalui pipa, jadi jauh lebih tidak efisien daripada IFS= read -r line ksh , tetapi masih jauh lebih intuitif.


Linux
  1. Utilitas Baris Perintah Untuk Mengambil Kata Sandi, Yang Tidak Memiliki Gema Kembali?

  2. Memahami Desktop Linux?

  3. Bagaimana cara membaca baris kedua hingga terakhir dalam file menggunakan Bash?

  1. Memahami Jika?

  2. Kotak Sibuk Membaca File Baris demi Baris?

  3. Shell Script, baca di baris yang sama setelah mengulang pesan

  1. Memahami YAML untuk Ansible

  2. Baca File Berorientasi Garis Yang Mungkin Tidak Berakhir Dengan Baris Baru?

  3. Baca baris demi baris dalam skrip bash