bash防止包含文件夹的副本

时间:2014-10-13 11:22:36

标签: bash

以下脚本循环遍历文件列表,并将文件副本复制到文件夹" src"位于与脚本相同的文件夹中。

其中一个文件/文件夹列表是包含脚本的文件夹。

如何防止它以递归方式将src文件夹复制到自身:

#!/bin/bash
FILES=files.txt
if [ -d src ]; then
    mkdir -p src
fi
rm -rf src/*
while read FILE; do
    DIR="src$(dirname $FILE)"
    NAME=$(basename $FILE) 
    if [ ! -d "$DIR" ]; then
        echo "Create $DIR"
        mkdir -p $DIR
    fi

    if [ -d "$FILE" ]; then
        echo Copying "FOLDER $FILE > $DIR/$NAME"
        cp -rp "$FILE" "$DIR/$NAME"
    elif [ -f "$FILE" ]; then
        echo Copying "FILE $FILE > $DIR/$NAME"
        cp -p "$FILE" "$DIR/$NAME"
    fi
done <$FILES

希望清楚,听起来有点令人困惑,但我不确定如何用它来表达它!

修改

我的最终目标实际上是将磁盘中的特定文件和文件夹收集到src文件夹中,然后将这些源文件推送到git repo。

1 个答案:

答案 0 :(得分:1)

您的脚本存在一些问题。首先,您要求修复它,而不更改任何其他问题:

添加一项检查,看$FILE是否等同于$PWD/src。如果是,只需continue循环,如

# avoid recursively copying src into itself
if [ "$FILE" == "$PWD/src" ];
then
    continue
fi

但是,这假定files.txt中的路径是绝对路径。 如果路径是相对的,则它们必须在其列出的名称下可见,以使dirnamebasenamecp起作用,这意味着它们是相对于当前工作目录的路径,我们可以将检查简化为"$FILE" == "src"

如果你有像src/../src这样的非规范化路径,那么支票就不行了。 我最喜欢在使用GNU实用程序的Linux上处理此问题的方法是使用readlink -f规范化完整路径,如

if [ "$(readlink -f "$FILE")" == "$(readlink -f "$PWD/src")" ];
then
    continue
fi

您可能想知道为什么我在readlink上使用$PWD,但如果您认为为了到达当前目录而遍历的其中一个目录是符号链接,则会有意义。在这种情况下,将$FILE的路径规范化的结果会错误地与$PWD不匹配。

如果您使用的是具有BSD实用程序的系统(如OSX计算机),则readlink不支持-f选项,事情开始变得棘手。 请记住,我们尝试处理的情况是files.txt中包含符号链接的路径,但实际上是指src。像../mysymlink/src这样的东西,其中mysymlink实际指向当前目录。

我知道的最简单的方法是检查src的inode编号并检查它是否符合符号链接时的文件:

# fetch the inode number for src, following symlinks
src_inode_num="$(stat -L --format=%i src)"
# fetch the inode number for FILE, following symlinks
file_inode_num="$(stat -L --format=%i "$FILE")"
# compare inode numbers to see if they are the same directory
if [ "$file_inode_num" == "$src_inode_num" ];
then
    continue
fi

我建议您查看stat联机帮助页,但简而言之-L表示stat跟踪符号链接而未提供stat信息,而--format=%i表示统计信息仅打印inode编号。

如果您有关于files.txt的更多信息,则可以使用其他解决方案,您可以使用grep或其他工具删除src的实例,甚至在处理文件之前


现在所有这些都得到了解决,您应该考虑可能出现在文件列表中的其他符号链接,因为它们会导致原始文件的重复。

简单的解决方案是

if [ -L "$FILE" ];
then
    ...
    cp -P "$FILE" "$DIR/$NAME"
fi

请注意,这不会阻止if [ -fif [ -d检查成功 - 符号链接也会通过这些检查,因此请务必在if [ -L通过时跳过它们。

当然,所有这些都容易受到src上面的同一组问题的影响,所以如果你想在路径中嵌入符号链接时避免重复,你应该做更多的工作。 同样,您可以使用readlink -f,或者,因为您要复制内容,您可以遍历路径(重复dirname正常工作)查找链接,并使用{{1}重新创建找到的链接}。


一些狡辩:

  • 无需在开头检查cp -P,因为if [ -d src即使存在也是安全的。我发现在没有mkdir -p src块的情况下更容易阅读,以及像if这样的简单评论。此外,您当前的检查是错误的 - 如果它已经存在,它只会创建# ensure src exists
  • 我更喜欢src,因为我们很清楚我们是连接路径。在shell中src/$(dirname "$FILE")相当于//,因此没有任何危害。如果它困扰你,你可以随时/,但这会忽略像sed 's://:/:g'这样的路径,所以我不会打扰。
  • 你在一堆地方使用a\//b,为什么不把它放在一个变量中呢?也许是像#34; target&#34;。
  • 这样的语义
  • Put&#34;复制&#34;在"$DIR/$NAME" s
  • 的引号内
  • 与开始时的echo一样,无需在src之前检查存在。
  • 对引用字符串更加警惕。像mkdir -p src
  • 这样的事情
  • "$(basename "$FILE")"实际上只是src/$(dirname "$FILE")/$(basename "$FILE"),为什么不使用它?即src/"$FILE",在这种情况下,我们可以省略对target="src/$FILE"
  • 的调用
  • 迫切需要评论。如果你为了这些而省略了这些,我认为最好不要这样做,因为它们向所有读者澄清 - 包括SO读者。

我在所有这些结尾处最终得到的问题是&#34;你想做什么?&#34; 您已经为我们提供了一个可以执行某项操作的脚本,并且可以轻松地在其上修补更多逻辑以完成您想要的操作,但也许您应该编辑问题以与我们分享您的实际目标。 也许在这里很好地使用basename,使用tar作为文件表输入,或者这可能通过聪明的files.txt命令解决。 我不想浪费时间根据脚本内容进行推测,但我看到这个问题时的第一直觉是你要创建一个备份 - 当然,在这种情况下,一个名为rsync的目录没有多大意义。