Unix shell脚本,参数带空格

Unix shell脚本,参数带空格

我需要制作一个名为 的 shell 脚本chExt.sh。接受文件扩展名,然后接受多个文件。然后需要更改文件扩展名或声明该文件不存在。

./chExt.sh txt ocelot.cpp ../otherFolder/file.H cat.dog.TXT king cobra.dat是自动测试仪测试的总体思路。问题是我完全不知道如何处理名称中带有空格的文件。

我的代码是:

newExt="$1"
shift
for x in $*
do
    fileName="$x"
    if test -f "$fileName";
    then
        name=$(echo "$fileName" | rev | cut -f 2- -d '.' | rev)
        newName="$name.$newExt"
        if test "$newName" != "$fileName";
        then
            mv "$x" "$newName"
        fi
    else
        echo "$fileName: No such file"
    fi
done

答案1

如果不引用文件名,shell 无法知道这foo bar.txt是一个参数而不是两个(foobar.txt)。因此,您需要像这样调用您的脚本:

./chExt.sh txt ocelot.cpp ../otherFolder/file.H cat.dog.TXT "king cobra.dat"

下一个问题是当变量扩展时,结果值在空白处分割(或者$IFS变量设置的任何值)。因此,当您编写 时for i in $*$*会在空格上展开并拆分。因此,对于一次迭代和下一次迭代来说,$i变得如此。解决方法是引用要扩展的变量,以便它不会被拆分。kingcobra.txt

这给我们带来了下一个问题,即$*和之间的区别$@。如果使用"$*",参数将全部被视为一个长字符串:

$ cat foo.sh
#!/bin/sh
for i in "$*"; do
    echo "$i"
done
$ foo.sh foo "bar baz"
foo bar baz

将以上内容与以下内容进行比较:

#!/bin/sh
for i in "$@"; do
    echo "$i"
done
$ foo.sh foo "bar baz"
foo
bar baz

如您所见,使用"$@"产生了预期的效果,每个输入参数都被单独处理,包括带空格的参数。

最后一点,您的脚本也会因以 开头的文件名-以及与 选项对应的字母而阻塞(例如,echo尝试使用名为 的文件)。-new剥离扩展的更好方法是使用 shell 的本机字符串操作功能。从变量末尾${var%.*}删除与 a 和 0 个或多个字符匹配的最短字符串。.换句话说,就是扩展。然后,您还需要添加--tomv来指示选项的结束和参数的开始,以便它也可以处理以以下开头的文件名-(请参阅指南 10这里)。

因此,脚本的改进和工作版本将是:

#!/bin/sh
newExt="$1"
shift
for fileName in "$@"
do
    if test -f "$fileName";
    then
    #    name=$(echo "$fileName" | rev | cut -f 2- -d '.' | rev)
    name=${fileName%.*}
        newName="$name.$newExt"
        if test "$newName" != "$fileName";
        then
            mv -- "$fileName" "$newName"
        fi
    else
        printf '%s: No such file\n' " $fileName"
    fi
done

进一步阅读:

答案2

"$*"和的另一个值得注意的特性"$@"是它们代表 shell 数组 - shell 参数的当前集合。这些可以通过set内置函数进行更改,但更普遍的是任何内置数组函数的默认操作数组 - 包括数组for

: "${2?no file arguments!}"
for x 
do     if    [ "${2+:}" ]
       then  set ".$1"
       else  [ ".${x##*.}" != "$1" ] &&
             [ -f "$x" ] &&
             mv -- "$x" "${x%.*}$1"
       fi
done

这与您的脚本的作用非常接近。它有点不同,因为它不会echo对不是常规文件的参数执行操作。不过,我不确定你正在做你认为你在那里的事情:你正在检查你的论点是否是现有的、可访问的、常规文件,但是你正在echo向 stdout 发送一条消息,上面写着参数:没有这样的文件。在类 Unix 系统上,一切都是文件:管道是文件,目录是文件,设备是文件。所以你的测试和你的信息不一致。更重要的是,如果您要求mv移动一个不存在的文件,它会为您打印您的消息,并打印到 stderr - 全部自行完成。

我注意到的另一件事是,您正在测试您的新名称是否等于您的旧名称,我认为这是为了避免mv抱怨将文件移动到自身之上。碰巧的是,mv有一个选项可以让这种情况安静下来。

我想我会做你的事情,比如:

: "${2?no file arguments!}"
for x 
do     [ "${2+:}" ] &&
       set -- ".$1" && continue
       set -- "${x%.*}$1" "$1"
{      [  -e "$1" ] && printf "%s: exists!\n" "$1"; } ||
{    ! [  -f "$x" ] && printf "%s: not a regular file.\n" "$x"; } ||
       mv -f --    "$x" "$1"
       shift
done   >&2

相关内容