仅复制具有相似名称的文件

仅复制具有相似名称的文件

我的 bash-fu 无法胜任这项任务。你能帮我吗?我有一个文件夹,其中包含:

viper.jpg
marshmallow.jpg
spider.jpg
cockroach.jpg
muffin.jpg
taffy.jpg

以及包含以下内容的文件夹

viper.doc
spider.doc
cockroach.doc

我需要将文件名匹配的相关插图从文件夹 1 复制到文件夹 2。该怎么做?

答案1

非常简单bash 参数扩展

#!/bin/bash
cd folder2
for i in *; do
    cp "../folder1/${i%.*}.jpg" .
done

处理空格、换行符以及您输入的任何其他奇怪的字符。

此外,如果您要复制的文件类型不止 .jpg,则可以cp用以下命令替换上述命令(注意最后的命令*在引号之外):

    cp "../folder1/${i%.*}."* .

如果您有具有多个扩展名的文件(例如.tar.gz)并且想要将它们全部删除,请将 更改%%%

答案2

这是一个有趣的例子:

find ./f1 -type f -exec sh -c 'bn=$(basename -s ".jpg" "$1");test -f "$2"/"$bn".doc || exit 1' sh  {} ./f2 \; -and -exec echo cp {} ./f2 \;

或者使用缩进作为脚本:

#!/bin/bash

find "$1" -type f -exec sh -c '
        bn=$(basename -s ".jpg" "$1");
        test -f "$2"/"$bn".doc || exit 1' sh  {} "$2" \; \
    -and -exec echo cp {} "$2" \;

是不是有点困惑?其实很简单:

  • find将遍历变量给出的目录$1并仅查找类型 f 的项目,即“常规文件”。

  • 对于每个文件,我们有两个-exec调用,用标志连接,只有第一个调用成功时-and才会执行第二个调用,有点像在 shell 脚本中一样。-exec&&

  • 首先exec执行函数并提取文件的基本名称并剥离扩展名,这一切都是在使用标志调用.jpeg的实例中完成的。使用标志调用的 shell将第一个位置参数放入变量中,这就是第一个位置参数必须是的原因。之后是当前处理的文件,将是您要检查重复项的目录,即来自 OP 示例。/bin/sh-c-c$0sh{}"$2folder 2

  • bn=$(basename -s ".jpg" "$1")提取基文件并去除当前处理文件的扩展名,因此如果我们正在./f1/foo.jpg处理,结果将foo保存在bn变量中。test -f "$2"/"$bn".doc || exit 1将检查目标目录中是否存在相同的基名但带有 .doc 扩展名,如果不存在 - 将返回退出状态 1,这将阻止第二个-exec被运行find
  • 如果所有 shell 都以退出状态 0 正常退出,则表示 filename 存在于目标目录中,我们可以运行第二个,-exececho cp {} ./f2 \; 笔记:删除echo部分以进行实际复制,echo之前cp仅用于测试,以便您可以确保只复制需要复制的内容,这通常是很好的做法。

因此,目录结构如下:

$ tree
.
├── dir 1
│   ├── bar.jpg
│   └── foo.jpg
├── findbasenames.sh
└── top dir
    └── dir 2
        └── foo.doc

你可以看到有foo.jpgfoo.doc。运行脚本我们得到:

$ ./findbasenames.sh  'dir 1'/ ./top\ dir/dir\ 2/
cp dir 1/foo.jpg dir 1/

如您所见,该脚本处理名称中包含空格的目录、多层目录(因此您无需到处查找cd),并且find是递归的,如果您需要它在多个目录中查找重复的基本名称,尽管-maxdepth 1您可以使用标志来指示find仅处理当前目录中的文件。此外,这应该可以处理困难的文件名,例如-foo.doc。这可能是一种冗长而迂回的方式,但可以相当灵活。

答案3

拥有findshell(POSIX sh/bash/Korn/zsh)参数扩展

find /absolute/path/to/dIR2 -type f -name '*.doc' -execdir sh -c '
    echo cp /absolute/path/to/dIR1/${0%.doc}.jpg .
' {} \;

cp /absolute/path/to/dIR1/./cockroach.jpg .
cp /absolute/path/to/dIR1/./spider.jpg .
cp /absolute/path/to/dIR1/./viper.jpg .

在哪里:

.
├── dIR1
│   ├── cockroach.jpg
│   ├── marshmallow.jpg
│   ├── muffin.jpg
│   ├── spider.jpg
│   ├── taffy.jpg
│   └── viper.jpg
└── dIR2
    ├── cockroach.doc
    ├── spider.doc
    └── viper.doc

结果(删除对文件执行复制的echo前面的):cp

.
├── dIR1
│   ├── cockroach.jpg
│   ├── marshmallow.jpg
│   ├── muffin.jpg
│   ├── spider.jpg
│   ├── taffy.jpg
│   └── viper.jpg
└── dIR2
    ├── cockroach.doc
    ├── cockroach.jpg
    ├── spider.doc
    ├── spider.jpg
    ├── viper.doc
    └── viper.jpg

为了避免cp对找到的每个文件进行调用,我们可以使用以下命令:

find /absolute/path/to/dIR2 -type f -name '*.doc' -execdir bash -c '
    printf "/absolute/path/to/dIR1/%s.jpg\0" "$'{@%.doc}'"' _ {} + \
    | xargs -0 cp -t /absolute/path/to/dIR2

答案4

不需要循环,只需使用传统的方式即可。

# assuming we are directory above folder{1,2}
# also assume no spaces in file names
# real file names don't have spaces :p

cd folder2
F=$(echo * | sed -e 's/\.doc/\.\*/g')
cd ../folder1 && cp $F ../folder2

相关内容