ファイルを複数の(可変)フォルダにコピーする

ファイルを複数の(可変)フォルダにコピーする

可変サブフォルダ名を使用して複数のサブフォルダのファイルをコピーしたいと思います。

例:

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.

**あなたの質問で述べたように(bashシェルを想定して)使用してください。

shopt -s globstar nullglob dotglob

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

では、サブディレクトリを繰り返し一致させるようにシェルオプションをbash設定し、非表示の名前も一致するようにパターンを有効にします。シェルオプションを使用すると、一致するものがない場合、パターンは拡張されずに残っているのではなく、完全に消えます。globstar**dotglobnullglob

繰り返しますが、次のように(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ない間違いを防ぐいくつかの安全チェックがあるという利点があります。

関連情報