web-dev-qa-db-ja.com

sedを使用して、異なるフォルダーにあるファイルの文字列を置き換えます

別のフォルダ(ファイル拡張子.mvw)に配置されているファイルがあります。各フォルダに移動し、文字列(ファイル* .mvw内)「D3PLOT_1DForce」をフォルダの名前に置き換えるスクリプトを作成したいと思います。

フォルダ構造は以下の通りです-

  • XYZ_DV_L02_P01
  • XYZ_DV_L02_P02
  • XYZ_DV_L02_P03

私は次のスクリプトを書きました-

#!/bin/sh
for ii in XYZ_DV_L02* ; do
(cd "$ii")
find . -iname "*.mvw" -type f -exec sed -i "s/D3PLOT_1DForce_EffPlStrainMax_VonMisesMax/"${ii}"/g" "{}" \;
done

これは機能し、文字列をフォルダの名前に置き換えますが、すべてのファイルの最初のフォルダの名前を置き換えています。 sedはforループに入っていないと思います。

Sedをループ内で機能させて、それぞれのフォルダー名を使用してファイル内の文字列を置き換えるにはどうすればよいですか?

2
Pavan Raj Singh

cdコマンドはサブシェルで実行されるため、事実上何もしません。したがって、findコマンドは、親ディレクトリから、XYZ_DV_L02*のサブディレクトリ名ごとに1回実行されます。 sedを最初に呼び出すと、すべてのファイルの文字列が変更されます。次のsedを呼び出すと、文字列は見つかりませんが、とにかく実行されます。

これを試して:

#!/bin/sh
for ii in XYZ_DV_L02* ; do
    find "$ii" -iname "*.mvw" -type f -exec sed -i "
      s/D3PLOT_1DForce_EffPlStrainMax_VonMisesMax/${ii}/g" "{}" +
done

StéphaneChazelasからの編集に感謝します

パスを修正するだけの質問からfindコマンドをコピーしましたが、変更点を説明する価値があります。

  • ${ii}は引用符で囲まれていませんでしたが、現在はsed引数全体を含む引用符の一部になっています。
  • +の代わりに\;を使用すると、findは、ファイルごとに1つではなく、複数のファイルに対して1つのsedコマンドを実行します。

編集

コメントからのリクエストに対して、これにより、セット[-_a-zA-Z0-9]の後に.h3dが続くすべてのシーケンスが、.mvw拡張子を含むファイル名である置換テキストに置き換えられます。これが私が理解している方法です。コメントですが、役に立たないようです。パターン内のテキストにアクセスする場合は、\1を使用できます。特定のニーズに合わせてセットを調整する必要がある場合があります。

#!/bin/sh
for ii in XYZ_DV_L02* ; do
    find "$ii" -iname "*.mvw" -type f -exec sed -i -r "
      s/([-_a-zA-Z0-9]+)\.h3d/${ii}.h3d/g" "{}" +
done
4
RalfFriedl

これを試して、

#!/bin/sh
for ii in XYZ_DV_L02* ; do
cd "$ii"
find . -iname "*.mvw" -type f -exec sed -i "s/D3PLOT_1DForce/"${ii}"/g" "{}" \;
cd ..
done

ディレクトリをforループの最初の入力フォルダに変更してファイルを見つけるのは問題ありません。ただし、次のループを実行する前に、親ディレクトリに戻る必要があります。

1
Siva

いくつかのサブディレクトリのみを含むディレクトリ構造があり、それぞれに変更する.mvwファイルがある場合は、findを使用する必要はありません。これは、ファイルはです。

for pathname in XYZ_DV_L02*/*.mvw; do
    sed -i "s/D3PLOT_1DForce/${pathname%%/*}/g" "$pathname"
done

これにより、指定されたサブディレクトリ内のすべての.mvwファイルが繰り返され、文字列がディレクトリ名に置き換えられます。パラメータ展開${pathname%%/*}は、/の文字列の最初の$pathname以降のすべてを削除します。

テスト:

$ tree
.
|-- XYZ_DV_L02_P01
|   `-- somefile.mvw
|-- XYZ_DV_L02_P02
|   `-- somefile.mvw
`-- XYZ_DV_L02_P03
    `-- somefile.mvw

3 directories, 3 files

$ cat XYZ_DV_L02_P0*/*.mvw
Hello D3PLOT_1DForce_EffPlStrainMax_VonMisesMax!
Hello D3PLOT_1DForce_EffPlStrainMax_VonMisesMax!
Hello D3PLOT_1DForce_EffPlStrainMax_VonMisesMax!

(ここでループを実行しています)

$ cat XYZ_DV_L02_P0*/*.mvw
Hello XYZ_DV_L02_P01_EffPlStrainMax_VonMisesMax!
Hello XYZ_DV_L02_P02_EffPlStrainMax_VonMisesMax!
Hello XYZ_DV_L02_P03_EffPlStrainMax_VonMisesMax!

明らかに、階層が深い場合は、とにかくfindを使用する必要がありますが、findの外部ではなく、findの内部からループを実行できます。

find XYZ_DV_L02*/ -type f -name '*.mvw' -exec sh -c '
    for pathname do
        sed -i "s/D3PLOT_1DForce/${pathname%%/*}/g" "$pathname"
    done' sh {} +

ここでは、パターン.mvwに一致するディレクトリの下にあるすべてのXYZ_DV_L02*/ファイルが見つかります。これらの見つかったファイルに対して、この回答の最初のループと同じことを行うループを実行します。これにより、任意のディレクトリの下にあるすべてのD3PLOT_1DForceファイルの文字列.mvwが、対応するXYZ_DV_L02*ディレクトリ名に置き換えられます。

関連:

この回答は、GNU sedまたはオプション引数なしで-iを使用してインプレース編集を行うその他のsed実装を前提としています。

1
Kusalananda