Bash 脚本的一个问题

Bash 脚本的一个问题

我想编写一个简单的脚本来检测 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/020http://mywiki.wooledge.org/BashFAQ/100以了解更多详情。

相关内容