Apa arti sebenarnya dari IFS = $ '\ n'?

125

Jika contoh berikut, yang menetapkan IFSvariabel lingkungan ke karakter umpan baris ...

IFS=$'\n'
  • Apa tanda dolar berarti persis ?
  • Apa fungsinya dalam kasus khusus ini?
  • Di mana saya dapat membaca lebih lanjut tentang penggunaan khusus ini (Google tidak mengizinkan karakter khusus dalam pencarian dan saya tidak tahu harus mencari apa)?

Saya tahu apa itu IFSvariabel lingkungan, dan apa \nkarakternya (umpan baris), tetapi mengapa tidak menggunakan formulir berikut: IFS="\n"(yang tidak berfungsi)?

Misalnya, jika saya ingin mengulang setiap baris file dan ingin menggunakan for loop, saya bisa melakukan ini:

for line in (< /path/to/file); do
    echo "Line: $line"
done

Namun, ini tidak akan berfungsi dengan benar kecuali IFSdisetel ke karakter umpan baris. Agar berhasil, saya harus melakukan ini:

OLDIFS=$IFS
IFS=$'\n'
for line in (< /path/to/file); do
    echo "Line: $line"
done
IFS=$OLDIFS

Catatan: Saya tidak memerlukan cara lain untuk melakukan hal yang sama, saya sudah tahu banyak cara lain ... Saya hanya ingin tahu tentang itu $'\n'dan bertanya-tanya apakah ada yang bisa memberi saya penjelasan tentang itu.

Yanick Girouard
sumber

Jawaban:

161

Biasanya bashtidak menafsirkan urutan escape dalam string literal. Jadi jika Anda menulis \natau "\n"atau '\n', itu bukan penggalan baris - itu adalah huruf n(dalam kasus pertama) atau garis miring terbalik diikuti oleh huruf n(dalam dua kasus lainnya).

$'somestring'adalah sintaks untuk string literal dengan escape sequence . Jadi tidak seperti '\n', $'\n'sebenarnya adalah linebreak.

sepp2k.dll
sumber
2
Tidak persis begitu - \nhanya huruf (lolos) n. Anda benar '\n'dan "\n"reaksi diikuti oleh n.
Roman Cheplyaka
15
Perhatikan bahwa $'\n'bash spesifik - ini tidak akan berfungsi di shell POSIX ( /bin/sh). Untuk mendapatkan efek yang sama dengan cara yang sesuai dengan POSIX, Anda dapat mengetik IFS=', lalu tekan kembali untuk mengetik karakter baris baru yang sebenarnya, lalu ketik penutup'
Richard Hansen
23
IFS=$(echo -e '\n')juga harus melakukannya dengan cara yang kompatibel dengan POSIX.
Vineet
12
@Vineet - ini memberi saya jeda untuk menyengketakan komentar yang diberi suara positif. Meskipun ini benar Posix, ini tidak berfungsi - Operator substitusi perintah di bash menghapus semua karakter baris baru yang tertinggal. Lihat ini untuk detail lebih lanjut .
Digital Trauma
9
@DigitalTrauma Saya pikir itu bahkan bukan POSIX: -etidak didefinisikan, dan \ntanpa -ebekerja sebagai ekstensi XSI: pubs.opengroup.org/onlinepubs/9699919799/utilities/… . printf '\n'batu;)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
21

Hanya untuk memberi nama resminya pada konstruksi : string dari formulir $'...'disebut string yang dikutip C ANSI .

Yaitu, seperti dalam string [ANSI] C, urutan escape backlash dikenali dan diperluas ke padanan literalnya (lihat di bawah untuk daftar lengkap dari urutan escape yang didukung).

Setelah perluasan ini, $'...'string berperilaku sama seperti '...'string - yaitu, string diperlakukan sebagai literal TIDAK tunduk pada ekspansi shell [lebih lanjut] .

Misalnya, $'\n'memperluas ke karakter baris baru literal - yang tidak dapat dilakukan oleh literal string bash biasa (baik '...'atau "..."). [1]

Fitur menarik lainnya adalah string yang dikutip C ANSI dapat keluar '(tanda kutip tunggal) karena\' , '...'(string yang dikutip tunggal reguler) tidak dapat:

