我想编写一个简单的脚本来检测 Windows 病毒创建的文件。通常它会创建一个与其放置的目录同名的 .exe 文件。
这是脚本。只有当路径名不包含 \n 时,它才有效。有人能帮我修复这个脚本吗?
#!/bin/bash
if [ $# == 0 ]; then
echo ""
echo "==== Give me a directory to begin with! ===="
echo ""
exit
fi
for f in `find $1 -name '*.exe'` ; do
filename=`basename "$f" .exe`
dir_name=`dirname "$f"`
current_dir_name=`basename "$dir_name"`
if [ $filename == $current_dir_name ]; then
rm -f "$f" # It can't remove files
# where the path contains spaces or \n ??!!
fi
done
答案1
Bash 有一个特殊的变量,称为 IFS。
bash 使用它来了解如何将字符串拆分为文件列表。(内部字段分隔符)
默认情况下,它包含空格、制表符和换行符。
for f in `find ...
将根据 $IFS 变量中包含的字符进行拆分。如果 find 命令返回的文件名中有换行符或空格,则会将其视为不同的文件名,因此将对名称的每个部分执行循环。
$ ls -l -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 my?bad.exe -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 space bad.exe $ for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; 完成 迭代(./空格) 迭代(bad.exe) 迭代(./my) 迭代(bad.exe)
我们可以调整 $IFS 变量,但这很棘手。让我们看看为什么:
(在脚本中测试此代码,以便 IFS 变量的更改不会保留在您的 shell 中,您可能会感到困惑)
$ IFS=""; for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; 完成 迭代(./space bad.exe 。/我的 坏.exe
如您所见,现在 bash 没有任何规则来拆分 find 命令返回的字符串,并将整个输出视为单个值。
显然这里的问题是,如果我们使用,for f in `find....
我们就无法区分文件中包含的换行符和 find 命令生成的换行符。
现在,为了完整起见,我将向您展示如何使您的方法发挥作用,但请在答案的末尾看到一个更简单的解决方案。
我们可以使用其他分隔符并解决您当前的问题:
$ for f in $(find -name "*.exe" -exec echo -n {}:\;); do echo "ITERATING ($f)"; 完成; 迭代(./space bad.exe) 迭代(./my 坏.exe 迭代()
此处的find -name "*.exe" -exec echo -n {}: \;
命令返回以“:”分隔的文件列表(这在 Windows 文件名中是不合法的)。
现在下一步是实际删除这些文件。考虑到最后一个文件以“:”结尾,因此 bash 使用空字符串进行最后一次迭代。
IFS=":"; 对于 $(find -name "*.exe" -exec echo -n {}:\;) 中的 f;如果 [ !-z "$f" ],则执行;然后 rm "$f";fi;完成;
您应该能够在此循环内执行 basename/dirname 测试,就像以前一样。
但是,这个解决方案很丑陋,并且只有当我们有一个可以依赖的角色时才有效。
事实证明,find 命令本身可以对它找到的每个文件执行命令。
查找 -name "*.exe" -exec ./yourscript.sh {} \;
然后 yourscript.sh 将会在 $1 中获取全名,并且您可以删除它,rm "$1"
即使它包含换行符和空格。
find 命令还可用于在脚本内部执行内联的 basename/dirname 测试,但它是更高级的主题。
答案2
您实际上可以使用( *.exe )
来解决这个问题bash
。
(
像这样)
在模式周围加上括号意味着它将被扩展并存储在数组中。
然后,您可以使用如下方法迭代数组中的每个项目:
exes=( *.exe )
for exe in "${exes[@]}"; do
echo "'$exe'"
done
这是一个完整的例子:
$ mkdir 'dir
> with
> spaces'
$ touch 'dir
> with
> spaces/dir
> with
> spaces'
$ touch 'dir
> with
> spaces/other
> file'
$ find .
.
./dir?with?spaces
./dir?with?spaces/dir?with?spaces
./dir?with?spaces/other?file
$ cd 'dir
> with
> spaces'
$ files=( * )
$ for file in "${files[@]}"; do
> echo "'$file'"
> done
'dir
with
spaces'
'other
file'
只有在您明确目录结构后,我才能提供完整的解决方案,因为我不确定current_dir_name
它是否按照您的想法执行。
答案3
这将处理包含文件名中允许的任何字符的文件名,包括换行符。
#!/bin/bash
if (($# != 1)); then
echo >&2 "Usage: $0 directory"
exit 1
fi
while IFS= read -r -d '' file; do
base=${file##*/} dir=${file%/*/*}
if [[ $dir/${base%.exe}/$base -ef $file ]]; then
echo rm "$file"
fi
done < <(find "$1" -type f -name "*.exe" -print0)
看http://mywiki.wooledge.org/BashFAQ/020和http://mywiki.wooledge.org/BashFAQ/100以了解更多详情。