如何在 Bash 中解析命令行参数?

发布于 2022-02-17 09:42:46

我有一个用这一行调用的脚本:

./myscript -vfd ./foo/bar/someFile -o /fizz/someOtherFile

或者这个:

./myscript -v -f -d -o /fizz/someOtherFile ./foo/bar/someFile 

什么是可接受的解析方式,使得在每种情况下(或两者的某种组合)$v、、$f$d都将设置为true并且$outFile等于/fizz/someOtherFile

关注者
0
被浏览
80
1 个回答
  • 面试哥
    面试哥 2022-02-17
    为面试而生,有面试问题,就找面试哥。

    Bash 空格分隔(例如,--option argument

    cat >/tmp/demo-space-separated.sh <<'EOF'
    #!/bin/bash
    
    POSITIONAL_ARGS=()
    
    while [[ $# -gt 0 ]]; do
      case $1 in
        -e|--extension)
          EXTENSION="$2"
          shift # past argument
          shift # past value
          ;;
        -s|--searchpath)
          SEARCHPATH="$2"
          shift # past argument
          shift # past value
          ;;
        --default)
          DEFAULT=YES
          shift # past argument
          ;;
        -*|--*)
          echo "Unknown option $1"
          exit 1
          ;;
        *)
          POSITIONAL_ARGS+=("$1") # save positional arg
          shift # past argument
          ;;
      esac
    done
    
    set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
    
    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 "Last line of file specified as non-opt/last argument:"
        tail -1 "$1"
    fi
    EOF
    
    chmod +x /tmp/demo-space-separated.sh
    
    /tmp/demo-space-separated.sh -e conf -s /etc /etc/hosts
    
    复制粘贴上面的块的输出
    FILE EXTENSION  = conf
    SEARCH PATH     = /etc
    DEFAULT         =
    Number files in SEARCH PATH with EXTENSION: 14
    Last line of file specified as non-opt/last argument:
    #93.184.216.34    example.com
    
    用法
    demo-space-separated.sh -e conf -s /etc /etc/hosts
    

    Bash 等于分隔(例如,--option=argument

    cat >/tmp/demo-equals-separated.sh <<'EOF'
    #!/bin/bash
    
    for i in "$@"; do
      case $i in
        -e=*|--extension=*)
          EXTENSION="${i#*=}"
          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
    
    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 "Last line of file specified as non-opt/last argument:"
        tail -1 $1
    fi
    EOF
    
    chmod +x /tmp/demo-equals-separated.sh
    
    /tmp/demo-equals-separated.sh -e=conf -s=/etc /etc/hosts
    
    复制粘贴上面的块的输出
    FILE EXTENSION  = conf
    SEARCH PATH     = /etc
    DEFAULT         =
    Number files in SEARCH PATH with EXTENSION: 14
    Last line of file specified as non-opt/last argument:
    #93.184.216.34    example.com
    
    用法
    demo-equals-separated.sh -e=conf -s=/etc /etc/hosts
    

    为了更好地理解在本指南${i#*=}中搜索“子字符串删除” 。它在功能上等价于调用一个不需要的子进程或调用两个不需要的子进程。sed 's/[^=]*=//' <<< "$i"````echo "$i" | sed 's/[^=]*=//'


    将 bash 与 getopt[s] 一起使用

    getopt(1) 限制(较旧、相对较新的getopt版本):

    • 无法处理空字符串参数
    • 无法处理带有嵌入空格的参数

    较新的getopt版本没有这些限制。有关更多信息,请参阅这些文档


    POSIX getopts

    此外,POSIX shell 和其他提供getopts的没有这些限制。我已经包括了一个简单的getopts例子。

    cat >/tmp/demo-getopts.sh <<'EOF'
    #!/bin/sh
    
    # A POSIX variable
    OPTIND=1         # Reset in case getopts has been used previously in the shell.
    
    # Initialize our own variables:
    output_file=""
    verbose=0
    
    while getopts "h?vf:" opt; do
      case "$opt" in
        h|\?)
          show_help
          exit 0
          ;;
        v)  verbose=1
          ;;
        f)  output_file=$OPTARG
          ;;
      esac
    done
    
    shift $((OPTIND-1))
    
    [ "${1:-}" = "--" ] && shift
    
    echo "verbose=$verbose, output_file='$output_file', Leftovers: $@"
    EOF
    
    chmod +x /tmp/demo-getopts.sh
    
    /tmp/demo-getopts.sh -vf /etc/hosts foo bar
    
    复制粘贴上面的块的输出
    verbose=1, output_file='/etc/hosts', Leftovers: foo bar
    
    用法
    demo-getopts.sh -vf /etc/hosts foo bar
    

    的优点getopts是:

    1. 它更便携,并且可以在其他 shell 中工作,例如dash.
    2. -vf filename它可以像典型的 Unix 方式那样自动处理多个单个选项。

    的缺点getopts是它只能处理短选项(-h, not --help)而无需额外代码。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看