2

I have a file which is placed in different folders(with file extension .mvw). I want to write a script which goes to each folder and replace a string(in the file *.mvw) "D3PLOT_1DForce" with the name of the folders.

folder Structure is as below-

  • XYZ_DV_L02_P01
  • XYZ_DV_L02_P02
  • XYZ_DV_L02_P03

I have written the following script-

#!/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

It works and replaces the string with the name of the folder but it is replacing the name of the 1st folder in all the files. I think sed is not going in for loop.

How can I put the sed to work in the loop so that respective folder names are used to replace the string in the file?

Siva
  • 9,077

3 Answers3

4

The cd command is executed in a sub shell and therefor effectively does nothing. So your find command is executed from the parent directory, once for each of the sub directory names in XYZ_DV_L02*. The first invocation of sed changes the string in all files, the following invocations of sed don't find the string but are performed anyway.

Try this:

#!/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

Thanks for the edit from Stéphane Chazelas

While I just copied the find command from the question only fixing the path, it is worth explaining the changes:

  • ${ii} was unquoted, now it is part of the quotes that contain the whole sed argument.
  • Using + instead of \; will cause find to execute one sed command for multiple file, not one for each file.

Edit

To the request from the comment, this will replace every sequence of the set [-_a-zA-Z0-9] followed by .h3d with the replacement text, which is the filename including the .mvw extension, which is how I understand the comment, but it doesn't seem useful. If you want to access the text in the pattern, you can use \1. You may have to adapt the set for your particular needs.

#!/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
RalfFriedl
  • 8,981
  • I want further help to refine the script. I want to add a wildcard to pick the string to be replaced, meaning, I only want to write *.h3d instead of (D3PLOT_1DForce_EffPlStrainMax_VonMisesMax.h3d) and I want to add extension after the variable in sed like ${ii}.h3d. I want to replace everything in the file with .h3d extension.Your help will be higly appreciated ..! – Pavan Raj Singh Aug 09 '18 at 07:35
1

Try this,

#!/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

It's fine to change the directory to the first input folder in for loop and finding the file. But we have to get back to the parent directory before running the next loop.

Siva
  • 9,077
1

If you have a directory structure with only a number of subdirectories, each having this .mvw file that you'd like to modify, then there is no need to use find because you already know exactly where your files are.

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

This iterates over all .mvw files in the indicated subdirectories and replaces the string with the directory name. The parameter expansion ${pathname%%/*} deletes everything after the first / in the string in $pathname.

Testing:

$ 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!

(running loop here)

$ 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!

Obviously, if you have a deep hierarchy, then you may need to use find anyway, but you can do the looping from within find rather than outside of 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 {} +

Here, we find all the .mvw files beneath any of the directories matching the pattern XYZ_DV_L02*/. For these found files, we run a loop that does the same thing as the loop at the start of this answer. This would replace the string D3PLOT_1DForce in all the .mvw files anywhere beneath any of the directories with the corresponding XYZ_DV_L02* directory name.

Related:

This answer assumes GNU sed or some other sed implementation that does in-place editing with -i without an option-argument.

Kusalananda
  • 333,661
  • Thank you so much...It works. The best part is that you have beautifully explained everything. I want further help to refine the script. I want to add a wildcard to pick the string to be replaced, meaning, I only want to write *.h3d instead of (D3PLOT_1DForce_EffPlStrainMax_VonMisesMax.h3d) and I want to add extension after the variable in sed like ${ii}.h3d. I want to replace everything in the file with .h3d extension.Your help will be highly appreciated ..! – Pavan Raj Singh Aug 09 '18 at 07:47