Mengapa tidak menggunakan banyak perintah dengan | | atau && pekerjaan bersyarat?

12

Ini berfungsi pada prompt shell (bash, dash):

[ -z "" ] && echo A || echo B
A

Namun, saya mencoba untuk menulis skrip shell POSIX , dimulai seperti ini:

#!/bin/sh

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

readonly raw_input_string=${1}

[ -z "${raw_input_string}" ] && echo "The given argument is empty."; exit 1

Dan saya tidak tahu mengapa, tetapi saya tidak mendapatkan pesannya :

Argumen yang diberikan kosong.

jika saya memanggil skrip seperti ini:

./test_empty_argument ""

Mengapa demikian?

LinuxSecurityFreak
sumber
5
Lihat Bagaimana saya bisa menguji apakah suatu variabel kosong atau hanya berisi spasi? untuk cara menguji apakah suatu variabel kosong, tidak disetel, atau hanya berisi kosong. Masalah dalam pertanyaan ini tidak ada hubungannya dengan itu.
ilkkachu
1
Cukup gunakanif [ X”” = X”$var” ] ; then echo isempty ; fi
user2497
3
@ user2497 Tidak ada alasan untuk menggunakannya dalam shell apa pun yang dirilis dalam 20 tahun terakhir. Itu solusi untuk cangkang kerang tua.
chepner
@ chepner Jadi ini bukan solusi yang valid? Sesuatu yang lain harus digunakan?
user2497
6
[ "" = "$var" ]akan bekerja dengan baik; string kosong yang dikutip tidak akan dihapus dari daftar argumen [. Tapi itu juga tidak perlu, karena [ -z "$var" ] juga berfungsi dengan baik.
chepner

Jawaban:

37

Perhatikan bahwa baris Anda

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

ini sama dengan

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."
exit 1

( ;kaleng tanda kutip , dalam sebagian besar keadaan, digantikan oleh karakter baris baru)

Ini berarti bahwa exit 1pernyataan selalu dieksekusi terlepas dari berapa banyak argumen yang diteruskan ke skrip. Ini pada gilirannya berarti bahwa pesan The given argument is empty.tersebut tidak akan pernah memiliki peluang untuk dicetak.

Untuk menjalankan lebih dari satu pernyataan tunggal setelah pengujian menggunakan "sintaks hubung singkat", kelompokkan pernyataan di { ...; }. Alternatifnya adalah dengan menggunakan ifpernyataan yang benar (yang, IMHO, terlihat lebih bersih dalam skrip):

if [ "$#" -ne 1 ]; then
    echo 'Invalid number of arguments, expected one.' >&2
    exit 1
fi

Anda memiliki masalah yang sama dengan tes kedua Anda.


Mengenai

[ -z "" ] && echo A || echo B

Ini akan bekerja untuk contoh yang diberikan, tetapi generik

some-test && command1 || command2

akan tidak sama dengan

if some-test; then
    command1
else
    command2
fi

Sebaliknya, itu lebih seperti

if ! { some-test && command1; }; then
    command2
fi

atau

if some-test && command1; then
    :
else
    command2
fi

Artinya, jika tes atau perintah pertama gagal, perintah kedua dijalankan, yang berarti memiliki potensi untuk mengeksekusi ketiga pernyataan yang terlibat.

Kusalananda
sumber
18

Ini:

[ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; exit 1

tidak:

[ "${#}" -eq 1 ] || { echo "Invalid number of arguments, expected one."; exit 1; }

Tetapi sebaliknya adalah:

{ [ "${#}" -eq 1 ] || echo "Invalid number of arguments, expected one."; } 
exit 1

Script Anda keluar terlepas dari berapa banyak argumen yang Anda berikan.

muru
sumber
8

Salah satu cara untuk membuatnya lebih mudah dibaca adalah mendefinisikan diefungsi (à la perl) seperti:

die() {
  printf >&2 '%s\n' "$@"
  exit 1
}

# then:

[ "$#" -eq 1 ] || die "Expected one argument, got $#"

[ -n "$1" ] || die "Empty argument not supported"

Anda dapat menambahkan lebih banyak lonceng dan peluit seperti warna, awalan, nomor baris ... jika perlu.

Stéphane Chazelas
sumber
Dalam praktiknya, apakah Anda pernah memanggil diefungsi Anda dengan beberapa argumen? (Jika demikian, dapatkah Anda memberi contoh?) Saya menggunakan fungsi yang hampir identik die, tetapi menggunakan "$*"sebagai gantinya, yang mungkin lebih dari apa yang Anda inginkan?
jrw32982 mendukung Monica
3
Nilainya "$@"adalah memungkinkan pesan multi-line tanpa perlu menambahkan baris baru secara literal.
Charles Duffy
1
@ jrw32982, menggunakan "$*"untuk bergabung dengan args dengan spasi juga berarti Anda perlu mengatur $IFSke SPC agar dapat bekerja di semua konteks termasuk yang $IFStelah dimodifikasi. Atau dengan ksh/ zsh, Anda dapat menggunakan print -r -- "$@"atau echo -E - "$@"dalam zsh.
Stéphane Chazelas
@CharlesDuffy Ya, tapi saya belum pernah melihat itu dilakukan dalam konteks diefungsi -type. Yang saya tanyakan adalah: dalam praktiknya, pernahkah Anda melihat seseorang menulis die "unable to blah:" "some error", dengan tujuan mendapatkan pesan kesalahan 2-baris?
jrw32982 mendukung Monica
@ StéphaneChazelas Poin bagus. Jadi (dalam formulasi saya) seharusnya die() { IFS=" "; printf >&2 "%s\n" "$*"; exit 1; }. Pernahkah Anda secara pribadi menggunakan diefungsi semacam ini untuk printfmenghasilkan pesan kesalahan multi-line dengan melewati beberapa argumen? Atau apakah Anda hanya pernah mengirimkan argumen tunggal diesehingga hanya menambahkan satu baris baru ke outputnya?
jrw32982 mendukung Monica
-1

Saya sering melihat ini sebagai tes untuk string kosong:

if [ "x$foo" = "x" ]; then ...
wef
sumber
Seharusnya "=" - diperbaiki.
wef
3
Praktek itu secara harfiah dari tahun 1970-an. Tidak ada alasan apapun untuk menggunakannya dengan shell yang kompatibel dengan 1992 POSIX sh standar (asalkan benar mengutip digunakan dan fungsionalitas sekarang-usang seperti -a, -o, (dan )sebagai derectives untuk memberitahu testuntuk menggabungkan beberapa operasi dalam doa tunggal dihindari; lihat penanda OB di pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html ).
Charles Duffy
Unifikasi non-linux dikirimkan dengan 'sh' asli ke tahun '90 -an - mungkin masih dilakukan. Dalam pekerjaan saya saat itu, saya harus menulis skrip instalasi portabel dan saya menggunakan konstruksi ini. Saya hanya melihat skrip instalasi yang dikirimkan NVidia untuk linux dan mereka masih menggunakan konstruk ini.
wef
NVidia dapat menggunakannya, tetapi itu tidak berarti mereka memiliki alasan teknis untuk melakukannya; Pengembangan kargo-kultus di UNIX komersial sayangnya lazim. Bahkan Heirloom Bourne tidak memiliki bug yang dipermasalahkan - jadi basis kode SunOS (yang menjadi UNIX komersial terakhir yang mengirimkan non-POSIX /bin/sh, dan pendahulu langsung Heirloom Bourne) juga tidak memilikinya.
Charles Duffy
1
Saya tidak memakai topi, jadi saya tidak bisa berjanji untuk memposting video YouTube memakannya jika seseorang membuka sebuah shell yang diterbitkan pada UNIX komersial dengan bug ini pasca-1990 ... tetapi jika saya melakukannya, itu akan menggoda . :)
Charles Duffy