Uji apakah perintah menghasilkan string kosong

237

Bagaimana saya bisa menguji jika suatu perintah menghasilkan string kosong?

barp
sumber
7
Sebuah perintah tidak mengembalikan sebuah string (ia mengembalikan kode keluar integer kecil, biasanya 0 untuk sukses dan 1 atau 2 pada kegagalan), tetapi mungkin menampilkan beberapa data, untuk dimasukkan ke dalam string.
Basile Starynkevitch
@BasileStarynkevitch ini adalah hal yang saya butuhkan.Saya perintah tahu hasil exit code.But perintah saya kode keluar selalu 0.so saya perlu untuk mengontrol output
Barp
1
Anda harus membaca beberapa tutorial scripting Bash tldp.org/LDP/abs/html
Basile Starynkevitch
Joey Hess memiliki kegunaan ifneuntuk ini. joeyh.name/code/moreutils
tripleee

Jawaban:

309

Sebelumnya, pertanyaannya bertanya bagaimana memeriksa apakah ada file dalam direktori. Kode berikut mencapai itu, tetapi lihat jawaban rsp untuk solusi yang lebih baik.


Output kosong

Perintah tidak mengembalikan nilai - mereka mengeluarkannya. Anda dapat menangkap output ini dengan menggunakan substitusi perintah ; mis $(ls -A). Anda dapat menguji untuk string yang tidak kosong di Bash seperti ini:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Perhatikan bahwa saya telah menggunakan -Adaripada -a, karena menghapus entri direktori simbolik ( .) dan parent ( ..).

Catatan: Seperti yang ditunjukkan dalam komentar, substitusi perintah tidak menangkap baris baru . Oleh karena itu, jika perintah hanya menghasilkan baris baru, substitusi akan menangkap apa-apa dan tes akan kembali salah. Meskipun sangat tidak mungkin, ini dimungkinkan dalam contoh di atas, karena satu baris baru adalah nama file yang valid! Informasi lebih lanjut dalam jawaban ini .


Kode keluar

Jika Anda ingin memeriksa bahwa perintah selesai dengan sukses, Anda dapat memeriksa $?, yang berisi kode keluar dari perintah terakhir (nol untuk sukses, bukan nol untuk kegagalan). Sebagai contoh:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

Info lebih lanjut di sini .

Will Vousden
sumber
4
Anda dapat menghilangkan -n , jadi itu hanyaif [[ $(ls -A) ]]; then
pengguna
6
Metode ini memberikan false untuk perintah yang hanya menghasilkan baris baru.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Terima kasih kepada netj untuk saran untuk meningkatkan sumber asli saya:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


Ini adalah pertanyaan lama tetapi saya melihat setidaknya dua hal yang perlu perbaikan atau setidaknya beberapa klarifikasi.

Masalah pertama

Masalah pertama yang saya lihat adalah bahwa sebagian besar contoh yang diberikan di sini tidak berfungsi . Mereka menggunakan perintah ls -aldan ls -Al- keduanya menghasilkan string yang tidak kosong di direktori kosong. Contoh-contoh itu selalu melaporkan bahwa ada file bahkan ketika tidak ada .

Untuk alasan itu Anda hanya perlu menggunakan ls -A- Mengapa ada orang yang ingin menggunakan -lsaklar yang berarti "menggunakan format daftar panjang" ketika semua yang Anda inginkan adalah menguji apakah ada output atau tidak, toh?

Jadi sebagian besar jawaban di sini tidak benar.

Masalah kedua

Masalah kedua adalah bahwa sementara beberapa jawaban bekerja dengan baik (orang-orang yang tidak menggunakan ls -alatau ls -Altetapi ls -Asebaliknya) mereka semua melakukan sesuatu seperti ini:

  1. jalankan perintah
  2. buffer seluruh output dalam RAM
  3. mengubah output menjadi string single-line besar
  4. bandingkan string itu dengan string kosong

Apa yang saya sarankan lakukan adalah:

  1. jalankan perintah
  2. hitung karakter dalam outputnya tanpa menyimpannya
    • atau bahkan lebih baik - hitung jumlah maksimal 1 karakter using head -c1
      (terima kasih kepada netj karena memposting ide ini di komentar di bawah)
  3. bandingkan angka itu dengan nol

Jadi misalnya, alih-alih:

if [[ $(ls -A) ]]

Saya akan menggunakan:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Dari pada:

if [ -z "$(ls -lA)" ]

Saya akan menggunakan:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

dan seterusnya.

