Truyền các đối số được đặt tên cho các kịch bản shell


114

Có cách nào dễ dàng để chuyển (nhận) các tham số được đặt tên cho tập lệnh shell không?

Ví dụ,

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

Và bên trong my_script.shnhận chúng là:

# 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"

Điều này có thể có trong Bash hay Zsh?


2
hãy xem docopt - nó cũng giúp với các tham số được đặt tên và cũng xác thực đầu vào
Đánh bại

Câu trả lời:


34

Cú pháp có lẽ gần nhất với điều đó là:

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

7
Liên quan đến điều này, nếu -ktùy chọn được đặt trong vỏ gọi , thì my_script p_out='/some/path' arg_1='5'có tác dụng tương tự. (Tất cả các đối số ở dạng bài tập được thêm vào môi trường, không chỉ các phép gán trước lệnh.)
chepner

13
Tôi đã từng thích cú pháp này, nhưng nó có một cảnh báo LỚN: sau khi thực hiện lệnh / hàm, các biến đó sẽ vẫn được xác định trong phạm vi hiện tại! Ví dụ: x=42 echo $x; echo $xCó nghĩa là trong lần thực hiện tiếp theo my_script, nếu p_outbị bỏ qua, nó sẽ bám vào giá trị được truyền lần trước !! ( '/some/path')
Lucas Cimon

@LucasCimon Bạn có thể không dùng unsetchúng sau lần thực hiện đầu tiên, đặt lại chúng trước lần thực hiện tiếp theo không?
Nikos Alexandris

2
@LucasCimon Điều đó không đúng. x=42 echo $xthậm chí không xuất bất cứ thứ gì nếu $xkhông được xác định trước.
Hauke ​​Laging 22/03/18

Bạn nói đúng @HaukeLaging, cảm ơn vì đã sửa nó
Lucas Cimon

148

Nếu bạn không bị giới hạn ở các tên đối số một chữ cái my_script -p '/some/path' -a5, thì trong bash, bạn có thể sử dụng hàm dựng sẵn getopts, ví dụ:

#!/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"

Sau đó bạn có thể làm

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

Có một hướng dẫn getopts nhỏ hữu ích hoặc bạn có thể gõ help getoptstại dấu nhắc shell.


25
Đây phải là câu trả lời được chấp nhận
Kaushik Ghose

3
Tôi biết điều này là một chút cũ, nhưng tại sao chỉ có 1 chữ cái cho các đối số?
Kevin

1
Tôi đã thực hiện điều này (nhưng với id). Khi tôi chạy nó với my_script -i asd -d asdmột chuỗi rỗng cho dđối số. Khi tôi chạy nó, my_script -d asd -i asdtôi nhận được chuỗi rỗng cho cả hai đối số.
Milkncookiez

3
@Milkncookiez - Tôi gặp vấn đề tương tự - Tôi không bao gồm dấu ':' sau đối số cuối cùng (a 'w' trong trường hợp của tôi). Khi tôi đã thêm ':' nó bắt đầu hoạt động như mong đợi
Derek

37

Tôi đã đánh cắp cái này từ drupal.org , nhưng bạn có thể làm một cái gì đó như thế này:

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

Nhắc nhở duy nhất là bạn phải sử dụng cú pháp my_script --p_out=/some/path --arg_1=5.


7
Sự cảnh báo là không cần thiết. :) Bạn có thể có các điều kiện như sau:-c|--condition)
Milkncookiez

28

Tôi sử dụng kịch bản này và làm việc như một cơ duyên:

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"

Sử dụng

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

Kết quả bảng điều khiển:

STEPS = ABC
REPOSITORY_NAME = stackexchange

STEPSREPOSITORY_NAME đã sẵn sàng để sử dụng trong tập lệnh.

Không quan trọng thứ tự các đối số là gì.


4
Đây là gọn gàng và nên được trả lời chấp nhận.
Miguelmorin

15

Với zsh, bạn sẽ sử dụng 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"

Nhưng bạn sẽ gọi kịch bản với myscript --p_out foo.

Lưu ý rằng zparseoptskhông hỗ trợ viết tắt các tùy chọn dài hoặc --p_out=foocú pháp như GNU getopt(3).


Bạn có biết tại sao zparseopts chỉ sử dụng một dấu gạch ngang cho các đối số trong khi trong []đó là 2 dấu gạch ngang không? Không có ý nghĩa!
Timo

@Timo, xem info zsh zparseoptschi tiết
Stéphane Chazelas

9

Tôi vừa nghĩ ra kịch bản này

while [ $# -gt 0 ]; do

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

  shift
done

vượt qua nó như thế my_script --p_out /some/path --arg_1 5và sau đó trong kịch bản bạn có thể sử dụng $arg_1$p_out.


Tôi thích giải pháp này trong KSH88 Tôi đã phải v=``echo ${1} | awk '{print substr($1,3)}'`` typeset $v="$2"(Xóa một backtick mỗi bên)
hol

-2

Nếu một hàm hoặc một ứng dụng có nhiều đối số hơn 0, thì nó luôn có một đối số cuối cùng.

Nếu bạn muốn đọc cờ tùy chọn và các cặp giá trị, như trong: $ ./t.sh -o output -i input -l last

Và bạn muốn chấp nhận một số lượng các cặp tùy chọn / giá trị khác nhau,

Và không muốn một cây "nếu .. thì .. khác .. fi",

Sau đó, sau khi kiểm tra số lượng đối số khác không và thậm chí,

Viết một vòng lặp while với bốn câu lệnh eval này là phần thân, theo sau là một câu lệnh tình huống sử dụng hai giá trị được xác định trong mỗi lần đi qua vòng lặp.

Phần khó khăn của kịch bản được thể hiện ở đây:

#!/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

-3
mitsos@redhat24$ my_script "a=1;b=mitsos;c=karamitsos"
#!/bin/sh
eval "$1"

bạn vừa tiêm các tham số dòng lệnh bên trong phạm vi tập lệnh !!


3
Điều này không hoạt động với cú pháp OP chỉ định; họ muốn-a 1 -b mitsos -c karamitsos
Michael Mrozek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.