有没有简单的方法可以将命名参数传递(接收)到Shell脚本?例如,

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


并且在my_script.sh内接收它们as:

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


在Bash或Zsh中可能吗?

评论

看看docopt –它有助于命名参数并进行输入验证

相关:stackoverflow.com/questions/5499472/…

#1 楼

如果您不介意局限于单字母参数名称,即my_script -p '/some/path' -a5,那么在bash中,您可以使用内置的getopts,例如

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


,那么您可以do

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


有一个有用的Small getopts教程,或者您可以在shell提示符下键入help getopts

评论


这应该是公认的答案

– Kaushik Ghose
15年5月21日在13:38

我知道这有点老了,但是为什么参数只用1个字母呢?

–凯文
17年4月28日在15:54

我实现了这一点(但使用i和d)。当我使用my_script -i asd -d asd运行它时,d参数得到一个空字符串。运行my_script -d asd -i asd时,两个参数都得到空字符串。

–Milkncookiez
18年2月14日在22:48



@Milkncookiez-我有一个类似的问题-我在最后一个参数之后没有加':'(在我的情况下是'w')。一旦我添加了':',它就会按预期开始工作

–德里克
18年5月24日在15:30

@KaushikGhose刚刚修复它。我想迟到总比没有好。我希望SO能够在您接受的答案以外的其他答案获得更多接受的投票时通知您。

–阿梅里奥·巴斯克斯·雷纳(Amelio Vazquez-Reina)
20 Jan 9 '20 at 12:47

#2 楼

我从drupal.org上偷了这个,但是您可以这样做:

while [ $# -gt 0 ]; do
  case "" 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


唯一的警告是您必须使用语法my_script --p_out=/some/path --arg_1=5

评论


不需要警告。 :)您可以具有以下条件:-c | --condition)

–Milkncookiez
17年5月30日在14:27

文档以截断变量中的字符串(并继续该线程中的趋势,这应该是可接受的答案)。

–toraritte
20年8月8日在16:31

#3 楼

我使用此脚本,其工作方式像一个魅力:

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"


用法

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


控制台结果:

STEPS = ABC
REPOSITORY_NAME = stackexchange


STEPS和REPOSITORY_NAME已准备在脚本中使用。

参数的顺序无关紧要。

评论


这很简洁,应该是公认的答案。

–miguelmorin
19年2月1日在17:07

这是一个很好的解决方案,但解析时间似乎很长,例如在我的PC上需要5到10秒,这是正常现象吗?

–宇航员
19年12月13日在14:01

在我看来,这是瞬时的。

–JRichardsz
19/12/13在17:22

这很简洁,但是假设您在键和值之间有=分隔符。即您不能使用这样的空格传递值:-p 3000或--port 3000

–杰里·格林(Jerry Green)
20-11-04在10:32



#4 楼

可能最接近的语法是:

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


评论


与此相关,如果在调用外壳程序中设置了-k选项,则my_script p_out ='/ some / path'arg_1 ='5'具有相同的效果。 (所有赋值形式的参数都添加到环境中,而不仅仅是命令前的那些赋值。)

–chepner
2014年5月14日22:55

我曾经喜欢这种语法,但是它有一个很大的警告:在执行命令/函数之后,那些变量仍将在当前作用域中定义!例如:x = 42 echo $ x; echo $ x这意味着在下一次执行my_script时,如果省略p_out,它将坚持上次通过的值! ('/ some / path')

–卢卡斯·西蒙(Lucas Cimon)
2015年9月1日在9:13

@LucasCimon您能否在第一次执行后取消设置它们,而在下一次执行之前将它们复位?

– Nikos Alexandris
17/12/18在15:46

@LucasCimon这是不正确的。 x = 42如果$ x之前没有定义,echo $ x甚至不输出任何东西。

–Hauke Laging
18 Mar 22 '18 at 11:02

您说对了@HaukeLaging,感谢您纠正

