Melewati argumen bernama ke skrip shell

114

Apakah ada cara mudah untuk menyampaikan (menerima) parameter bernama ke skrip shell?

Sebagai contoh,

my_script -p_out '/some/path' -arg_1 '5'

Dan di dalam my_script.shmenerima mereka sebagai:

# I believe this notation does not work, but is there anything close to it?
p_out=$ARGUMENTS['p_out']
arg1=$ARGUMENTS['arg_1']

printf "The Argument p_out is %s" "$p_out"
printf "The Argument arg_1 is %s" "$arg1"

Apakah ini mungkin di Bash atau Zsh?

Amelio Vazquez-Reina
sumber
2
lihat docopt - ini membantu dengan parameter bernama dan melakukan validasi input juga
Kalahkan

Jawaban:

34

Sintaks yang mungkin paling dekat dengan itu adalah:

p_out='/some/path' arg_1='5' my_script
Hauke ​​Laging
sumber
7
Terkait dengan ini, jika -kopsi diatur dalam shell panggilan , maka my_script p_out='/some/path' arg_1='5'memiliki efek yang sama. (Semua argumen dalam bentuk penugasan ditambahkan ke lingkungan, bukan hanya penugasan yang mendahului perintah.)
chepner
13
Saya dulu suka sintaks ini, tetapi memiliki peringatan BESAR: setelah eksekusi perintah / fungsi, variabel-variabel itu masih akan didefinisikan dalam lingkup saat ini! Misalnya: x=42 echo $x; echo $xYang berarti dalam eksekusi selanjutnya my_script, jika p_outdihilangkan, itu akan menempel pada nilai yang dilewati terakhir kali !! ( '/some/path')
Lucas Cimon
@LucasCimon Tidak bisakah Anda melakukannya unsetsetelah eksekusi pertama, mengatur ulang sebelum eksekusi berikutnya?
Nikos Alexandris
2
@LucasCimon Itu tidak benar. x=42 echo $xbahkan tidak menghasilkan apa pun jika $xtidak didefinisikan sebelumnya.
Hauke ​​Laging
Anda benar @ HaukeLaging, terima kasih sudah memperbaiki itu
Lucas Cimon
148

Jika Anda tidak keberatan terbatas pada nama argumen satu-huruf yaitu my_script -p '/some/path' -a5, maka dalam bash Anda dapat menggunakan built-in getopts, misalnya

#!/bin/bash

while getopts ":a:p:" opt; do
  case $opt in
    a) arg_1="$OPTARG"
    ;;
    p) p_out="$OPTARG"
    ;;
    \?) echo "Invalid option -$OPTARG" >&2
    ;;
  esac
done

printf "Argument p_out is %s\n" "$p_out"
printf "Argument arg_1 is %s\n" "$arg_1"

Maka Anda bisa melakukannya

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5

Ada tutorial Getopts kecil yang membantu atau Anda bisa mengetikkan help getoptsprompt shell.

Steeldriver
sumber
25
Ini harus menjadi jawaban yang diterima
Kaushik Ghose
3
Saya tahu ini agak lama, tetapi mengapa hanya 1 huruf untuk argumen?
Kevin
1
Saya menerapkan ini (tetapi dengan idan d). Ketika saya menjalankannya dengan my_script -i asd -d asdsaya mendapatkan string kosong untuk dargumen. Ketika saya menjalankannya my_script -d asd -i asdsaya mendapatkan string kosong untuk kedua argumen.
Milkncookiez
3
@Milkncookiez - Saya punya masalah yang sama - Saya tidak memasukkan ':' setelah argumen terakhir (a 'w' dalam kasus saya). Setelah saya menambahkan ':' mulai bekerja seperti yang diharapkan
Derek
37

Saya mencuri ini dari drupal.org , tetapi Anda dapat melakukan sesuatu seperti ini:

while [ $# -gt 0 ]; do
  case "$1" in
    --p_out=*)
      p_out="${1#*=}"
      ;;
    --arg_1=*)
      arg_1="${1#*=}"
      ;;
    *)
      printf "***************************\n"
      printf "* Error: Invalid argument.*\n"
      printf "***************************\n"
      exit 1
  esac
  shift
done

