在Bash中解析命令行参数的两种样例脚本

2024-05-09 15:47:27 浏览数 (2)

问题:

假设,我有一个脚本,它会被这样一行调用:

代码语言:javascript复制
./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

或者这个:

代码语言:javascript复制
./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile

如何解析 v、f 和 d,使它们都被设置为 true,并且 outFile 等于 /fizz/someOtherFile ?

回答:

以空格分隔选项和参数

样例程序如下:

代码语言:javascript复制
cat >/tmp/demo-space-separated.sh <<'EOF'
#!/bin/bash

POSITIONAL_ARGS=() #初始化一个空数组,用来存储位置参数

while [[ $# -gt 0 ]]; do  #当命令行参数的数量大于0时,进入循环
  case $1 in
    -e|--extension)  #如果参数是这个,脚本会将紧随其后的参数(文件扩展名)保存在变量 EXTENSION 中
      EXTENSION="$2"
      shift # 跳过参数
      shift # 跳过后面的值
      ;;
    -s|--searchpath)  #如果参数是这个,脚本会将紧随其后的参数(搜索路径)保存在变量 SEARCHPATH 中
      SEARCHPATH="$2"
      shift # 跳过参数
      shift # 跳过后面的值
      ;;
    --default)  #如果参数是这个,脚本会将变量 DEFAULT 设置为 YES
      DEFAULT=YES
      shift # 跳过参数
      ;;
    -*|--*)  #如果参数是以 - 或 -- 开头且未知的选项,打印错误信息并退出
      echo "Unknown option $1"
      exit 1
      ;;
    *)  #对于所有其他非选项参数(即位置参数),将它们逐一添加到 POSITIONAL_ARGS 数组中,
      POSITIONAL_ARGS =("$1") # 保存位置参数
      shift
      ;;
  esac
done

set -- "${POSITIONAL_ARGS[@]}" # 将数组里的参数设置为当前 shell 的位置参数

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)

if [[ -n $1 ]]; then
    echo "The non option arguments are:" $@
fi
EOF

chmod  x /tmp/demo-space-separated.sh

/tmp/demo-space-separated.sh -e log -s /var/log pos1 pos2

复制粘贴上述代码块的输出:

推荐用法:

代码语言:javascript复制
demo-space-separated.sh -e log -s /var/log pos1

以等号分隔选项和参数

样例程序如下:

代码语言:javascript复制
cat >/tmp/demo-equals-separated.sh <<'EOF'
#!/bin/bash

for i in "$@"; do  #循环遍历所有的命令行参数
  case $i in
    -e=*|--extension=*)
      EXTENSION="${i#*=}"  #使用 ${i#*=} 来提取等号 = 后面的值(即文件扩展名),并将其保存在变量 EXTENSION 中
      shift # past argument=value
      ;;
    -s=*|--searchpath=*)
      SEARCHPATH="${i#*=}"
      shift # past argument=value
      ;;
    --default)
      DEFAULT=YES
      shift # past argument with no value
      ;;
    -*|--*)
      echo "Unknown option $i"
      exit 1
      ;;
    *)
      ;;
  esac
done  #结束for循环

echo "FILE EXTENSION  = ${EXTENSION}"
echo "SEARCH PATH     = ${SEARCHPATH}"
echo "DEFAULT         = ${DEFAULT}"
echo "Number files in SEARCH PATH with EXTENSION:" $(ls -1 "${SEARCHPATH}"/*."${EXTENSION}" | wc -l)

if [[ -n $1 ]]; then
    echo "The non option arguments are:" $@
fi
EOF

chmod  x /tmp/demo-equals-separated.sh

/tmp/demo-equals-separated.sh -e=log -s=/var/log pos3 pos4

其中${i#*=}用于删除参数$i从左边开始匹配的第一个=及其左边的所有字符。

复制粘贴上述代码块的输出:

推荐用法:

代码语言:javascript复制
demo-equals-separated.sh -e=log -s=/var/log pos1 pos2

参考:

  • stackoverflow question 192249
  • https://www.gnu.org/software/bash/manual/bash.html#The-Set-Builtin
  • https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion

0 人点赞