–卢卡斯·西蒙(Lucas Cimon)
18年4月19日在9:01

#5 楼

对于zsh,您可以使用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"


,但是您可以使用myscript --p_out foo调用脚本。

请注意,zparseopts不会支持缩写长选项或类似GNU --p_out=foogetopt(3)语法。

评论


您知道zparseopts为什么仅对参数使用一个破折号而在[]中使用两个破折号吗?没有道理!

– Timo
17年7月7日在14:59

@Timo,有关详细信息,请参见信息zsh zparseopts

–StéphaneChazelas
17年11月7日在16:20

#6 楼

我只是想出了这个脚本

while [ $# -gt 0 ]; do

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

  shift
done


my_script --p_out /some/path --arg_1 5一样传递它,然后在脚本中可以使用$arg_1$p_out

评论


我喜欢KSH88中的这种解决方案,我不得不v =“ echo $ {1} | awk'{print substr($ 1,3)}'''排版$ v =“ $ 2”(每侧删除一个反引号)

–hol
18-09-24在9:45



#7 楼

我发现cdmo的解决方案是最好的,因为它不仅限于单个字母。稍作调整,它将消耗空格分隔的参数(例如--url www.example.com)和分配有赋值的参数(例如,同义词参数-u=www.example.com)。甚至可以分析标志参数:

while [ $# -gt 0 ]; do
  case "" in
    --url*|-u*)
      if [[ "" != *=* ]]; then shift; fi # Value is next arg if no `=`
      URL="${1#*=}"
      ;;
    --file*|-f*)
      if [[ "" != *=* ]]; then shift; fi
      FILE="${1#*=}"
      ;;
    --help|-h)
      printf "Meaningful help message" # Flag argument
      exit 0
      ;;
    *)
      >&2 printf "Error: Invalid argument\n"
      exit 1
      ;;
  esac
  shift
done


评论


意外标记')'附近的语法错误*)我添加;;时有效出口0后

–蒂姆
20-5-5在23:41



#8 楼

这个答案最初是对@cdmo答案的编辑(也感谢@Milkncookiez的评论!),但被按预期拒绝了,所以就这样了:
#!/usr/bin/env bash

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

以下调用均等效:
$ ./my-script.sh -a "lofa" -p "miez"
$ ./my-script.sh -arg_1 "lofa" --p_out "miez"
$ ./my-script.sh --arg_1 "lofa" -p "miez"

使用变量时,可以确保使用例如"${p_out:-"default value"}"来设置默认值。

#9 楼

我结合了这里推荐的一些方法。就我而言,我需要一个可以处理这种格式参数的脚本:--arg1=test
所以我使用了以下内容:
 args=()

for argument in "$@"
do

    key=$(echo $argument | cut -f1 -d=)
    value=$(echo $argument | cut -f2 -d=)   

    if [[ $key == *"--"* ]]; then
        v="${key/--/}"
        declare $v="${value}" 
   fi
done

args+=( '--local_dir' ${local_dir})
args+=( '--format' ${format})

python3 ./test.py "${args[@]}"
 

您可以看到我将这些参数加在一起以调用带有这些参数的python脚本:
test_script.sh --local_dir=test --format=other
感谢Shahzad Malik和JRichardsz提供了结合这两种方法的指南。

#10 楼

如果函数或应用程序的参数多于零,则它总是有最后一个参数。

如果要读取选项标志和值对,例如:
并且您想接受可变数量的选项/值对,

并且不希望有一个庞大的“ if .. then .. else .. fi”树,

然后在检查了非零且偶数的参数计数后,

用这四个eval语句作为主体编写while循环,然后使用case语句使用在每遍循环。

脚本的棘手部分在此处演示:

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


#11 楼

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


您刚刚在脚本范围内注入了命令行参数!

评论


这不适用于OP指定的语法;他们想要-a 1 -b mitsos -c karamitsos

– Michael Mrozek
15年2月18日在14:48