将命名参数传递给 shell 脚本

Ame*_*ina 192 bash zsh shell-script arguments

有没有什么简单的方法可以将命名参数传递(接收)给 shell 脚本?

例如,

my_script -p_out '/some/path' -arg_1 '5'
Run Code Online (Sandbox Code Playgroud)

并在内部my_script.sh将它们接收为:

# 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"
Run Code Online (Sandbox Code Playgroud)

这在 Bash 或 Zsh 中可能吗?

ste*_*ver 224

如果您不介意仅限于单字母参数名称 ie 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"
Run Code Online (Sandbox Code Playgroud)

然后你可以做

$ ./my_script -p '/some/path' -a5
Argument p_out is /some/path
Argument arg_1 is 5
Run Code Online (Sandbox Code Playgroud)

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

  • 这应该是公认的答案 (28认同)
  • 我知道这有点旧,但为什么参数只有 1 个字母? (8认同)
  • @Milkncookiez——我有一个类似的问题——我没有在最后一个参数之后包含一个 ':'(在我的例子中是一个 'w')。一旦我添加了 ':' 它开始按预期工作 (4认同)
  • 我实现了这个(但使用`i`和`d`)。当我使用 `my_script -i asd -d asd` 运行它时,我得到一个用于 `d` 参数的空字符串。当我运行它`my_script -d asd -i asd`时,我得到两个参数的空字符串。 (2认同)
  • @KaushikGhose 刚刚修复它。我想迟到总比不到好。我希望当您接受的答案以外的其他答案获得更多接受票时通知您。 (2认同)

小智 58

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

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
Run Code Online (Sandbox Code Playgroud)

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

  • 警告不是必需的。:) 你可以有如下条件:`-c|--condition)` (13认同)
  • @LeQuangNam 在上面的示例中,将 `--p_out` 更改为 `-p|--p_out` (2认同)

小智 58

我使用这个脚本并像魅力一样工作:

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"
Run Code Online (Sandbox Code Playgroud)

用法

bash my_scripts.sh  STEPS="ABC" REPOSITORY_NAME="stackexchange"
Run Code Online (Sandbox Code Playgroud)

控制台结果:

STEPS = ABC
REPOSITORY_NAME = stackexchange
Run Code Online (Sandbox Code Playgroud)

STEPSREPOSITORY_NAME已准备好在脚本中使用。

参数的顺序无关紧要。

  • 这很整洁,应该是公认的答案。 (4认同)
  • 如果添加 `[[ $KEY == --* ]] && KEY=${KEY:2}`,您还可以支持传递带有可选 `--` 前缀的参数名称(例如 `--profile=test`)就在 `export "$KEY"="$VALUE"` 行之前。 (2认同)

Hau*_*ing 40

可能最接近的语法是:

p_out='/some/path' arg_1='5' my_script
Run Code Online (Sandbox Code Playgroud)

  • 我曾经喜欢这种语法,但它有一个很大的警告:在命令/函数执行之后,这些变量仍将在当前作用域中定义!例如:`x=42 echo $x; echo $x` 这意味着在下一次执行`my_script`时,如果省略`p_out`,它会坚持上次传递的值!!(`'/一些/路径'`) (14认同)
  • 与此相关,如果在 *calling* shell 中设置了 `-k` 选项,则 `my_script p_out='/some/path' arg_1='5'` 具有相同的效果。(赋值形式的所有参数都被添加到环境中,而不仅仅是命令之前的那些赋值。) (8认同)
  • @LucasCimon 那是不正确的。如果之前没有定义 `$x`,`x=42 echo $x` 甚至不会输出任何内容。 (2认同)

Sté*_*las 18

使用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"
Run Code Online (Sandbox Code Playgroud)

但是你会用myscript --p_out foo.

请注意,zparseopts它不支持缩写长选项或--p_out=foo像 GNUgetopt(3)那样的语法。


小智 15

我刚想出这个脚本

while [ $# -gt 0 ]; do

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

  shift
done
Run Code Online (Sandbox Code Playgroud)

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


小智 10

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

while [ $# -gt 0 ]; do
  case "$1" in
    --url*|-u*)
      if [[ "$1" != *=* ]]; then shift; fi # Value is next arg if no `=`
      URL="${1#*=}"
      ;;
    --file*|-f*)
      if [[ "$1" != *=* ]]; 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
Run Code Online (Sandbox Code Playgroud)


tor*_*tte 7

这个答案最初是对@cdmo 的回答的编辑(也感谢@Milkncookiez 的评论!),正如预期的那样被拒绝了,所以它是这样的:

#!/usr/bin/env bash

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

以下调用都是等效的:

$ ./my-script.sh -a "lofa" -p "miez"
$ ./my-script.sh -arg_1 "lofa" --p_out "miez"
$ ./my-script.sh --arg_1 "lofa" -p "miez"
Run Code Online (Sandbox Code Playgroud)

使用变量时,可以确保它们具有"${p_out:-"default value"}"例如使用设置的默认值