排除目录的bash命令 - "for file in $(find ...)"

4

我需要编写一个脚本,将主目录(home directory)中的所有*.txt文件复制到脚本的第一个参数所指定的新创建的目录中(${1})。如果备份目录已经存在,我希望跳过它。我尝试使用find命令中的-exclude选项进行排除,但这对我不起作用。最终我在循环中使用了if语句,但它也不起作用,我不知道为什么...谢谢你的帮助!!

以下是我的代码:

#!/bin/bash

mkdir ${1}

for file in $(find ~/ -name *.txt)

do

    if [ ! -f ~/${1}/$file ]
    then
        cp -i -v $file -t ~/${1}
    fi

done

"对我不起作用" ... 嗯,好的。怎么回事? - Ignacio Vazquez-Abrams
如果您不需要目录递归,可以使用cp *.txt destination/命令,也可以加上cp -n选项。另外还可以考虑使用rsync命令。 - tripleee
3个回答

1

这个应该可以:

#!/bin/bash

[[ -n "$1" ]] || { echo >&2 'Give me an argument!'; exit 1; }

destdir=$(readlink -m -- "$1")

[[ "$destdir" == *\** ]] && { echo >&2 "Sorry, I'm in the stupid situation where the destination dir contains the \`*' character. I can't handle this."; exit 1; }

mkdir -pv -- "$destdir" || { echo >&2 "Couldn't create directory \`$destdir'. Sorry."; exit 1; }

find "$HOME" -path "$destdir" -prune -o \( -name '*.txt' -exec cp -iv -t "$destdir" -- {} \; \)

优点:与文件名中包含空格或奇怪符号的文件一起工作(不像你的文件),但有一个愚蠢的情况除外,请参见下面的缺点

缺点:正如ormaaj在评论中指出的那样,如果您的目标路径名称包含模式字符*,则可能会失败。这种情况已经得到了安全考虑,如果发生这种情况,脚本会优雅地退出。

说明。

  • 给该脚本提供一个参数。它可以是绝对路径或相对于当前目录的路径。使用-m选项的readlink将负责将其转换为绝对路径:这就是变量destdir
  • 如果适用,则创建具有其父级的目录$destdir
  • 在主目录中,如果我们找到$destdir目录,则我们修剪此分支,否则,我们查找所有*.txt文件并将其复制到$destdir

再次强调,这个脚本对于文件名中带有奇怪符号的情况是100%安全的:包括空格、换行符或连字符,但目标目录名称中的通配符*除外。即使出现这种情况,脚本也会通过优雅的退出方式来确保文件不会被搞乱。


当destdir包含模式字符时,它是不安全的。通常,-path-prune不能混合使用。与通配符不同,-path不会单独评估每个路径组件。例如,/foo/*/bork匹配/foo/bar/bork/foo/bar/baz/bork - ormaaj
@ormaaj 没错!我忘记这个愚蠢的模式字符 * 了。那你为什么说 -path-prune 不兼容呢?在 man find 中有一个很好的例子可以说明它们如何一起使用。 - gniourf_gniourf
我看错了,把“-path”误读成了“-name”。不过这差不多。 - ormaaj

0

代码中存在错误,您正在创建目录:

mkdir ${1}

但是复制并测试不同的路径(-f ~/${1}/$file-t ~/${1})。创建一个变量来防止这样的错误:

#!/bin/bash
dest=~/${1}
[ -d "$dest" ] || mkdir "$dest"
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for file in $(find ~/ -name '*.txt' -print)
do
  dfile="$dest/"$(basename "$file")
  if [ "$file" -ef "$dfile" ]; then
    echo same
  else
    cp -i -v "$file" -t "$dfile"
  fi  
done
IFS=$SAVEIFS

一旦文件名中有空格,程序就会中断。此外,使用绝对/相对路径时有些笨拙。 - gniourf_gniourf
谢谢回复,但是cp仍然试图从$dest路径复制文件到$dest路径... - jirinovo

0

你可以简化为

mkdir -p "$1"
find ~/ -name "$1" -prune , -name '*.txt' -print0 | xargs -0 cp -n -v -t "$1"

你必须使用引号*.txt,否则shell会将其扩展为当前目录下的所有.txt文件,而不是在你的主目录下搜索所有.txt文件。 -name "$1" -prune 排除备份目录。

xargs 尽可能多地调用cp文件名,从而节省了大量时间。

cp -n 而不是 cp -i 防止覆盖已经存在的文件。如果你想保留它,当然可以将 -n 替换为 -i

如果你想保留目录层次结构,rsync 可能更适合这个备份任务。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接