我有一个脚本,它在当前工作目录中查找文件,并判断文件是文件还是目录
for file in $(ls | sort) ; do
if [ -d $file ]
then
echo "$file is a directory"
fi
if [ -f $file ]
then
echo "$file is a file"
fi
done
一切正常。但是,当我编辑它以查看父目录中的文件时,我没有得到任何输出。我改变了这个
for file in $(ls | sort) ; do
更改为:
for file in $(ls .. | sort) ; do
我这里遗漏了什么吗?
答案1
建议
在 bash 中列出文件和目录时,使用 Shell 通配符查找文件名是最有效、最可靠的方法……几乎所有其他外部文件列表工具都容易出错,例如单词拆分在处理其输出时在 bash 中并且它们需要在脚本中实现特殊的处理/输出格式和错误处理机制才能可靠地与它们一起工作在 bash 中... 默认情况下,它们的输出可能未被格式化为可在 bash 中可靠处理。
所以, …
要处理当前目录中的文件和目录,请使用:
for f in *; do
if [ -d "$f" ]; then
echo "$f is a directory"
elif [ -f "$f" ]; then
echo "$f is a file"
fi
done
注意双引号("
)围绕参数$f
必须在循环体内使用(即两个命令结构之间do … done
) 但一定不在循环头部使用(IEfor f in *;
)。
要仅对目录进行操作,请使用:
for f in */; do ...
要处理父目录内容,请使用:
for f in ../*; do ...
或者限制仅在父目录中的目录,使用:
for f in ../*/; do ...
请注意,bash 中的 shell 文件名默认按字母和数字顺序排序。
请注意,的输出ls
也将默认排序,因此您不需要sort
像在示例中那样将其通过管道传输...ls
除非您知道后果,否则请不要以这种方式解析输出...请参阅为什么不是解析ls
(以及该做什么)?
示范
为了进一步了解差异,请看下面的演示:
创建以下四个文件:
touch 3_with_tab$'\t'file.txt; touch 1_with_newline$'\n'file.txt; touch '2_with_space file.txt'; touch 4_normal_file.txt
使用 shell 通配符列出它们...完美运行:
for f in *; do [ -f "$f" ] && echo "$f is a file"; done
1_with_newline
file.txt is a file
2_with_space file.txt is a file
3_with_tab file.txt is a file
4_normal_file.txt is a file
通过find
管道传输到while
循环......文件名包含换行符时失败:
find | while IFS= read -r f; do [ -f "$f" ] && echo "$f is a file"; done
./3_with_tab file.txt is a file
./4_normal_file.txt is a file
./2_with_space file.txt is a file
在循环find
中for
...文件名包含空格、制表符或换行符时失败:
for f in $(find); do [ -f "$f" ] && echo "$f is a file"; done
./4_normal_file.txt is a file
通过ls
管道传输到while
循环......文件名包含换行符时失败:
ls | while IFS= read -r f; do [ -f "$f" ] && echo "$f is a file"; done
2_with_space file.txt is a file
3_with_tab file.txt is a file
4_normal_file.txt is a file
在循环ls
中for
...文件名包含空格、制表符或换行符时失败:
for f in $(ls); do [ -f "$f" ] && echo "$f is a file"; done
4_normal_file.txt is a file
机制(find
例如)
但是,如果你必须使用外部文件列表工具,那么你需要注意格式化它的输出并将其作为 bash 的输入读取……例如find
,你最好的选择是使用find
的行动-print0
(NULL 分隔输出)通过管道传输到一个while
循环,利用read
使用-d
bash
的 ANSI-C 引用 $'\0'
像这样:
find -print0 | while IFS= read -r -d $'\0' f; do [ -f "$f" ] && echo "$f is a file"; done
./3_with_tab file.txt is a file
./4_normal_file.txt is a file
./1_with_newline
file.txt is a file
./2_with_space file.txt is a file
或者使用sort
的选项-z
(行分隔符为 NULL,而不是换行符)像这样:
find -print0 | sort -z | while IFS= read -r -d $'\0' f; do [ -f "$f" ] && echo "$f is a file"; done
./1_with_newline
file.txt is a file
./2_with_space file.txt is a file
./3_with_tab file.txt is a file
./4_normal_file.txt is a file
答案2
是的,你漏掉了一些东西。你列出了父目录中的文件,但是当你检查它是文件还是目录时,你仍然在当前目录中 - 你必须像这样修改它才能使用父目录:
for file in $(ls .. | sort) ; do
if [ -d "../$file" ]
then
echo "$file is a directory"
fi
if [ -f "../$file" ]
then
echo "$file is a file"
fi
done
然而,正如评论所表明的,这不一定是解决这个问题的最佳方法。