我正在使用 bash shell。我想查看两个目录中文件名的差异,但仅限于这些目录的直接子目录。例如,如果我有这些目录,其中子文件/目录名为 ...
dir1
-> abc
-> def
-> ghi
dir2
-> abc
-> fff
-> ghi
我希望能够运行一些命令,告诉我 dir2 有一个名为“fff”的文件/目录,而 dir1 没有,而“dir1”有一个名为“def”的子文件/目录,而 dir2 没有没有。我怎么做?
答案1
for name in dir1/* dir2/*; do
bname=${name##*/}
if [ ! -e "dir1/$bname" ]; then
printf '"%s" not in dir1\n' "$bname"
elif [ ! -e "dir2/$bname" ]; then
printf '"%s" not in dir2\n' "$bname"
fi
done
这将迭代dir1
和中的所有名称dir2
。$bname
将是名称的基本名称($name
不带路径)。
如果在 中找不到(基本)名称dir1
,则会报告此情况。否则,如果在dir2
报告中没有找到它。
这可以处理包含嵌入换行符的文件名,并且应该与/bin/sh
任何兼容的sh
shell 一起运行。
这可能会扩展到两个以上的目录。和bash
:
dirs=( dir1 dir2 dir3 dir4 )
for dir in "${dirs[@]}"; do
for name in "$dir"/*; do
bname=${name##*/}
for tdir in "${dirs[@]}"; do
if [ ! -e "$tdir/$bname" ]; then
printf '"%s" not found in %s\n' "$bname" "$tdir"
fi
done
done
done
$ tree
.
|-- dir1
| `-- filename 1
|-- dir2
| |-- filename 1
| |-- filename 3
| `-- hello
world
`-- script.sh
$ sh script.sh
"filename 3" not in dir1
"hello
world" not in dir1
答案2
为此,我非常喜欢comm -3
plainls
的输出:
$ comm -3 <(ls dir1) <(ls dir2)
def
fff
第一列中的条目仅在第一个目录中,第二列中的条目仅在第二个目录中;该-3
选项删除第三列,该列显示两者共有的条目。您还可以指定-1
仅查看 中存在dir2
但不存在的文件dir1
,以及-2
仅查看 中存在dir1
但不存在的文件dir2
。
为此,您需要确保没有设置任何ls
选项通过它的OPTIONS
配置或别名。您可以使用诸如以下的变体来避免此类问题
$ comm -z -3 <(cd dir1; printf "%s\0" *) <(cd dir2; printf "%s\0" *) | tr '\0' '\n'
def
fff
后者避免了文件名中特殊字符的问题。您可以将其与-1
或结合起来-2
,然后将tr
其放入xargs -0
其中以进一步处理任一组文件。