即使 $1 不是 1,为什么总是选择 'if [ $1="1" ]' 分支?

Zen*_*Zen 10 shell bash test

我有一个名为“teleport.sh”的 shell 脚本,如下所示:

if [ $1="1" ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ $1="2" ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ $1="3" ];
    then
    shift
        mv "$@" ~/lab/Earth
fi
Run Code Online (Sandbox Code Playgroud)

当我执行:

sh teleport.sh 2 testfile
Run Code Online (Sandbox Code Playgroud)

testfile被移动到~/lab/Sun目录中,这让我很困惑,因为我没有将 1 或 '1' 传递给该脚本。

这里有什么问题?

Kar*_*rlo 19

使用空格可以解决您的问题。

if [ "$1" = 1 ];
    then
    shift
        mv "$@" ~/lab/Sun
elif [ "$1" = 2 ];
    then
    shift
        mv "$@" ~/lab/Moon
elif [ "$1" = 3 ];
    then
    shift
        mv "$@" ~/lab/Earth
fi
Run Code Online (Sandbox Code Playgroud)

虽然这更整洁:

#!/bin/bash

action=$1
shift
files=("$@")
case $action in  
  1) mv -- "${files[@]}" ~/lab/Sun     ;;
  2) mv -- "${files[@]}" ~/lab/Moon    ;;
  3) mv -- "${files[@]}" ~/lab/Earth   ;;
esac
Run Code Online (Sandbox Code Playgroud)

  • @echristopherson 如果没有空格,`test` 只会看到一个参数(一个“左值”)。没有其他参数(“运算符”和“r 值”),就没有什么可测试的,所以`test` 只是说“好吧,是的,你给了我一些东西,这肯定是空洞的”。 (6认同)
  • 是的,空格是必要的,但我想知道为什么原始的 `$1="1"` 语法“有效”(产生真实的结果)。`[`/`test` 内建函数实际上如何解释该表达式? (3认同)
  • `$1="1"` 是由 `=1` 连接的 `$1` (2认同)

kon*_*box 11

首先显而易见的是,您应该在[,test或的参数之间提供空格[[

if [ "$1" = 1 ];
Run Code Online (Sandbox Code Playgroud)

在 Bash 中,[[ ]]建议使用 using ,因为它不会为条件表达式做不必要的事情,例如分词和路径名扩展。也不需要引用双引号。==还可以使用更易读的运算符。

if [[ $1 == 1 ]];
Run Code Online (Sandbox Code Playgroud)

补充说明:如果第二操作数还包含变量,报价是必要的,因为它可能会受到图案匹配,如果它包含可识别的字符,例如*?[]等。如果扩展通配符或模式匹配与启用shopt -s extglob,其它形式的喜欢@()!()等也将被识别为模式。请参阅模式匹配

对于像<and>这样的运算符,它可能仍然是必要的,因为我曾经遇到过不引用第二个参数会导致不同结果的错误。

至于第一个操作数,什么都不适用。

还要考虑这个更简单的变体:

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac
Run Code Online (Sandbox Code Playgroud)

或浓缩:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac
Run Code Online (Sandbox Code Playgroud)

"${@:2}"是子串扩展或数组成员扩展的一种形式,其中2是偏移量。这使得扩展从第二个值开始。有了这个,我们可能不需要使用shift.

添加的--防止mv将以破折号 ( -)开头的文件名识别为无效选项。

  • @Archemar:不,没有失败(与许多其他语言相反)。 (2认同)
  • @Mat,如果您使用 `;&amp;` 而不是 `;;`(仅限 ksh、bash、zsh),则会导致失败。但是然后 `break` 仍然不能阻止失败,`break` 只是为了跳出循环。 (2认同)

tha*_*guy 7

要回答为什么会发生的问题,这种行为[也称为test在POSIX文件

在下面的列表中,$1、$2、$3 和 $4 代表提交给测试的参数:

[...]

1 论点:

如果 $1 不为空,则退出真 (0);否则,退出假。

你传递给它 1 个参数,2=1,它不为空,因此test成功退出。

正如其他帖子(和shellcheck)指出的那样,如果您想比较相等性,则必须传递 3 个参数2,=1

  • 从某种意义上说,这是回答所问问题的唯一答案(而不是给出一个或多个执行 OP 想要完成的工作的食谱),并且 shell 可以足够神秘,知道*为什么*它会做它所做的事情很有用。 (3认同)