Berikut adalah rangkaian kasus yang echo $var
dapat menunjukkan nilai yang berbeda dari yang baru saja ditetapkan. Ini terjadi terlepas dari apakah nilai yang ditetapkan adalah "dikutip ganda", 'dikutip tunggal', atau tidak dikutip.
Bagaimana cara mendapatkan shell untuk mengatur variabel saya dengan benar?
Tanda bintang
Output yang diharapkan adalah /* Foobar is free software */
, tetapi saya mendapatkan daftar nama file:
$ var="/* Foobar is free software */"
$ echo $var
/bin /boot /dev /etc /home /initrd.img /lib /lib64 /media /mnt /opt /proc ...
Tanda kurung siku
Nilai yang diharapkan adalah [a-z]
, tetapi terkadang saya mendapatkan satu huruf saja!
$ var=[a-z]
$ echo $var
c
Feed baris (baris baru)
Nilai yang diharapkan adalah daftar baris terpisah, tetapi sebaliknya semua nilai berada dalam satu baris!
$ cat file
foo
bar
baz
$ var=$(cat file)
$ echo $var
foo bar baz
Banyak spasi
Saya mengharapkan tajuk tabel yang disejajarkan dengan hati-hati, tetapi sebaliknya beberapa spasi hilang atau diciutkan menjadi satu!
$ var=" title | count"
$ echo $var
title | count
Tab
Saya mengharapkan dua nilai yang dipisahkan tab, tetapi saya mendapatkan dua nilai yang dipisahkan spasi!
$ var=$'key\tvalue'
$ echo $var
key value
var=$(cat file)
baik-baik saja, tetapiecho "$var"
dibutuhkan.Jawaban:
Dalam semua kasus di atas, variabel disetel dengan benar, tetapi tidak terbaca dengan benar! Cara yang benar adalah dengan menggunakan tanda kutip ganda saat merujuk :
Ini memberikan nilai yang diharapkan dalam semua contoh yang diberikan. Selalu mengutip referensi variabel!
Mengapa?
Ketika sebuah variabel tidak dikutip , itu akan:
Lakukan pemisahan bidang di mana nilainya dibagi menjadi beberapa kata di spasi (secara default):
Sebelum:
/* Foobar is free software */
Setelah:
/*
,Foobar
,is
,free
,software
,*/
Masing-masing kata ini akan mengalami perluasan nama jalur , di mana polanya diperluas menjadi file yang cocok:
Sebelum:
/*
Setelah:
/bin
,/boot
,/dev
,/etc
,/home
, ...Akhirnya, semua argumen diteruskan ke echo, yang menuliskannya dipisahkan oleh spasi tunggal , memberi
alih-alih nilai variabel.
Ketika variabel dikutip itu akan:
Inilah mengapa Anda harus selalu mengutip semua referensi variabel , kecuali Anda secara khusus memerlukan pemisahan kata dan perluasan nama jalur. Alat seperti shellcheck ada untuk membantu, dan akan memperingatkan tentang tanda kutip yang hilang dalam semua kasus di atas.
sumber
$(..)
strip trailing linefeed. Anda dapat menggunakannyavar=$(cat file; printf x); var="${var%x}"
untuk menyiasatinya.Anda mungkin ingin tahu mengapa ini terjadi. Bersama dengan penjelasan hebat oleh orang lain itu , temukan referensi Mengapa skrip shell saya tersedak spasi atau karakter khusus lainnya? ditulis oleh Gilles di Unix & Linux :
sumber
Selain masalah lain yang disebabkan oleh gagal mengutip,
-n
dan-e
dapat dikonsumsiecho
sebagai argumen. (Hanya yang pertama sah menurut spesifikasi POSIXecho
, tetapi beberapa implementasi umum melanggar spesifikasi dan konsumsi-e
juga).Untuk menghindari hal ini, gunakan
printf
bukanecho
ketika rincian materi.Jadi:
Namun, kutipan yang benar tidak selalu menyelamatkan Anda saat menggunakan
echo
:... sedangkan itu akan menyelamatkan Anda dengan
printf
:sumber
-e
/-n
/ garis miring terbalik saya tidak muncul?" Kami dapat menambahkan tautan dari sini yang sesuai.-n
juga ?-e
. Standar untukecho
tidak menentukan keluaran ketika argumen pertamanya adalah-n
, membuat setiap / semua kemungkinan keluaran legal dalam kasus itu; tidak ada ketentuan untuk itu-e
.pengguna kutip ganda untuk mendapatkan nilai yang tepat. seperti ini:
dan itu akan membaca nilai Anda dengan benar.
sumber
echo $var
keluaran sangat tergantung pada nilaiIFS
variabel. Secara default berisi spasi, tab, dan karakter baris baru:Ini berarti bahwa ketika shell melakukan pemisahan bidang (atau pemisahan kata), shell menggunakan semua karakter ini sebagai pemisah kata. Inilah yang terjadi saat mereferensikan variabel tanpa tanda kutip ganda ke echo it (
$var
) dan dengan demikian keluaran yang diharapkan diubah.Salah satu cara untuk mencegah pemisahan kata (selain menggunakan tanda kutip ganda) adalah dengan menyetel
IFS
ke null. Lihat http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_05 :Menetapkan ke nol berarti menyetel ke nilai kosong:
Uji:
sumber
set -f
mencegah globbingIFS
disetel ke nol,echo $var
akan diperluas keecho '/* Foobar is free software */'
dan perluasan jalur tidak dilakukan di dalam string bertanda kutip tunggal.mkdir "/this thing called Foobar is free software etc/"
melihatnya masih mengembang. Ini jelas lebih praktis sebagai[a-z]
contoh.[a-z]
misalnya.The jawaban dari ks1322 membantu saya untuk mengidentifikasi masalah saat menggunakan
docker-compose exec
:Jika Anda menghilangkan
-T
bendera,docker-compose exec
tambahkan karakter khusus yang memutus keluaran, kita akan melihatb
alih-alih1b
:Dengan
-T
bendera,docker-compose exec
berfungsi seperti yang diharapkan:sumber
Selain meletakkan variabel dalam kutipan, seseorang juga bisa menerjemahkan output dari variabel menggunakan
tr
dan mengonversi spasi ke baris baru.Meskipun ini sedikit lebih berbelit-belit, ini menambahkan lebih banyak keragaman dengan output karena Anda dapat mengganti karakter apa pun sebagai pemisah antara variabel array.
sumber
tr
cara lain untuk membuat array dari file teks.tr
perlu membuat array dengan benar / benar dari file teks - Anda dapat menentukan pemisah apa pun yang Anda inginkan dengan mengatur IFS. Misalnya:IFS=$'\n' read -r -d '' -a arrayname < <(cat file.txt && printf '\0')
berfungsi sepenuhnya melalui bash 3.2 (versi terlama yang beredar luas), dan dengan benar menyetel status keluar ke false jika Andacat
gagal. Dan jika Anda menginginkan, katakanlah, tab sebagai ganti baris baru, Anda cukup mengganti$'\n'
dengan$'\t'
.arrayname=( $( cat file | tr '\n' ' ' ) )
, maka itu rusak pada beberapa lapisan: Ini menggumpal hasil Anda (jadi*
berubah menjadi daftar file di direktori saat ini), dan itu akan bekerja dengan baik tanpatr
( ataucat
, dalam hal ini; seseorang bisa saja menggunakanarrayname=$( $(<file) )
dan itu akan rusak dengan cara yang sama, tetapi kurang efisien).