/usr/share/doc/util-linux/getopt-example.bash:何时才会用到“Internal error!”?

编程语言 2026-07-10

在得知关于 getopt 的信息后(并且失望地发现似乎无论如何都要和 eval 一起工作,这意味着为了安全起见,函数输入必须经过过滤),我仍然按照 man getopt 的建议使用这个示例:

#!/bin/bash

# A small example script for using the getopt(1) program.
# This script will only work with bash(1).
# A similar script using the tcsh(1) language can be found
# as getopt-example.tcsh.

# Example input and output (from the bash prompt):
#
# ./getopt-example.bash -a par1 'another arg' --c-long 'wow!*\?' -cmore -b " very long "
# Option a
# Option c, no argument
# Option c, argument 'more'
# Option b, argument ' very long '
# Remaining arguments:
# --> 'par1'
# --> 'another arg'
# --> 'wow!*\?'

# Note that we use "$@" to let each command-line parameter expand to a
# separate word. The quotes around "$@" are essential!
# We need TEMP as the 'eval set --' would nuke the return value of getopt.
TEMP=$(getopt -o 'ab:c::' --long 'a-long,b-long:,c-long::' -n 'example.bash' -- "$@")

if [ $? -ne 0 ]; then
    echo 'Terminating...' >&2
    exit 1
fi

# Note the quotes around "$TEMP": they are essential!
eval set -- "$TEMP"
unset TEMP

while true; do
    case "$1" in
        '-a'|'--a-long')
            echo 'Option a'
            shift
            continue
        ;;
        '-b'|'--b-long')
            echo "Option b, argument '$2'"
            shift 2
            continue
        ;;
        '-c'|'--c-long')
            # c has an optional argument. As we are in quoted mode,
            # an empty parameter will be generated if its optional
            # argument is not found.
            case "$2" in
                '')
                    echo 'Option c, no argument'
                ;;
                *)
                    echo "Option c, argument '$2'"
                ;;
            esac
            shift 2
            continue
        ;;
        '--')
            shift
            break
        ;;
        *)
            echo 'Internal error!' >&2
            exit 1
        ;;
    esac
done

echo 'Remaining arguments:'
for arg; do
    echo "--> '$arg'"
done

我没能想到一个会输出 'Internal error!' 的情景:有没有什么想法?

解决方案

该脚本需要两条独立的命令处于同步状态;分别是 TEMP=$(getopt ...)case ... esac。仅改变其中一个命令可能会使脚本的功能失效。

脚本的作者添加了 Internal error! 信息,作为兜底测试,以防他们忘记让两条命令保持同步。[作者也完全可以生成信息 Something is screwed up with this script's coding if you see this message!。]

假设我们想修改脚本以处理一个新的 -d 选项,因此进行如下修改:

TEMP=$(getopt -o 'ab:c:d::'  -l  'a-long,b-long:,c-long::,'  -n 'example.bash' -- "$@")
                       ^^ -------- only changes -------- ^

但在匆忙修改脚本时,我们忘记更新 case 语句。

现在我们用一个 -d 的输入来测试脚本:

$ ./getopt-example.bash -d 'some data'
Internal error!

回顾脚本的编码工作最终应该会让我们意识到,我们忘记更新 case 命令以处理新的 -d 选项。

净结果:

  • 脚本作者添加了一个兜底测试,以在两条命令未保持同步时中断 case 命令。
  • 假设两条命令保持同步,则不应能够生成 Internal error! 输出。

注:

  • 这个假设场景也表明为什么你应该在一个 getopt/case 构造中始终提供兜底测试;移除 *) echo 'Internal error!' >&2; exit 1 ;; 将导致 while/case 在处理 -d 时无限循环。
  • 作者本可以更进一步,显示出 case 命令在处理时遇到的问题(即,显示 $1 的内容)。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章