![仅复制具有相似名称的文件](https://linux22.com/image/1064717/%E4%BB%85%E5%A4%8D%E5%88%B6%E5%85%B7%E6%9C%89%E7%9B%B8%E4%BC%BC%E5%90%8D%E7%A7%B0%E7%9A%84%E6%96%87%E4%BB%B6.png)
我的 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
$0
sh
{}
"$2
folder 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 存在于目标目录中,我们可以运行第二个,
-exec
即echo cp {} ./f2 \;
笔记:删除echo
部分以进行实际复制,echo
之前cp
仅用于测试,以便您可以确保只复制需要复制的内容,这通常是很好的做法。
因此,目录结构如下:
$ tree
.
├── dir 1
│ ├── bar.jpg
│ └── foo.jpg
├── findbasenames.sh
└── top dir
└── dir 2
└── foo.doc
你可以看到有foo.jpg
和foo.doc
。运行脚本我们得到:
$ ./findbasenames.sh 'dir 1'/ ./top\ dir/dir\ 2/
cp dir 1/foo.jpg dir 1/
如您所见,该脚本处理名称中包含空格的目录、多层目录(因此您无需到处查找cd
),并且find
是递归的,如果您需要它在多个目录中查找重复的基本名称,尽管-maxdepth 1
您可以使用标志来指示find
仅处理当前目录中的文件。此外,这应该可以处理困难的文件名,例如-foo.doc
。这可能是一种冗长而迂回的方式,但可以相当灵活。
答案3
拥有find
和shell(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