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

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

我有一个名为“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

当我执行时:

sh teleport.sh 2 testfile

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

这是怎么回事?

答案1

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

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

虽然这更整洁:

#!/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

答案2

[第一个明显的事情是您应该在,test或 的参数之间提供空格[[

if [ "$1" = 1 ];

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

if [[ $1 == 1 ]];

*添加注释:如果第二个操作数也包含变量,则需要引用,因为如果它包含可识别的字符(如、?、等),则可能会受到模式匹配的影响。如果使用、 其他形式(如、等)[]启用扩展通配符或模式匹配。也将被识别为模式。看shopt -s extglob@()!()模式匹配

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

至于第一个操作数,没有任何适用。

也考虑这个更简单的变体:

case "$1" in
1)
    mv -- "${@:2}" ~/lab/Sun
    ;;
2)
    mv -- "${@:2}" ~/lab/Moon
    ;;
3)
    mv -- "${@:2}" ~/lab/Earth
    ;;
esac

或者浓缩:

case "$1" in
1) mv -- "${@:2}" ~/lab/Sun ;;
2) mv -- "${@:2}" ~/lab/Moon ;;
3) mv -- "${@:2}" ~/lab/Earth ;;
esac

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

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

答案3

为了回答为什么会发生这种情况的问题,[又名的这种行为test记录在 POSIX 中:

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

[...]

1个参数:

如果 $1 不为 null,则退出 true (0);否则,退出 false。

您向它传递了 1 个参数,2=1该参数不为空,因此test成功退出。

正如其他帖子(以及外壳检查)指出,如果您想比较是否相等,则必须传递 3 个参数2,=1

答案4

我只是想推荐一个便携的但也是更简洁的选择。重击是不是通用(如果你不需要通用,为什么要编写 shell 脚本?)

#! /bin/sh
action="$1"
shift
case "$action" in
    1) dest=Sun   ;;
    2) dest=Moon  ;;
    3) dest=Earth ;;
    *) echo "Unrecognized action code '$action' (must be 1, 2, or 3)" >&2; exit 1 ;;
esac
mv -- "$@" ~/lab/"$dest"

$action(学究们注意:是的,我知道行中的引号case "$action" in是不必要的,但我觉得最好还是把它们放在那里,这样未来的读者就不必记住这一点。)

相关内容