Untuk output kecil mungkin bukan masalah tetapi untuk output yang lebih besar perbedaannya mungkin signifikan:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

Bandingkan dengan:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

Dan bahkan lebih baik:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Contoh lengkap

Contoh yang diperbarui dari jawaban oleh Will Vousden:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Diperbarui lagi setelah saran oleh netj :

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Pembaruan tambahan oleh jakeonfire :

grepakan keluar dengan kegagalan jika tidak ada kecocokan. Kita dapat memanfaatkan ini untuk menyederhanakan sintaksis sedikit:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Buang spasi putih

Jika perintah yang Anda uji bisa menghasilkan beberapa spasi putih yang ingin Anda perlakukan sebagai string kosong, maka alih-alih:

| wc -c

Anda bisa menggunakan:

| tr -d ' \n\r\t ' | wc -c

atau dengan head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

atau semacam itu.

Ringkasan

  1. Pertama, gunakan perintah yang berfungsi .

  2. Kedua, hindari penyimpanan yang tidak perlu dalam RAM dan pemrosesan data yang berpotensi besar.

Jawabannya tidak menentukan bahwa output selalu kecil sehingga kemungkinan output yang besar perlu dipertimbangkan juga.

rsp
sumber
3
[[ $(... | head -c | wc -c) -gt 0 ]]atau [[ -n $(... | head -c1) ]]lebih baik karena wc -charus mengkonsumsi seluruh output dari perintah.
netj
@netj Ini adalah ide yang sangat bagus. Lihat jawaban saya yang diperbarui - saya menambahkan saran Anda. Terima kasih banyak!
rsp
Ada ide tentang bagaimana mendapatkan hasil?
fahrradflucht
Saya pikir yang asli (tanpa kepala) lebih baik dalam kasus umum. Tidak selalu merupakan ide yang baik untuk mematikan perintah segera setelah mulai menulis output!
stk
@stk Sebagian besar program akan keluar dengan aman setelah menerima sinyal mematikan.
cambunctious
40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

Ini akan menjalankan perintah dan memeriksa apakah output (string) yang dikembalikan memiliki panjang nol. Anda mungkin ingin memeriksa halaman manual 'test' untuk flag lainnya.

Gunakan "" di sekitar argumen yang sedang diperiksa, jika tidak, hasil kosong akan menghasilkan kesalahan sintaks karena tidak ada argumen kedua (untuk memeriksa) yang diberikan!

Catatan: yang ls -laselalu kembali .dan ..menggunakan itu tidak akan berfungsi, lihat halaman manual ls . Selain itu, walaupun ini mungkin tampak mudah dan mudah, saya kira itu akan mudah pecah. Menulis skrip / aplikasi kecil yang mengembalikan 0 atau 1 tergantung pada hasilnya jauh lebih dapat diandalkan!

