我有一个名为“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
是不必要的,但我觉得最好还是把它们放在那里,这样未来的读者就不必记住这一点。)