Ekspansi string yang dikutip vs yang tidak dikutip

11
  1. for i in $(xrandr); do echo "$i" ; done
  2. for i in "$(xrandr)"; do echo "$i"; done
  3. for i in "$(xrandr)"; do echo $i; done

Saya mengerti mengapa 1 berbeda dari 2. Tetapi mengapa 3 memberikan output yang berbeda dari 2? Tolong jelaskan juga hasilnya. Bagaimana cara kerja kutipan di baris baru?

ManuelSchneid3r
sumber
1
Terkait: Kapan perlu mengutip ganda?
Gilles 'SO- berhenti bersikap jahat'

Jawaban:

19

Variabel yang tidak dikutip (seperti dalam $var) atau substitusi perintah (seperti dalam $(cmd)atau `cmd`) adalah operator split + glob dalam shell yang mirip Bourne.

Artinya, konten mereka dibagi sesuai dengan nilai saat ini dari $IFSvariabel khusus (yang secara default berisi spasi, tab, dan karakter baris baru)

Dan kemudian setiap kata yang dihasilkan dari pemisahan itu tunduk pada generasi nama file (juga dikenal sebagai globbing atau ekspansi nama file ), yaitu, mereka dianggap sebagai pola dan diperluas ke daftar file yang cocok dengan pola itu.

Jadi for i in $(xrandr), dalam $(xrandr), karena tidak ada dalam tanda kutip, dibagi pada urutan spasi, tab, dan karakter baris baru. Dan setiap kata yang dihasilkan dari pemisahan tersebut diperiksa untuk mencocokkan nama file (atau dibiarkan seolah-olah tidak cocok dengan file apa pun), dan formengulanginya semua.

Dalam for i in "$(xrandr)", kami tidak menggunakan operator split + glob seperti yang dikutip oleh substitusi , jadi ada satu pass dalam loop pada satu nilai: output dari xrandr(tanpa karakter baris tambahan yang memerintahkan strip pengganti ).

Namun dalam echo $i, $itidak dikutip lagi, sehingga konten $isplit dan tunduk pada generasi nama file dan mereka dilewatkan sebagai argumen terpisah untuk echoperintah (dan echooutput argumennya dipisahkan oleh spasi).

Jadi pelajaran yang didapat:

  • jika Anda tidak ingin pemisahan kata atau pembuatan nama file , selalu kutip ekspansi variabel dan penggantian perintah
  • jika Anda memang ingin pemisahan kata atau pembuatan nama file , biarkan tidak dikutip tetapi tetapkan $IFSsesuai dan / atau aktifkan atau nonaktifkan pembuatan nama file jika diperlukan ( set -f, set +f).

Biasanya, dalam contoh Anda di atas, jika Anda ingin mengulang daftar kata-kata yang dipisahkan kosong di output dari xrandr, Anda harus:

  • biarkan $IFSpada nilai default-nya (atau tidak disetel) untuk dibagi menjadi kosong
  • Gunakan set -funtuk menonaktifkan generasi nama file kecuali Anda yakin bahwa xrandrtidak pernah output setiap *atau ?atau [karakter (yang wildcard digunakan dalam pola generasi nama file)

Dan kemudian hanya menggunakan operator split + glob (hanya biarkan substitusi perintah atau ekspansi variabel tidak dikutip) di inbagian forloop:

set -f; unset -v IFS
for i in $(xrandr); do whatever with "$i"; done

Jika Anda ingin loop atas (non-kosong) baris dari xrandroutput, Anda akan perlu untuk set $IFSuntuk karakter baris baru:

IFS='
'
Stéphane Chazelas
sumber
4

Baris baru yang dikutip adalah baris baru. Jadi echo "$1"memberikan argumen baris perintah tunggal untuk bergema, yang kemudian mencetak baris baru secara langsung.

Baris baru tanpa tanda kutip adalah spasi. Jadi echo $1memberikan banyak argumen baris perintah untuk echo, yang mencetaknya satu demi satu dipisahkan dengan spasi.

rici
sumber
Sebuah baris baru tanpa tanda kutip adalah spasi putih terlalu banyak jalan pintas untuk benar-benar berguna sebagai penjelasan tentang perilaku di sini IMO.
Stéphane Chazelas