可変サブフォルダ名を使用して複数のサブフォルダのファイルをコピーしたいと思います。
例:
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
**
dotglob
nullglob
繰り返しますが、次のように(zsh
通常のファイルを明示的に要求し、dotglob
同じワイルドカード処理を有効にする):nullglob
bash
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
ない間違いを防ぐいくつかの安全チェックがあるという利点があります。