通过循环遍历文件夹时
for f in $path
我遇到了意外的行为,即即使没有文件与通配符匹配,仍然会进入循环。我正在使用 cygwin,这是我的代码
#!/bin/sh
here=$(pwd)
release(){
release="$1"
targetdisk="$2"
package="$3"
searchPath="../$package/${package}Setup/*/*/*.msi"
echo $searchPath
ls $searchPath
for f in $searchPath; do
mode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:')
version=$(echo $file | sed -e 's:.*/\(.*\)/.*/*.\.msi:\1:')
PREVIOUSIFS=$IFS
IFS=#
targetPath="$targetdisk:\\Software and tools\\Install\\$release"
newFile="$targetPath\\$package-$mode-$release.exe"
if [ ! -d "$targetPath" ]; then
mkdir -p "$targetPath"
fi
echo $newFile
echo $f
#cp $file $newFile
IFS=$PREVIOUSIFS
done;
}
release $1 $2 MyStuff
我故意放了 MyStuff,它不存在:
$ ./release.sh 1.0-dev G
../MyStuff/MyStuffSetup/*/*/*.msi
ls: ../MyStuff/MyStuffSetup/*/*/*.msi: No such file or directory
G:\Software and tools\Install\1.0-dev\MyStuff--1.0-dev.exe
../MyStuff/MyStuffSetup/*/*/*.msi
然而,正如您所看到的,两个回显都被执行了。为什么即使没有匹配也会发生这种情况?
答案1
与任何文件都不匹配的通配符模式将不展开。目的是允许在命令中键入包含通配符但不属于模式的参数。这在命令行上有一定的意义,它允许偶尔保存两个引号字符。在脚本中,这是非常烦人的。不幸的是,没有办法改变传统 sh 中的这种行为。您可以检查图案是否未展开。
for f in "../$package/${package}Setup"/*/*/*.msi; do
if [ "$f" = "../$package/${package}Setup"/*/*/*.msi" ]; then
# No match
break
fi
…
done
可以指示 Bash 将不匹配的通配符模式扩展为空列表。将您的 shebang 行更改为#!/bin/bash
,您可以执行以下操作:
shopt -s nullglob
for f in "../$package/${package}Setup"/*/*/*.msi; do
…
您的脚本中还存在其他问题。searchPath
如果 中存在空格或通配符(包括反斜杠),则使用变量的方式将会中断$package
。不要将$searchPath
变量替换保留为不带引号的。始终在变量替换周围使用双引号,除非您希望将它们视为以空格分隔的模式列表,但这里的情况并非如此 - $package
是一个字符串(需要双引号),而明显*
使该批次成为通配符模式。将模式直接放入for
循环中,如上所述。
对于日志记录,不要使用这些echo
命令(变量替换周围缺少双引号,因此参数将被破坏),而是使用set -x
在执行时在 stderr 上显示每个命令。
在脚本中的某个时刻,您正在设置IFS=#
,但您从未对 进行任何拆分#
。IFS
如果您不打算使用它,请不要设置。
有几个问题mode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:')
。如上所述,它缺少双引号"$file"
。此外,为每个文件调用额外的处理速度sed
很慢(在 Cygwin 上执行外部进程很慢)并且过于复杂。 shell 具有字符串处理结构;虽然它们很原始,但在这里就足够了。你的
dir="${file%/*}"
mode="${dir##*/}"
dir="${dir%/*}"
version="${dir##*/}"
答案2
该脚本有很多问题,但您所询问的行为(为什么循环执行)是这样的:
在外壳构造中
for f in <expr>; do <commands>; done
文件模式通常用于“expr”,然后将其替换为 shell 文件名通配符,这就是您所做的。但是您的表达式没有匹配任何内容,因此通配符不会将其扩展为文件路径列表。但它也不是像许多其他语言那样的零长度(空)列表,而是带有您在循环上方回显的星号的路径表达式。因此循环执行一次,并将 $f 设置为等于以 *.msi 结尾的未展开字符串。您可以在循环之前回显它的位置看到这一点。我们知道这不匹配任何内容,因为您也将它与 ls 一起使用,这就是您收到“没有这样的文件或目录”错误的原因。
(另请注意,您在循环中使用 $file,我认为您的意思是在那里使用 $f 。