Uji string panjang yang bukan nol di Bash: [-n “$ var”] atau [“$ var”]

182

Saya telah melihat tes skrip Bash untuk string panjang yang bukan nol dengan dua cara berbeda. Sebagian besar skrip menggunakan -nopsi:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

Tetapi opsi -n tidak terlalu dibutuhkan:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

Mana cara yang lebih baik?

Demikian pula, yang merupakan cara yang lebih baik untuk pengujian untuk panjang nol:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

atau

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi
AllenHalsey
sumber

Jawaban:

394

Sunting: Ini adalah versi yang lebih lengkap yang menunjukkan lebih banyak perbedaan antara [(alias test) dan [[.

Tabel berikut menunjukkan bahwa apakah suatu variabel dikutip atau tidak, apakah Anda menggunakan tanda kurung tunggal atau ganda dan apakah variabel hanya berisi spasi adalah hal-hal yang mempengaruhi apakah menggunakan tes dengan atau tanpa -n/-zcocok untuk memeriksa variabel.

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

Jika Anda ingin tahu apakah suatu variabel panjangnya tidak nol, lakukan salah satu dari yang berikut:

  • mengutip variabel dalam kurung tunggal (kolom 2a)
  • gunakan -ndan kutip variabel dalam kurung tunggal (kolom 4a)
  • gunakan tanda kurung ganda dengan atau tanpa mengutip dan dengan atau tanpa -n(kolom 1b - 4b)

Perhatikan pada kolom 1a mulai dari baris berlabel "dua" bahwa hasilnya menunjukkan bahwa [mengevaluasi isi variabel seolah-olah mereka adalah bagian dari ekspresi kondisional (hasilnya cocok dengan pernyataan yang tersirat oleh "T" atau "F" di kolom keterangan). Ketika [[digunakan (kolom 1b), konten variabel dilihat sebagai string dan tidak dievaluasi.

Kesalahan dalam kolom 3a dan 5a disebabkan oleh fakta bahwa nilai variabel termasuk spasi dan variabel tidak dikutip. Sekali lagi, seperti yang ditunjukkan pada kolom 3b dan 5b, [[mengevaluasi konten variabel sebagai string.

Sejalan dengan itu, untuk pengujian untuk string panjang nol, kolom 6a, 5b dan 6b menunjukkan cara yang benar untuk melakukan itu. Perhatikan juga bahwa salah satu tes ini dapat dinegasikan jika meniadakan menunjukkan maksud yang lebih jelas daripada menggunakan operasi yang berlawanan. Sebagai contoh: if ! [[ -n $var ]].

Jika Anda menggunakan [, kunci untuk memastikan bahwa Anda tidak mendapatkan hasil yang tidak diharapkan adalah mengutip variabel. Menggunakannya [[, itu tidak masalah.

Pesan kesalahan, yang sedang ditekan, adalah "operator yang tidak diharapkan" atau "operator biner yang diharapkan".

Ini adalah skrip yang menghasilkan tabel di atas.

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF
Dijeda sampai pemberitahuan lebih lanjut.
sumber
1
Terima kasih! Saya telah memutuskan untuk mengadopsi gaya "kutip variabel dalam kurung tunggal (kolom 2a)" IMO, -n hanya menambah noise dan mengurangi keterbacaan. Demikian pula, untuk pengujian untuk panjang nol atau tidak disetel, saya akan menggunakan [! "$ var"] bukan [-z "$ var"].
AllenHalsey
2
Jadi, bagan Anda untuk ["vs [-n"(pertanyaan pertama OP) menunjukkan bahwa mereka benar-benar setara, bukan?
Hobs
1
@ hobs: Ya, dan yang digunakan tergantung mana yang lebih jelas. Anda juga akan melihat bahwa mereka setara ketika menggunakan formulir braket ganda yang lebih disukai saat menggunakan Bash.
Dijeda sampai pemberitahuan lebih lanjut.
1
Jadi, untuk meringkas meja Anda yang luar biasa, cara yang lebih jelas ("lebih baik") untuk menguji string yang bukan nol adalah[["
hobs
22

Lebih baik menggunakan yang lebih kuat [[ sejauh menyangkut Bash.

Kasus biasa

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

Dua konstruksi di atas terlihat bersih dan mudah dibaca. Mereka harus cukup dalam banyak kasus.

Perhatikan bahwa kita tidak perlu mengutip ekspansi variabel di dalamnya [[karena tidak ada bahaya pemisahan kata dan penggumpalan .

Untuk mencegah keluhan lembut dan shellcheck tentang , kita bisa menggunakan opsi.[[ $var ]][[ ! $var ]]-n

Kasing langka

Dalam kasus yang jarang kita harus membuat perbedaan antara "diset ke string kosong" vs "tidak disetel sama sekali", kita bisa menggunakan ini:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

Kami juga dapat menggunakan -vtes:

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

Posting dan dokumentasi terkait

Ada banyak posting yang berkaitan dengan topik ini. Berikut ini beberapa di antaranya:

codeforester
sumber
9

Berikut ini beberapa tes lagi

Benar jika string tidak kosong:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

Benar jika string kosong:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
sumber
Ini tidak menjawab pertanyaan OP tentang apa cara yang lebih baik.
codeforester
0

Jawaban yang benar adalah sebagai berikut:

if [[ -n $var ]] ; then
  blah
fi

Perhatikan penggunaan [[...]], yang menangani dengan benar mengutip variabel untuk Anda.

pengguna2592126
sumber
2
Mengapa menggunakannya -nsaat tidak benar-benar dibutuhkan di Bash?
codeforester
0

Cara alternatif dan mungkin lebih transparan untuk mengevaluasi variabel lingkungan kosong adalah menggunakan ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
pengguna1310789
sumber
10
Ini sekolah yang sangat tua dari masa shell Bourne. Jangan langgengkan kebiasaan lama ini. bashadalah alat yang lebih tajam dari pendahulunya.
tbc0
2
Anda bahkan tidak memerlukan ini dengan shell pra-bash, jika Anda menghindari sintaks bahwa standar POSIX secara eksplisit menandai usang. [ "$ENV_VARIABLE" != "" ]akan bekerja pada setiap shell dengan testimplementasi yang sesuai dengan POSIX - bukan hanya bash, tetapi ash / dash / ksh / etc.
Charles Duffy
... dengan kata lain, jangan gunakan -aatau -ountuk menggabungkan tes tetapi sebaliknya gunakan [ ... ] && [ ... ]atau [ ... ] || [ ... ]dan satu-satunya kasus sudut yang dapat diterapkan untuk menggunakan data variabel arbitrer dalam suatu tes tertutup rapat.
Charles Duffy
-2

Gunakan case/esacuntuk menguji:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
sumber
12
Tidak, tolong Ini bukan untuk apa case.
tbc0
2
casebekerja paling baik ketika ada lebih dari dua alternatif.
codeforester
casetidak dimaksudkan untuk penggunaan seperti ini.
Luis Lavaire