echo $'Honey, I\'m home' # OK; this cannot be done with '...'

Daftar urutan pelolosan yang didukung :

Urutan escape dengan garis miring terbalik, jika ada, didekodekan sebagai berikut:

\ peringatan (bel)

\ b spasi mundur

\ e \ E karakter escape (bukan ANSI C)

\ f umpan formulir

\ n baris baru

kereta kembali

\ t tab horizontal

\ v tab vertikal

\ garis miring terbalik

\ 'kutipan tunggal

\ "petik ganda

\ nnn karakter delapan-bit yang nilainya adalah nilai oktal nnn (satu sampai tiga digit)

\ xHH karakter delapan-bit yang nilainya adalah nilai heksadesimal HH (satu atau dua digit hex)

\ uHHHH karakter Unicode (ISO / IEC 10646) yang nilainya adalah nilai heksadesimal HHHH (satu hingga empat digit hex)

\ UHHHHHHHH karakter Unicode (ISO / IEC 10646) yang nilainya adalah nilai heksadesimal HHHHHHHH (satu hingga delapan digit hex)

\ cx karakter kontrol-x

Hasil yang diperluas adalah kutipan tunggal, seolah-olah tanda dolar tidak ada.


[1] Namun, Anda dapat menyematkan baris baru yang sebenarnya dalam string '...' dan "..."; yaitu, Anda dapat menentukan string yang menjangkau beberapa baris.

mklement0
sumber
16

Dari http://www.linuxtopia.org/online_books/bash_guide_for_beginners/sect_03_03.html :

Kata-kata dalam bentuk "$ 'STRING'" diperlakukan dengan cara khusus. Kata tersebut diperluas menjadi string, dengan karakter pelolosan garis miring terbalik diganti sebagaimana ditentukan oleh standar ANSI-C. Urutan escape dengan garis miring terbalik dapat ditemukan di dokumentasi Bash

Saya kira itu memaksa skrip untuk keluar dari umpan baris ke standar ANSI-C yang tepat.

Brad Swerdfeger
sumber
8

Memulihkan kembali IFS default- ini OLDIFS=$IFStidak perlu. Jalankan IFS baru di subkulit untuk menghindari menimpa IFS default:

ar=(123 321); ( IFS=$'\n'; echo ${ar[*]} )

Selain itu, saya tidak yakin Anda memulihkan IFS lama sepenuhnya. Anda harus mengutipnya dua kali untuk menghindari pemutusan baris seperti OLDIFS="$IFS".

Marek
sumber
2
ini adalah teknik yang sangat berguna. saya hanya menggunakannya untuk shell cleaner bergabung op: args=$(IFS='&'; echo "$*"). memulihkan IFSdengan $' \t\n'cara yang ramah Bourne shell bukanlah prestasi yang berarti.
jeberle
Re Besides I don't really believe you recover the old IFS fully: pemisahan kata tidak dilakukan di kanan variabel tugas (tapi penghapusan kutipan), jadi OLDIFS=$IFSdan OLDIFS="$IFS"berperilaku dengan cara yang sama.
mklement0
3

String yang dikutip ANSI C adalah poin kunci. Terima kasih kepada @ mklement0.

Anda dapat menguji string yang dikutip C ANSI dengan perintah od.

echo -n $'\n' | od -c
echo -n '\n' | od -c
echo -n $"\n" | od -c
echo -n "\n" | od -c

Keluaran:

0000000  \n  
0000001

0000000   \   n   
0000002

0000000   \   n   
0000002

0000000   \   n   
0000002

Anda dapat mengetahui artinya dengan jelas dari keluarannya.

Perisai Besar
sumber
-7

Ini seperti mengambil nilai dari variabel:

VAR='test'
echo VAR
echo $VAR

berbeda, jadi tanda dolar pada dasarnya mengevaluasi konten.

Pieter
sumber
6
Ini tidak ada hubungannya dengan variabel. $'FOO'(tidak seperti $FOOpertanyaan ini yang bukan tentang) adalah string literal. Jika Anda mengeksekusi echo $'VAR', Anda akan melihat bahwa itu mencetak string VAR, bukan test.
sepp2k