Veer
sumber
Anda mungkin lebih suka menggunakan $(daripada backquote, karena $(sarang lebih baik
Basile Starynkevitch
Anda benar, itu adalah praktik yang lebih baik untuk menggunakannya selalu, meskipun kami tidak perlu bersarang di sini.
Veger
23

Bagi mereka yang menginginkan solusi elegan, versi bash-independen (sebenarnya harus bekerja di shell modern lainnya) dan mereka yang suka menggunakan one-liners untuk tugas cepat. Kita mulai!

ls | grep . && echo 'files found' || echo 'files not found'

(perhatikan sebagai salah satu komentar yang disebutkan, ls -aldan pada kenyataannya, hanya -ldan -asemua akan mengembalikan sesuatu, jadi dalam jawaban saya, saya menggunakan sederhanals

Alex
sumber
5
Jika Anda menggunakan grep -q ^sebagai gantinya, karakter baris baru juga akan cocok, dan grep tidak akan mencetak apa pun ke output standar. Selain itu, ia akan keluar segera setelah menerima input apa pun, daripada menunggu aliran inputnya berakhir.
mortehu
Harus mulai dengan ls -Ajika Anda ingin memasukkan file-file (atau direktori)
wrlee
13

Manual Referensi Bash

6.4 Ekspresi Bersyarat Bash

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

Anda dapat menggunakan versi steno:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi
pengguna
sumber
1
Ada -z atau -n yang tidak ada dalam parenthessis [[...]].
71GA
4
@ 71GA: Mungkin itu mudah untuk diabaikan, tetapi jika Anda akan melihat dengan seksama, Anda akan melihat bahwa itu stringadalah sinonim untuk -n string.
pengguna
10

Seperti komentar Jon Lin, ls -alakan selalu menampilkan (untuk .dan ..). Anda ingin ls -Almenghindari dua direktori ini.

Misalnya Anda bisa memasukkan output perintah ke dalam variabel shell:

v=$(ls -Al)

Notasi yang lebih tua, tidak dapat ditumpuk, adalah

v=`ls -Al`

tapi saya lebih suka notasi nestable $(...)

Anda dapat menguji apakah variabel itu tidak kosong

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

Dan Anda bisa menggabungkan keduanya sebagai if [ -n "$(ls -Al)" ]; then

Basile Starynkevitch
sumber
5

Saya kira Anda ingin output dari ls -alperintah, jadi dalam bash, Anda akan memiliki sesuatu seperti:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi
Jon Lin
sumber
Ini tidak berfungsi: perintah lstidak pernah dijalankan. Anda hanya memeriksa apakah perluasan variabel LS tidak kosong.
gniourf_gniourf
1
@ gniourf_gniourf - apa yang membuat Anda berpikir begitu? Mantra backtick diakui tidak secantik atau sefleksibel $ (), tetapi berfungsi ...
tink
The -asaklar tidak akan pernah kembali ada konten (yaitu, itu akan kembali konten apakah ada file atau tidak) karena selalu ada implisit .dan ..hadir direktori.
mundur
3

Inilah solusi untuk kasus yang lebih ekstrem:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

Ini akan bekerja

  • untuk semua cangkang Bourne;
  • jika output perintah adalah semua nol;
  • efisien terlepas dari ukuran output;

namun,

  • perintah atau subprosesnya akan dimatikan begitu ada sesuatu yang keluar.
Singkat
sumber
1

Semua jawaban yang diberikan sejauh ini berhubungan dengan perintah yang mengakhiri dan menghasilkan string yang tidak kosong.

Sebagian besar rusak dalam pengertian berikut:

  • Mereka tidak berurusan dengan benar dengan perintah yang hanya menghasilkan baris baru;
  • mulai dari Bash≥4.4 sebagian besar spam standar akan error jika perintah menghasilkan byte nol (karena mereka menggunakan substitusi perintah);
  • sebagian besar akan menghirup aliran output penuh, jadi akan menunggu sampai perintah berakhir sebelum menjawab. Beberapa perintah tidak pernah berakhir (coba, misalnya, yes).

Jadi untuk memperbaiki semua masalah ini, dan untuk menjawab pertanyaan berikut secara efisien,

Bagaimana saya bisa menguji jika suatu perintah menghasilkan string kosong?

kamu bisa memakai:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Anda juga dapat menambahkan beberapa batas waktu sehingga untuk menguji apakah perintah output string yang tidak kosong dalam waktu tertentu, menggunakan read's -tpilihan. Misalnya, untuk batas waktu 2,5 detik:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Ucapan. Jika Anda merasa perlu menentukan apakah perintah menghasilkan string yang tidak kosong, kemungkinan besar Anda memiliki masalah XY.

gniourf_gniourf
sumber
Saya pikir jawaban ini agak kurang dihargai. Saya kinerja pengujian beberapa solusi di sini menggunakan 100 iterasi untuk masing-masing 3 skenario input ( seq 1 10000000, seq 1 20, dan printf""). Satu-satunya pertarungan pendekatan baca ini tidak menang adalah dengan -z "$(command)"pendekatan untuk input kosong. Meski begitu, kerugiannya hanya sekitar 10-15%. Mereka tampaknya impas seq 1 10. Terlepas dari itu, -z "$(command)"pendekatannya menjadi sangat lambat ketika ukuran input bertambah sehingga saya menghindari menggunakannya untuk input ukuran variabel.
abathur
0

Berikut adalah pendekatan alternatif yang menulis std-out dan std-err dari beberapa perintah file sementara, dan kemudian memeriksa untuk melihat apakah file itu kosong. Manfaat dari pendekatan ini adalah bahwa ia menangkap kedua output, dan tidak menggunakan sub-shell atau pipa. Aspek-aspek terakhir ini penting karena mereka dapat mengganggu trapping bash exit handling (mis. Di sini )

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"
Darren Smith
sumber
0

Terkadang Anda ingin menyimpan output, jika tidak kosong, untuk meneruskannya ke perintah lain. Jika demikian, Anda bisa menggunakan sesuatu seperti

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

Dengan cara ini, rmperintah tidak akan hang jika daftar kosong.

Scott C Wilson
sumber