为什么for循环不在目录上执行

Nov*_*ser 3 bash for

在下面的脚本中,第一个for循环按预期执行,但不是第二个。我没有收到任何错误,脚本似乎只是挂起。

HOME=/root/mydir

DIR=$HOME/var
DIRWORK=$HOME/Local

for f in $(find $DIR -type f); do

    lsof -n $f | grep [a-z] > /dev/null

    if [ $? != 0 ]; then
    echo "hi"       
    fi
done


for i in $(find $DIRWORK -type d -name work); do
    echo "2"
done
Run Code Online (Sandbox Code Playgroud)

Ran*_*ein 5

您的脚本以危险的方式编码。

首先,我假设您正在使用 Bash shell,因为您将它标记为“/bash”和“/for”。

在我的回答中,我将引用这个很棒的Bash 指南,这可能是从中学习 Bash 的最佳来源。

1)切勿使用不带引号的任何一种命令替换。这里有一个主要问题:使用不带引号的扩展将输出拆分为参数。

具体来说, this$(find $DIRWORK -type d -name work)$(find $DIR -type f) 会进行分,因此如果find找到一个文件名中有空格的文件,即“文件名”,Bash的分词结果将传递2个参数供for命令迭代,即一个为“文件”一个是“名字”。在这种情况下,您希望获得“文件:没有这样的文件或目录”和“名称:没有这样的文件或目录”,而不是在它们确实存在时可能对它们造成损害。

2)按照惯例,环境变量 (PATH, EDITOR, SHELL, ...) 和内部 shell 变量 (BASH_VERSION, RANDOM, ...) 完全大写。所有其他变量名都应该小写。由于变量名区分大小写,因此该约定可避免意外覆盖环境变量和内部变量。

你的 $DIRWORK 目录打破了这个约定,它也没有被引用,因此如果我们让DIRWORK='/path/to/dir1 /path/to/dir2'find当 $DIRWORK 没有被引用时,将查看两个不同的目录。使用引号的主题在 Bash 中非常重要,因此您应该“双引号”每个扩展,以及可能包含特殊字符的任何内容,例如“$var”、“$@”、“${array[@” ]}", "$(命令)"。Bash 将“单引号”内的所有内容视为文字。了解 ' 和 " 和 ` 之间的区别。请参阅Quotes , Arguments,您可能还想查看此链接:http : //wiki.bash-hackers.org/syntax/words

这是您脚本的更安全版本,我建议您改用它:

my_home="/root/mydir"

my_dir="$my_home/var"
dir_work="$my_home/Local"

while IFS= read -r -d '' f; do
    # I'm guessing that you also want to ignore stderr;
    # this is where the 2>&1 came from.
    if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
        echo "hey, I'm safer now!"
    fi
done < <(find "$dir_work" -type f -print0)


while IFS= read -r -d '' f; do
    echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
Run Code Online (Sandbox Code Playgroud)

如您所见,该IFS变量设置为 emtpy,从而防止read修剪一行的前导和尾随空格。该read命令使用空字符串 ( -d '') 作为分隔符,读取直到到达 \0。 find需要相应地修改,因此它使用-print0选项用 \0 而不是新行来分隔其数据 - 令人惊讶的是,这可能是文件名的一部分。用 \n 将这样的文件分成两部分会破坏我们的代码。

如果您不完全理解我的脚本,您可能想阅读有关进程替换的内容。

之前的答案指出find ... | while read name; do ...; done应该用于读取finds 输出也可能很糟糕。while上面的循环在一个新的子 shell 中执行,它自己的变量副本是从父级复制的。然后,此副本可用于您喜欢的任何内容。当while循环结束时,子shell副本被丢弃,父级的原始变量没有改变。

如果您的目标是修改此while循环内的某些变量,然后在父级中使用它们,请考虑使用上面更安全的脚本,以防止数据丢失。