我正在编写一个脚本,需要比较两个目录(递归),并且仅写出文件(如果它们的大小或修改时间(YY-MM-DD HH:MM)不同或者文件仅存在于一个目录中)。
输出格式为:
<dir1>:<local-path> <size> <last-modify> <dir2>:<local-path> <size> <last-modify>
如果文件仅存在于一个目录中:
<dir1>:<local-path> <size> <last-modify>
或者
<dir2>:<local-path> <size> <last-modify>
到目前为止,我已设法使用以下方法获取指定格式的数据:
find dir1 -type f -exec stat -c '%n %s %y' {} \; | sed 's,^[^/]*/,,' | sed 's/\:[^:]*$//' | sort # > dir1.txt
find dir2 -type f -exec stat -c '%n %s %y' {} \; | sed 's,^[^/]*/,,' | sed 's/\:[^:]*$//' | sort # > dir2.txt
它为我提供了给定目录和子目录中文件的 2 个有序列表以及它们的大小和最后修改时间戳。
现在我需要以某种方式比较它们并将它们转换为上面指定的格式。我尝试使用 diff -y,但它逐行比较,但我需要同名同名。我也尝试了 comm,但不知道如何转换该输出格式。
有任何想法吗?
答案1
rsync
我想我会尝试在空运行模式(--dry-run
或)下使用一些东西-n
。
为了说明,给出:
$ tree -Ds Adir/ Bdir/
Adir/
├── [ 4096 Nov 19 9:36] sub1
│ ├── [ 35 Nov 19 9:35] common
│ └── [ 23 Nov 19 9:36] onlyA
├── [ 4096 Nov 19 9:41] sub2
│ ├── [ 35 Nov 19 9:35] common
│ ├── [ 44 Nov 19 9:44] newerA
│ ├── [ 44 Nov 19 9:37] olderA
│ └── [ 6 Nov 19 10:36] size
└── [ 4096 Nov 19 9:35] sub3
└── [ 35 Nov 19 9:35] common
Bdir/
├── [ 4096 Nov 19 9:46] sub1
│ └── [ 35 Nov 19 9:35] common
├── [ 4096 Nov 19 10:36] sub2
│ ├── [ 35 Nov 19 9:35] common
│ ├── [ 44 Nov 19 9:38] newerA
│ ├── [ 44 Nov 19 9:44] olderA
│ └── [ 24 Nov 19 10:36] size
└── [ 4096 Nov 19 9:40] sub3
├── [ 35 Nov 19 9:35] common
└── [ 23 Nov 19 9:40] onlyB
6 directories, 14 files
然后我们可以列出具有不同大小或修改时间的文件,如下所示:
$ rsync -aOn --delete --itemize-changes Adir/ Bdir/
*deleting sub3/onlyB
>f+++++++++ sub1/onlyA
>f..t...... sub2/newerA
>f..t...... sub2/olderA
>f.s....... sub2/size
[更改字符串对于我们的目的来说实际上并不重要,但例如*deleting
表示sub3/onlyB
在源目录中不存在;s
表示大小差异;t
表示修改时间的差异。]
不幸的是,似乎无法直接从 rsync 输出中获取实际的时间戳,但我们可以简单地读取文件列表并统计每个目录中的相应文件:
#!/bin/bash
dirA="$1"
dirB="$2"
rsync -aOn --itemize-changes --delete "$dirA"/ "$dirB"/ | while read -r c f ; do
printf '%s:%s ' "$dirA" "$(cd "$dirA" && stat -c '%n %s %y' "$f" 2>/dev/null || printf '(none) - - - -')"
printf '%s:%s\n' "$dirB" "$(cd "$dirB" && stat -c '%n %s %y' "$f" 2>/dev/null || printf '(none) - - - -')"
done
我们可以按如下方式使用
$ ./rstat.sh Adir Bdir | column -t
Adir:(none) - - - - Bdir:sub3/onlyB 23 2016-11-19 09:40:12.253318393 -0500
Adir:sub1/onlyA 23 2016-11-19 09:36:52.220421434 -0500 Bdir:(none) - - - -
Adir:sub2/newerA 44 2016-11-19 09:44:45.953236221 -0500 Bdir:sub2/newerA 44 2016-11-19 09:38:33.270838033 -0500
Adir:sub2/olderA 44 2016-11-19 09:37:41.675642039 -0500 Bdir:sub2/olderA 44 2016-11-19 09:44:45.953236221 -0500
Adir:sub2/size 6 2016-11-19 10:36:31.460487036 -0500 Bdir:sub2/size 24 2016-11-19 10:36:31.460487036 -0500
答案2
我想你已经结束了,下面是:
如果文件仅存在于目录 1 中(考虑名称、大小或修改时间的任何差异:
grep -Fxvf dir2.txt dir1.txt > inDir1Only
或者如果文件仅存在于目录 2 中:
grep -Fxvf dir1.txt dir2.txt > inDir2Only
因此,对于您的问题“仅写出大小或修改时间(YY-MM-DD HH:MM)不同的文件”,只需将上述两个结果连接起来,如下所示:)
假设仅限目录1和仅限 inDir2内容如下:
$ cat inDir1Only c.txt 26 2016-11-04 14:23 b.txt 26 2016-11-04 14:23
$ cat inDir2Only b.txt 57 2016-11-04 18:20 a.txt 14 2016-11-04 18:11
awk
因为执行以下命令后你想要的输出如下所示,
$ awk 'NR==FNR{seen[$1]=$0;next} {
print "inDir1Only:"$0, ($1 in seen) ?"inDir2Only:"seen[$1]:"";seen[$1]=""}
END{
for(x in seen) if (seen[x]!=NULL) print "inDir2Only:"seen[x]
}' inDir2Only inDir1Only
inDir1Only:c.txt 26 2016-11-04 14:23
inDir1Only:b.txt 26 2016-11-04 14:23 inDir2Only:b.txt 57 2016-11-04 18:20
inDir2Only:a.txt 14 2016-11-04 18:11