Case语句没有捕获空字符串

Rei*_*ica 1 bash shell switch-statement

我想检测是否传递了无参数或无效参数并打印帮助消息。一个空的参数单独检查可能的,但不是那么优雅。

我的 bash 脚本如下所示:

COMMAND="$1"
shift
case "$COMMAND" in     
        loop)
            loop_
            ;;  
        ...            
        *)
            echo $"Usage: $0 {loop|...}"
            exit 1 
esac
Run Code Online (Sandbox Code Playgroud)

当没有参数传递时,什么都不执行;如果我通过“”,则会触发正确的情况。如果我$1直接使用而不是使用临时变量,那么它会按预期工作。

我什至尝试添加一个特定的案例,"")但无济于事。

Cha*_*ffy 6

您的case语句与 no $1given不匹配的唯一方法是首先没有输入它。

考虑以下:

#!/usr/bin/env bash
set -e

command=$1
shift
case $command in
  *) echo "Default case was entered";;
esac
Run Code Online (Sandbox Code Playgroud)

这在$1未设置时不发出任何输出——但不是因为case语句有任何问题。

相反,问题在于当没有可用的内容时shift以非零退出状态退出shift,并且set -e导致整个脚本在该失败时退出。


这个故事的第一个寓意:不要使用set -e(或#!/bin/bash -e

请参阅BashFAQ #105进行扩展讨论——或者如果赶时间的话,可以查看其中包含练习。在不同的“符合 POSIX 标准”的 shell 之间set -e 完全不兼容,因此使得行为难以预测。手动错误处理可能并不有趣,但它更可靠。


第二:考虑一个使用函数

这为您提供了一种将使用信息放在一个地方的简洁方法,并在必要时重新使用它(例如,如果您没有$1to shift):

#!/usr/bin/env bash

usage() { echo "Usage: $0 {loop|...}" >&2; exit 1; }

command=$1
shift || usage
case $command in
  *) usage ;;
esac
Run Code Online (Sandbox Code Playgroud)

由于中|| usage,退出状态shift被认为是“检查”,所以即使你使用运行脚本set -e,它将不再构成致命的错误。


或者,shift显式标记为已检查

相似地:

shift ||:
Run Code Online (Sandbox Code Playgroud)

... will run shift,但然后回退到 running :( 的同义词true,历史上/传统上暗示占位符使用)应该shift失败,同样阻止set -e触发。


旁白:为您自己的变量使用小写名称

POSIX 指定shell(以及其他标准适用的工具)的行为仅由具有全大写名称的环境变量修改:

POSIX.1-2017 的 Shell 和 Utilities 卷中的实用程序使用的环境变量名称仅由大写字母、数字和可移植字符集中定义的字符中的 ( '_' ) 组成,并且不以数字开头。实现可能允许其他字符;应用程序应容忍此类名称的存在。大写和小写字母应保留其唯一标识,不得折叠在一起。包含小写字母的环境变量名称的命名空间是为应用程序保留的。应用程序可以使用该命名空间中的名称定义任何环境变量,而无需修改标准实用程序的行为。

这甚至适用于常规的、非export编辑的 shell 变量,因为指定一个与环境变量同名的 shell 变量会覆盖后者。

BASH_COMMAND例如,在 bash 中具有独特的含义——因此可以在脚本的前面设置为非空值。COMMAND对于符合 POSIX 的 shell 解释器来说,同样有意义并且已经被它们使用,没有什么可以阻止的。

如果您想避免 shell 使用脚本使用的名称设置内置变量的情况,或者脚本意外覆盖对 shell 有意义的变量的情况下的副作用,请在编写脚本时坚持使用小写或大小写混合的名称用于符合 POSIX 的 shell。