Saya mencoba menulis skrip di bash yang memeriksa validitas input pengguna.
Saya ingin mencocokkan input (katakanlah variabel x
) dengan daftar nilai yang valid.
apa yang saya temukan saat ini adalah:
for item in $list
do
if [ "$x" == "$item" ]; then
echo "In the list"
exit
fi
done
Pertanyaan saya adalah apakah ada cara yang lebih sederhana untuk melakukan ini,
seperti list.contains(x)
untuk sebagian besar bahasa pemrograman.
Tambahan:
Katakanlah daftar adalah:
list="11 22 33"
kode saya akan menggemakan pesan hanya untuk nilai-nilai itu karena list
diperlakukan sebagai array dan bukan string, semua manipulasi string akan divalidasi 1
sementara saya ingin gagal.
[[ $list =~ (^| )$x($| ) ]] && echo 'yes' || echo 'no'
x=.
contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
.[[ " $list " =~ " $x " ]] && echo 'yes' || echo 'no'
. ini benar dengan asumsi spasi adalah pemisah dan$x
tidak berisi spasiisIn()
sehingga Anda dapat menulis item terlebih dahulu dalam parameter. Anda juga dapat melakukanecho
sesuatu selain menggunakanexit
seperti ini:[[ $2 =~ (^|[[:space:]])$1($|[[:space:]]) ]] && echo 1 || echo 0
Jadi Anda dapat menggunakan fungsi ini dengan cara ini:result=$(isIn "-t" "-o -t 45") && echo $result
Matvey benar, tetapi Anda harus mengutip $ x dan mempertimbangkan segala jenis "spasi" (misalnya baris baru) dengan
[[ $list =~ (^|[[:space:]])"$x"($|[[:space:]]) ]] && echo 'yes' || echo 'no'
jadi, yaitu
# list_include_item "10 11 12" "2" function list_include_item { local list="$1" local item="$2" if [[ $list =~ (^|[[:space:]])"$item"($|[[:space:]]) ]] ; then # yes, list include item result=0 else result=1 fi return $result }
berakhir kemudian
`list_include_item "10 11 12" "12"` && echo "yes" || echo "no"
atau
if `list_include_item "10 11 12" "1"` ; then echo "yes" else echo "no" fi
Perhatikan bahwa Anda harus menggunakan
""
dalam kasus variabel:`list_include_item "$my_list" "$my_item"` && echo "yes" || echo "no"
sumber
$item
berisi karakter khusus, seperti.
(tetapi$list
variabel mungkin perlu dikutip di dalam pengujian). Dan fungsi dapat didefinisikan lebih sederhana:contains () { [[ "$1" =~ (^|[[:space:]])"$2"($|[[:space:]]) ]]; }
.list="aa bb xx cc ff"
danx="aa bb"
$list =~ (^|[[:space:]])"$item"($|[[:space:]])
. Atau, jika Anda punya waktu, saya akan senang mendengar penjelasan Anda untuk ungkapan ini. Catatan: Saya kira itu ekspresi regex (dimulai dengan ^ dll) tapi saya tidak tahu apa artinya = ~. Jadi saya ingin penjelasan keseluruhan: Pbagaimana tentang
echo $list | grep -w -q $x
Anda dapat memeriksa output atau
$?
baris di atas untuk membuat keputusan.grep -w
memeriksa pola kata keseluruhan. Menambahkan-q
mencegah menggemakan daftar.sumber
Solusi termudah IMHO adalah menambahkan spasi di awal dan menambahkan string asli dengan spasi dan memeriksa regex dengan
[[ ]]
haystack='foo bar' needle='bar' if [[ " $haystack " =~ .*\ $needle\ .* ]]; then ... fi
ini tidak akan menjadi positif palsu pada nilai dengan nilai yang mengandung jarum sebagai substring, misalnya dengan tumpukan jerami
foo barbaz
.(Konsep ini dicuri tanpa malu-malu dari
hasClass()
-Method JQuery )sumber
haystack="foo:bar"
dan[[ ":$haystack:" = *:$needle:* ]]
Anda juga dapat menggunakan (* karakter pengganti) di luar pernyataan kasus, jika Anda menggunakan tanda kurung ganda:
string='My string'; if [[ $string == *My* ]] then echo "It's there!"; fi
sumber
*My*
) harus berada di sisi kanan pengujian.Jika daftar nilai Anda akan di-hardcode dalam skrip, cukup mudah untuk menguji menggunakan
case
. Berikut adalah contoh singkat, yang dapat Anda sesuaikan dengan kebutuhan Anda:for item in $list do case "$x" in item1|item2) echo "In the list" ;; not_an_item) echo "Error" >&2 exit 1 ;; esac done
Jika daftarnya adalah variabel array pada waktu proses, salah satu jawaban lain mungkin lebih cocok.
sumber
Jika daftar diperbaiki dalam skrip, saya paling suka yang berikut ini:
validate() { grep -F -q -x "$1" <<EOF item 1 item 2 item 3 EOF }
Kemudian gunakan
validate "$x"
untuk menguji apakah$x
diizinkan.Jika Anda menginginkan satu baris, dan tidak peduli dengan spasi kosong pada nama item, Anda dapat menggunakan ini (pemberitahuan
-w
daripada-x
):validate() { echo "11 22 33" | grep -F -q -w "$1"; }
Catatan:
sh
sesuai dengan POSIX .validate
tidak tidak menerima substring (menghapus-x
pilihan untuk grep jika Anda ingin itu).validate
menafsirkan argumennya sebagai string tetap, bukan ekspresi reguler (hapus-F
opsi ke grep jika Anda menginginkannya).Kode contoh untuk menjalankan fungsinya:
for x in "item 1" "item2" "item 3" "3" "*"; do echo -n "'$x' is " validate "$x" && echo "valid" || echo "invalid" done
sumber
Pertimbangkan untuk mengeksploitasi kunci array asosiatif . Saya akan menganggap ini mengungguli pencocokan regex / pola dan perulangan, meskipun saya belum membuat profilnya.
declare -A list=( [one]=1 [two]=two [three]='any non-empty value' ) for value in one two three four do echo -n "$value is " # a missing key expands to the null string, # and we've set each interesting key to a non-empty value [[ -z "${list[$value]}" ]] && echo -n '*not* ' echo "a member of ( ${!list[*]} )" done
Keluaran:
sumber
echo -n
) dengan penggunaan kreatif parameter:do is="${list[$value]+is }"; echo "$value ${is:-is *not* }a member of ( ${!list[*]} )"; done
.Saya merasa lebih mudah menggunakan formulir
echo $LIST | xargs -n1 echo | grep $VALUE
seperti yang diilustrasikan di bawah ini:LIST="ITEM1 ITEM2" VALUE="ITEM1" if [ -n "`echo $LIST | xargs -n1 echo | grep -e \"^$VALUE`$\" ]; then ... fi
Ini berfungsi untuk daftar yang dipisahkan spasi, tetapi Anda dapat menyesuaikannya dengan pembatas lain (seperti
:
) dengan melakukan hal berikut:LIST="ITEM1:ITEM2" VALUE="ITEM1" if [ -n "`echo $LIST | sed 's|:|\\n|g' | grep -e \"^$VALUE`$\"`" ]; then ... fi
Perhatikan bahwa
"
diperlukan agar tes dapat bekerja.sumber
LIST="SOMEITEM1 ITEM2"
pengembalian menjadi benar meskipunITEM1
tidak ada di dalamnyaPikir saya akan menambahkan solusi saya ke daftar.
# Checks if element "$1" is in array "$2" # @NOTE: # Be sure that array is passed in the form: # "${ARR[@]}" elementIn () { # shopt -s nocasematch # Can be useful to disable case-matching local e for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done return 1 } # Usage: list=(11 22 33) item=22 if elementIn "$item" "${list[@]}"; then echo TRUE; else echo FALSE fi # TRUE item=44 elementIn $item "${list[@]}" && echo TRUE || echo FALSE # FALSE
sumber
Contoh
$ in_list super test me out NO $ in_list "super dude" test me out NO $ in_list "super dude" test me "super dude" YES # How to use in another script if [ $(in_list $1 OPTION1 OPTION2) == "NO" ] then echo "UNKNOWN type for param 1: Should be OPTION1 or OPTION2" exit; fi
in_list
function show_help() { IT=$(CAT <<EOF usage: SEARCH_FOR {ITEM1} {ITEM2} {ITEM3} ... e.g. a b c d -> NO a b a d -> YES "test me" how "test me" -> YES ) echo "$IT" exit } if [ "$1" == "help" ] then show_help fi if [ "$#" -eq 0 ]; then show_help fi SEARCH_FOR=$1 shift; for ITEM in "$@" do if [ "$SEARCH_FOR" == "$ITEM" ] then echo "YES" exit; fi done echo "NO"
sumber
Dengan asumsi variabel TARGET hanya dapat 'binomial' atau 'regresi', maka berikut ini akan dilakukan:
# Check for modeling types known to this script if [ $( echo "${TARGET}" | egrep -c "^(binomial|regression)$" ) -eq 0 ]; then echo "This scoring program can only handle 'binomial' and 'regression' methods now." >&2 usage fi
Anda dapat menambahkan lebih banyak string ke dalam daftar dengan memisahkannya dengan | (pipa) karakter.
Keuntungan menggunakan egrep, adalah Anda dapat dengan mudah menambahkan case insensitivity (-i), atau memeriksa skenario yang lebih kompleks dengan ekspresi reguler.
sumber
Ini hampir merupakan proposal asli Anda, tetapi hampir 1 baris. Tidak serumit jawaban valid lainnya, dan tidak begitu tergantung pada versi bash (dapat berfungsi dengan bash lama).
OK=0 ; MP_FLAVOURS="vanilla lemon hazelnut straciatella" for FLAV in $MP_FLAVOURS ; do [ $FLAV == $FLAVOR ] && { OK=1 ; break; } ; done [ $OK -eq 0 ] && { echo "$FLAVOR not a valid value ($MP_FLAVOURS)" ; exit 1 ; }
Saya kira proposal saya masih bisa ditingkatkan, baik panjang maupun gayanya.
sumber
Jika tidak terlalu lama; Anda bisa merangkainya di antara kesetaraan sepanjang perbandingan OR logis seperti itu.
if [ $ITEM == "item1" -o $ITEM == "item2" -o $ITEM == "item3" ]; then echo In the list fi
Saya memiliki masalah yang tepat ini dan sementara yang di atas jelek, lebih jelas apa yang sedang terjadi daripada solusi umum lainnya.
sumber
Kompgen bawaan shell dapat membantu di sini. Ia dapat mengambil daftar dengan flag -W dan mengembalikan setiap kecocokan potensial yang ditemukannya.
# My list can contain spaces so I want to set the internal # file separator to newline to preserve the original strings. IFS=$'\n' # Create a list of acceptable strings. accept=( 'foo' 'bar' 'foo bar' ) # The string we will check word='foo' # compgen will return a list of possible matches of the # variable 'word' with the best match being first. compgen -W "${accept[*]}" "$word" # Returns: # foo # foo bar
Kita bisa menulis fungsi untuk menguji apakah sebuah string sama dengan string yang paling cocok. Ini memungkinkan Anda mengembalikan 0 atau 1 untuk lulus atau gagal masing-masing.
function validate { local IFS=$'\n' local accept=( 'foo' 'bar' 'foo bar' ) if [ "$1" == "$(compgen -W "${accept[*]}" "$1" | head -1)" ] ; then return 0 else return 1 fi }
Sekarang Anda dapat menulis pengujian yang sangat bersih untuk memvalidasi jika sebuah string dapat diterima.
validate "blah" || echo unacceptable if validate "foo" ; then echo acceptable else echo unacceptable fi
sumber
Solusi alternatif yang terinspirasi oleh respons yang diterima, tetapi itu menggunakan logika terbalik:
MODE="${1}" echo "<${MODE}>" [[ "${MODE}" =~ ^(preview|live|both)$ ]] && echo "OK" || echo "Uh?"
Di sini, input ($ MODE) harus menjadi salah satu opsi dalam ekspresi reguler ('preview', 'live', atau 'keduanya'), berlawanan dengan mencocokkan seluruh daftar opsi dengan input pengguna. Tentu saja, Anda tidak mengharapkan ekspresi reguler berubah.
sumber