在下面的脚本中,第一个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)
您的脚本以危险的方式编码。
首先,我假设您正在使用 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
应该用于读取find
s 输出也可能很糟糕。while
上面的循环在一个新的子 shell 中执行,它自己的变量副本是从父级复制的。然后,此副本可用于您喜欢的任何内容。当while
循环结束时,子shell副本被丢弃,父级的原始变量没有改变。
如果您的目标是修改此while
循环内的某些变量,然后在父级中使用它们,请考虑使用上面更安全的脚本,以防止数据丢失。