将文件复制到多个(可变)文件夹中

将文件复制到多个(可变)文件夹中

我想复制具有可变子文件夹名称的多个子文件夹中的文件。

例子:

mkdir rootdir
mkdir rootdir/dir1
mkdir rootdir/dir2
mkdir rootdir/dir3
touch rootdir/dir1/foo.txt
touch rootdir/dir2/foo.txt
touch rootdir/dir3/foo.txt

使用已知的子文件夹名称,我可以单独复制每个文件。

cp rootdir/dir1/foo.txt rootdir/dir1/bar.txt
cp rootdir/dir2/foo.txt rootdir/dir2/bar.txt
cp rootdir/dir3/foo.txt rootdir/dir3/bar.txt

但是,由于子文件夹数量未知且子文件夹名称未知(我知道文件名),我无法再这样做了。

我可以找到这些文件...

ls ./**/foo.txt
find . -name foo.txt

...但我没有找到允许将此信息传输到 cp (或替代工具)的语法。

答案1

有以下几种选择:

find rootdir -type f -name foo.txt -execdir cp {} bar.txt \;

foo.txt这将搜索在 中或下的任何位置调用的常规文件rootdir,当找到一个文件时,cp用于将其复制到bar.txt同一目录中的名称。该-execdir选项是非标准的,但通常实现,并将在找到文件的目录中执行给定的实用程序。将{}替换为找到的文件的名称。

或者,

find rootdir -type f -name foo.txt -exec sh -c '
    for pathname do
        cp "$pathname" "${pathname%/*}/bar.txt"
    done' sh {} +

这基本上做同样的事情,但调用一个简短的内联sh -c脚本,其中包含批量找到的foo.txt文件。 in循环cp会将其中每个文件复制到与找到的文件相同的目录,但路径名的文件名部分替换为bar.txt.

使用**,正如您在问题中提到的那样(假设有bashshell):

shopt -s globstar nullglob dotglob

for pathname in rootdir/**/foo.txt; do
    cp "$pathname" "${pathname%/*}/bar.txt"
done

在 中bash,设置globstarshell 选项可以使用**递归地匹配子目录,并且dotglob使模式也可以匹配隐藏名称。nullglob如果没有匹配项,shell 选项会使模式完全消失,而不是保持未展开状态。

再次,但使用(zsh明确要求常规文件并启用与dotglob和相同的通配符处理):nullglobbash

for pathname in rootdir/**/foo.txt(.ND); do
    cp $pathname $pathname:h/bar.txt
done

这里,与删除路径名的文件名部分$pathname:h相同(如“仅头部”,而不是尾部位)。$pathname:h

答案2

通过zsh,您还可以使用它的zmv自动加载功能:

autoload -Uz zmv # best in ~/.zshrc
zmv -C '(**/)(foo.txt)(#q.)' '${1}bar.txt'

或者:

zmv -C '**/foo.txt(#q.)' '$f:h/bar.txt'

与手动编写循环相比,使用它zmv的优点是它有一些安全检查,可以防止您犯不可逆转的错误。

相关内容