1

我在一些 Bash 脚本(在 OSX 上)方面得到了一些帮助。我想创建一个带有两个参数的脚本 - 源文件夹和目标文件夹 - 并检查源层次结构中的所有文件以查看它们是否存在于目标层次结构中。即给定一张数据 DVD,检查其中包含的文件是否已经在内部驱动器上。

到目前为止我想出的是

#!/bin/bash

if [ $# -ne 2 ]
then
        echo "Usage is command sourcedir targetdir"
        exit 0
fi

source="$1"
target="$2"

for f in "$( find $source -type f -name '*' -print )"
do

我现在不确定如何最好地获取没有路径的文件名,然后查看它是否存在。我真的是脚本的初学者。

编辑:到目前为止给出的答案在紧凑代码方面都非常有效。但是,我需要能够在目标层次结构中的任何位置查找在整个源层次结构中找到的文件。如果找到,我想比较校验和和最后修改日期等并发表评论,或者,如果没有找到,我想注意这一点。目的是检查外部媒体上的文件是否已上传到文件服务器。

4

3 回答 3

1

这应该给你一些想法:

#!/bin/bash

DIR1="tmpa"
DIR2="tmpb"

function sorted_contents
{
    cd "$1"
    find . -type f | sort
}

DIR1_CONTENTS=$(sorted_contents "$DIR1")
DIR2_CONTENTS=$(sorted_contents "$DIR2")

diff -y  <(echo "$DIR1_CONTENTS") <(echo "$DIR2_CONTENTS")

在我的测试目录中,输出是:

[user@host so]$ ./dirdiff.sh
./address-book.dat ./address-book.dat
./passwords.txt ./passwords.txt
./some-song.mp3 <
./the-holy-grail.info ./the-holy-grail.info
                                             > ./victory.wav
./zzz.wad ./zzz.wad

如果不清楚,“some-song.mp3”仅在第一个目录中,而“victory.wav”仅在第二个目录中。其余文件很常见。

请注意,这只比较文件名,而不是内容。如果你喜欢它的发展方向,你可以使用这些diff选项(也许--suppress-common-lines如果你想要更清晰的输出)。

但这可能是我的处理方式——将大量工作转移到diff.

编辑:我还应该指出一些简单的事情:

[user@host so]$ diff tmpa tmpb

也可以:

    仅在 tmpa 中:some-song.mp3
    仅在 tmpb 中:victory.wav

...但感觉不如自己编写脚本那么令人满意。:-)

于 2012-08-31T23:04:17.700 回答
1

仅列出$source_dir其中不存在的文件$target_dir

 comm -23 <(cd "$source_dir" && find .|sort) <(cd "$target_dir" && find .|sort)

-f您可以使用find命令将其限制为常规文件。

comm命令(“common”的缩写)查找两个文本文件之间的共同行并输出三列:仅在第一个文件中的行,仅在第二个文件中的行,以及两者共有的行。数字抑制了相应的列,因此输出comm -23仅是第一个文件中未出现在第二个文件中的行。

进程替换语法<(command)被连接到给定命令输出的命名管道的路径名替换,这使您可以在任何可以放置文件名的地方使用“管道”,而不仅仅是标准输入和标准输出。

在这种情况下,命令会生成两个目录下的文件列表 -cd使输出相对于被比较的目录,以便相应的文件作为相同的字符串出现,并sort确保comm不会被列出的相同文件混淆两个文件夹中的顺序不同。

于 2012-08-31T23:44:18.073 回答
0

关于这条线的一些评论for f in "$( find $source -type f -name '*' -print )"

  • 做那个"$source"。始终在变量替换周围使用双引号。否则,结果将被拆分为被视为通配符模式的单词(shell 解析规则中的一个历史奇点);特别是,如果变量的值包含空格,这将失败。
  • 你不能迭代find这种方式的输出。由于双引号,循环中会有一次迭代,其中$f包含来自find. 如果没有双引号,包含空格和其他特殊字符的文件名会使脚本出错。
  • -name '*'是无操作的,它匹配所有内容。

据我了解,您想按名称查找文件,而与它们的位置无关,即您认为/dvd/path/to/somefile/internal-drive/different/path-to/somefile. 因此,在每边按名称索引的文件列表。你可以通过按摩一点输出来做到这find一点。下面的代码可以处理文件名中的任何字符,但换行符除外。

list_files () {
  find . -type f -print |
  sed 's:^\(.*\)/\(.*\)$:\2/\1/\2:' |
  sort
}
source_files="$(cd "$1" && list_files)"
dest_files="$(cd "$2" && list_files)"
join -t / -v 1 <(echo "$source_files") <(echo "$dest_files") |
sed 's:^[^/]*/::'

list_files函数生成带有路径的文件名列表,并在文件前面添加文件名,因此 eg/mnt/dvd/some/dir/filename.txt将显示为filename.txt/./some/dir/filename.txt. 然后它对文件进行排序。

join命令打印出行,例如在源层次结构中filename.txt/./some/dir/filename.txt调用文件filename.txt但在目标层次结构中没有调用的文件。我们终于稍微调整了它的输出,因为我们不再需要行首的文件名。

于 2012-09-01T01:16:42.850 回答