Apa perbedaan dari -a dan -e dalam ekspresi kondisional bash?

12

Dari man bash:

CONDITIONAL EXPRESSIONS
[...]
       -a file
              True if file exists.
[...]
       -e file
              True if file exists.
  1. Jadi apa perbedaan antara [ -a $FILE ]dan [ -e $FILE ], jika ada?
  2. Jika tidak ada perbedaan nyata, mengapa ada dua bendera untuk tujuan yang sama?
polym
sumber
Hanya pengembang yang akan tahu # 2 --- kecuali mereka membagikan alasan mereka kepada komunitas.
Belmin Fernandez

Jawaban:

13

Di bash, dengan konteks testperintah dua argumen , -a filedan -e filesama. Tetapi mereka memiliki beberapa perbedaan, karena -ajuga merupakan operator biner.

-eunary didefinisikan oleh POSIX, tetapi -aunary tidak. POSIX hanya mendefinisikan -abiner (Lihat tes POSIX).

POSIX mendefinisikan tiga testperilaku argumen :

3 argumen:

  • Jika $ 2 adalah biner primer, lakukan uji biner $ 1 dan $ 3.

  • Jika $ 1 adalah '!', Negasikan uji dua argumen dari $ 2 dan $ 3.

  • Jika $ 1 adalah '(' dan $ 3 adalah ')', lakukan tes unary sebesar $ 2. Pada sistem yang tidak mendukung opsi XSI, hasilnya tidak ditentukan jika $ 1 adalah '(' dan $ 3 adalah ')'.

  • Kalau tidak, hasilkan tidak ditentukan.

Demikian -ajuga mengarah pada hasil yang aneh:

$ [ ! -a . ] && echo true
true

-adianggap sebagai operator biner dalam konteks tiga argumen. Lihat Bash FAQ pertanyaan E1 . POSIX juga menyebutkan bahwa -adapatkan dari KornShell tetapi kemudian diubah menjadi -ekarena membuat membingungkan antara -abiner dan -aunary.

-E primer, yang memiliki fungsi serupa dengan yang disediakan oleh shell C, ditambahkan karena ia memberikan satu-satunya cara bagi skrip shell untuk mengetahui apakah ada file tanpa mencoba membuka file tersebut. Karena implementasi diizinkan untuk menambahkan jenis file tambahan, skrip portabel tidak dapat menggunakan:

uji -b foo -o -c foo -o -d foo -o -f foo -o -p foo

untuk mengetahui apakah foo adalah file yang ada. Pada sistem BSD historis, keberadaan file dapat ditentukan oleh:

test -f foo -o -d foo

tetapi tidak ada cara mudah untuk menentukan bahwa file yang ada adalah file biasa. Proposal awal menggunakan KornShell -a primer (dengan makna yang sama), tetapi ini diubah menjadi -e karena ada kekhawatiran tentang probabilitas tinggi manusia membingungkan -a primer dengan -a operator biner.

-abiner juga ditandai sebagai usang, karena mengarah ke beberapa ekspresi ambigu, yang memiliki lebih dari 4 argumen. Dengan> 4 argumen argumen ini, POSIX mendefinisikan hasilnya tidak ditentukan.

cuonglm
sumber
Keren tahu! Sekarang sedikit lebih jelas :)!
Polym
9

.1. Jadi apa perbedaan antara [-a $ FILE] dan [-e $ FILE], jika ada?

Tidak ada perbedaan sama sekali.

Sejalan 505-507di test.cversi pesta 4.2.45(1)-release:

case 'a':           /* file exists in the file system? */
case 'e':
  return (sh_stat (arg, &stat_buf) == 0);

Itu menunjukkan bahwa tidak ada perbedaan nyata antara kedua bendera.

.2. Jika tidak ada perbedaan nyata, mengapa ada dua bendera untuk tujuan yang sama?

Lihat jawaban gnouc .

polym
sumber
3
Di sisi lain, mengingat bahwa -aini juga operator boolean (dan) di bash, tampaknya bug rawan untuk menambahkan makna tambahan yang benar-benar berlebihan; Saya kira itu lebih terkait dengan kompatibilitas dengan beberapa implementasi lain dan / atau kompatibilitas dengan versi sebelumnya yang tidak memiliki -e.
celtschk
@celtschk - itu tidak bugprone, tapi parser shell mungkin. POSIX menentukan -adan -obooleans untuk [ test ]. Tetapi beberapa cangkang (seperti bash) mengisap membedakan argumen dari operator dan [ test ]hasilnya tidak dapat diandalkan. Sejak saat itu POSIX telah mewajibkan bahwa setiap shell yang sesuai menangani setidaknya 4 argumen dalam [ test ]tanda kurung.
mikeserv
5

Jawaban terbaik yang saya temukan adalah ini, dari pertanyaan StackOverflow:

-asudah usang, sehingga tidak terdaftar di halaman manual /usr/bin/testlagi, tetapi masih dalam satu untuk bash. Gunakan -e. Untuk single '[', bash builtin berperilaku sama dengan testbash builtin, yang berperilaku sama dengan /usr/bin/[dan /usr/bin/test (yang satu adalah symlink ke yang lain). Perhatikan efek -atergantung pada posisinya: Jika pada awalnya, artinya file exists. Jika di tengah dua ekspresi, itu berarti logis and.

[ ! -a /path ] && echo existstidak berfungsi, seperti yang ditunjukkan bash manual yang -adianggap sebagai operator biner di sana, dan karenanya di atas tidak diuraikan sebagai negate -a ..tetapi sebagai if '!' and '/path' is true(non-kosong). Jadi, skrip Anda selalu menampilkan "-a"(yang sebenarnya menguji file), dan "! -a"yang sebenarnya adalah biner di andsini.

Karena [[, -atidak digunakan sebagai biner andlagi ( &&digunakan di sana), jadi tujuan uniknya adalah untuk memeriksa file di sana (meskipun sudah usang). Jadi, negasi sebenarnya melakukan apa yang Anda harapkan.

jimm-cl
sumber