Saya mencoba untuk membangun skrip shell yang menerima berbagai opsi dan getopts
sepertinya solusi yang baik karena dapat menangani pemesanan variabel opsi dan argumen (saya pikir!).
Saya hanya akan menggunakan opsi pendek dan setiap opsi pendek akan membutuhkan nilai yang sesuai, misalnya: ./command.sh -a arga -g argg -b argb
tetapi saya ingin mengizinkan opsi dimasukkan dalam urutan non-spesifik, seperti cara kebanyakan orang terbiasa bekerja dengan perintah shell .
Poin lainnya adalah bahwa saya ingin melakukan pemeriksaan nilai argumen opsi saya sendiri, idealnya dalam case
pernyataan. Alasan untuk ini adalah bahwa pengujian saya :)
dalam case
pernyataan saya telah menghasilkan hasil yang tidak konsisten (mungkin karena kurangnya pemahaman di pihak saya).
Sebagai contoh:
#!/bin/bash
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
;;
u)
MYSQL_USER=$OPTARG
;;
p)
MYSQL_PASS=$OPTARG
;;
d)
BACKUP_DIR=$OPTARG
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
:)
echo "Option -$OPTARG requires an argument" >&2
exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Gagal untuk kejadian seperti ini ... ./command.sh -d -h
Ketika saya ingin flag -d sebagai membutuhkan argumen tapi saya mendapatkan nilai -d=-h
yang bukan yang saya butuhkan.
Jadi saya pikir akan lebih mudah untuk menjalankan validasi saya sendiri dalam laporan kasus untuk memastikan bahwa setiap opsi diatur dan ditetapkan hanya sekali.
Saya mencoba melakukan yang berikut tetapi if [ ! "$MYSQL_HOST" ]; then
blok saya tidak terpicu.
OPTIND=1 # Reset if getopts used previously
if (($# == 0)); then
echo "Usage"
exit 2
fi
while getopts ":h:u:p:d:" opt; do
case "$opt" in
h)
MYSQL_HOST=$OPTARG
if [ ! "$MYSQL_HOST" ]; then
echo "host not set"
exit 2
fi
;;
u)
MYSQL_USER=$OPTARG
if [ ! "$MYSQL_USER" ]; then
echo "username not set"
exit 2
fi
;;
p)
MYSQL_PASS=$OPTARG
if [ ! "$MYSQL_PASS" ]; then
echo "password not set"
exit 2
fi
;;
d)
BACKUP_DIR=$OPTARG
if [ ! "$BACKUP_DIR" ]; then
echo "backup dir not set"
exit 2
fi
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 2;;
#:)
# echo "Option -$opt requires an argument" >&2
# exit 2;;
esac
done
shift $((OPTIND-1))
echo "MYSQL_HOST='$MYSQL_HOST' MYSQL_USER='$MYSQL_USER' MYSQL_PASS='$MYSQL_PASS' BACKUP_DIR='$BACKUP_DIR' Additionals: $@"
Apakah ada alasan bahwa saya tidak dapat memeriksa apakah OPTARG
panjang nol dari dalam getopts ... while ... case
?
Apa cara yang lebih baik untuk menjalankan validasi argumen saya sendiri getopts
dalam kasus di mana saya tidak ingin bergantung pada :)
. Lakukan validasi argumen saya di luar while ... case ... esac
?
Lalu saya bisa berakhir dengan nilai argumen -d
dll dan tidak menangkap opsi yang hilang.
while
loop menjalankan opsi. Ini adalah metode yang telah saya gunakan seolah-olah itu verbose dibandingkan dengan yang lain, sangat jelas apa yang terjadi dalam naskah. Dan pemeliharaan oleh orang lain adalah penting dalam hal ini.Karena jawaban lain sebenarnya tidak terlalu banyak menjawab pertanyaan Anda: Ini adalah perilaku yang diharapkan dan berdasarkan pada bagaimana POSIX ditafsirkan:
Ini adalah semua yang sedang dinyatakan, dan untuk membuat cerita pendek (tidak terlalu panjang):
getopts
tidak secara ajaib tahu bahwa argumen yang benar-benar valid untuk opsi yang Anda butuhkan untuk memiliki argumen sebenarnya bukanlah argumen opsi tetapi opsi lain . Tidak ada yang mencegah Anda dari memiliki-h
argumen yang valid ke-d
opsi, yang dalam praktiknya berarti bahwagetopts
hanya akan menimbulkan kesalahan jika opsi Anda yang membutuhkan argumen datang terakhir pada baris perintah, misalnya:test.sh
:Contoh:
Alasan mengapa pendekatan kedua Anda tidak berhasil adalah karena parsing yang
getopts
dilakukan masih sama, jadi setelah Anda berada di dalam loop, "kerusakan" sudah dilakukan.Jika Anda benar-benar ingin melarang ini, seperti yang ditunjukkan Benubird, Anda harus memeriksa sendiri argumen opsi Anda dan melemparkan kesalahan jika itu sama dengan opsi yang valid.
sumber
Saya akan terus menggunakan
getopts
tetapi melakukan beberapa pemeriksaan tambahan setelah: (belum diuji)membutuhkan bash versi 4
sumber
The Gnu non-shell built-in getopt melakukan sedikit pekerjaan untuk Anda tetapi memungkinkan fleksibilitas yang lebih besar karena Anda bisa menentukan apa yang terjadi setelah bendera ditemukan, termasuk pergeseran dari argumen sendiri.
Saya menemukan artikel yang membandingkan getop dan getopt yang mungkin akan membantu karena manualnya agak sulit untuk dipahami (setidaknya sebelum kopi).
sumber
Pertama, Anda harus menggunakan
if [ -z "$MYSQL_USER" ]
.Kedua, tidak perlu penugasan -
if [ -z "$OPTARG" ]
akan berfungsi dengan baik.Ketiga, saya curiga apa yang sebenarnya Anda inginkan
if [ ${#OPTARG} = 0 ]
. $ {# x} adalah hal bash mengembalikan panjang string $ x (lihat lebih lanjut di sini ).Keempat, jika Anda melakukan validasi Anda sendiri, saya akan merekomendasikan getopt daripada getopts, karena memberikan fleksibilitas lebih.
Terakhir, untuk menjawab pertanyaan pertama Anda, Anda dapat mendeteksi kapan sebuah bendera dilewatkan sebagai argumen dengan meletakkan daftar bendera di bagian atas, seperti ini:
Dan kemudian memiliki tanda centang pada opsi di mana Anda ingin memeriksa apakah argumen yang diberikan adalah opsi, sesuatu seperti ini:
Bukan jawaban yang sempurna, tetapi berhasil! Masalah Anda adalah bahwa "-h" adalah argumen yang benar-benar valid, dan Anda tidak memberi jalan bagi shell untuk mengetahui bahwa itu hanya mencari parameter yang bukan juga flag yang valid.
sumber