Satu-satunya peringatan adalah bahwa Anda harus menggunakan sintaks my_script --p_out=/some/path --arg_1=5.

cdmo
sumber
7
Peringatan itu tidak perlu. :) Anda dapat memiliki ketentuan sebagai berikut:-c|--condition)
Milkncookiez
28

Saya menggunakan skrip ini dan berfungsi seperti mantra:

for ARGUMENT in "$@"
do

    KEY=$(echo $ARGUMENT | cut -f1 -d=)
    VALUE=$(echo $ARGUMENT | cut -f2 -d=)   

    case "$KEY" in
            STEPS)              STEPS=${VALUE} ;;
            REPOSITORY_NAME)    REPOSITORY_NAME=${VALUE} ;;     
            *)   
    esac    


done

echo "STEPS = $STEPS"
echo "REPOSITORY_NAME = $REPOSITORY_NAME"

Pemakaian

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"

Hasil konsol:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPS dan REPOSITORY_NAME siap digunakan dalam skrip.

Tidak masalah urutan argumen apa.

JRichardsz
sumber
4
Ini rapi dan harus menjadi jawaban yang diterima.
miguelmorin
15

Dengan zsh, Anda akan menggunakan zparseopts:

#! /bin/zsh -
zmodload zsh/zutil
zparseopts -A ARGUMENTS -p_out: -arg_1:

p_out=$ARGUMENTS[--p_out]
arg1=$ARGUMENTS[--arg_1]

printf 'Argument p_out is "%s"\n' "$p_out"
printf 'Argument arg_1 is "%s"\n' "$arg_1"

Tetapi Anda akan memanggil skrip dengan myscript --p_out foo.

Catatan yang zparseoptstidak mendukung opsi penyingkat panjang atau --p_out=foosintaksis seperti GNU getopt(3).

Stéphane Chazelas
sumber
Apakah Anda tahu mengapa zparseopts menggunakan hanya satu tanda hubung untuk argumen sedangkan di []dalamnya adalah 2 tanda hubung? Tidak masuk akal!
Timo
@Timo, lihat info zsh zparseoptsdetailnya
Stéphane Chazelas
9

Saya baru saja membuat naskah ini

while [ $# -gt 0 ]; do

   if [[ $1 == *"--"* ]]; then
        v="${1/--/}"
        declare $v="$2"
   fi

  shift
done

berikan seperti my_script --p_out /some/path --arg_1 5dan kemudian dalam skrip yang dapat Anda gunakan $arg_1dan $p_out.

Shahzad Malik
sumber
Saya suka solusi ini di KSH88 saya harus v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(Hapus satu backtick masing-masing sisi)
hol
-2

Jika suatu fungsi atau aplikasi memiliki lebih dari nol argumen, ia selalu memiliki argumen terakhir.

Jika Anda ingin membaca flag opsi dan pasangan nilai, seperti pada: $ ./t.sh -o output -i input -l last

Dan Anda ingin menerima sejumlah variabel dari pasangan opsi / nilai,

Dan tidak ingin pohon "if .. then .. else .. fi" yang besar,

Kemudian setelah memeriksa jumlah argumen yang bukan nol dan genap,

Tulis loop sementara dengan keempat pernyataan eval ini sebagai badan, diikuti oleh pernyataan kasus menggunakan dua nilai yang ditentukan dalam setiap melewati loop.

Bagian rumit dari skrip ditunjukkan di sini:

#!/bin/sh    

# For each pair - this chunk is hard coded for the last pair.
eval TMP="'$'$#"
eval "PICK=$TMP"
eval TMP="'$'$(($#-1))"
eval "OPT=$TMP"

# process as required - usually a case statement on $OPT
echo "$OPT \n $PICK"

# Then decrement the indices (as in third eval statement) 

:<< EoF_test
$ ./t.sh -o output -i input -l last
-l 
last
$ ./t.sh -o output -l last
-l 
last
$ ./t.sh  -l last
-l 
last
EoF_test
knc1
sumber
-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

Anda baru saja menyuntikkan parameter baris perintah di dalam lingkup skrip !!

thettalos
sumber
3
Ini tidak bekerja dengan sintaks OP yang ditentukan; mereka mau-a 1 -b mitsos -c karamitsos
Michael Mrozek