У меня есть файл, который находится в разных папках (с расширением .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 не входит в цикл.

Как я могу заставить sed работать в цикле, чтобы соответствующие имена папок использовались для замены строки в файле?

2
Siva 7 Авг 2018 в 22:23
2
Вам не нужно использовать cd и использовать здесь find. Используйте sed напрямую с ${ii}/*.mvw в качестве аргумента.
 – 
don_crissti
7 Авг 2018 в 22:26

3 ответа

Команда cd выполняется во вспомогательной оболочке и поэтому фактически ничего не делает. Таким образом, ваша команда find выполняется из родительского каталога по одному разу для каждого имени подкаталога в XYZ_DV_L02*. Первый вызов 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

Спасибо за редактирование от Стефана Шазеласа

Пока я просто скопировал команду find из вопроса, только исправив путь, стоит пояснить изменения:

  • ${ii} был без кавычек, теперь он является частью кавычек, содержащих весь аргумент sed.
  • Использование + вместо \; приведет к тому, что find будет выполнять одну команду 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 9 Авг 2018 в 19:51
Мне нужна дальнейшая помощь в доработке сценария. Я хочу добавить подстановочный знак для выбора заменяемой строки, то есть я хочу написать только *.h3d вместо (D3PLOT_1DForce_EffPlStrainMax_VonMisesMax.h3d) и добавить расширение после переменной в sed, например ${ii}.h3d. Я хочу заменить все в файле с расширением .h3d. Ваша помощь будет высоко оценена ..!
 – 
Pavan Raj Singh
9 Авг 2018 в 10:35

Попробуй это,

#!/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 7 Авг 2018 в 22:22
Это работает хорошо..! Большое спасибо. Не могли бы вы объяснить, почему вы удалили скобки команды cd?
 – 
Pavan Raj Singh
9 Авг 2018 в 10:39

Если у вас есть структура каталогов только с несколькими подкаталогами, в каждом из которых есть этот .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 или какую-либо другую реализацию sed, которая выполняет редактирование на месте с помощью -i без параметра-аргумента.

1
Kusalananda 8 Авг 2018 в 09:44
Большое спасибо ... Это работает. Самое приятное, что вы все прекрасно объяснили. Мне нужна дальнейшая помощь в доработке сценария. Я хочу добавить подстановочный знак для выбора заменяемой строки, то есть я хочу написать только *.h3d вместо (D3PLOT_1DForce_EffPlStrainMax_VonMisesMax.h3d) и добавить расширение после переменной в sed, например ${ii}.h3d. Я хочу заменить все в файле с расширением .h3d. Ваша помощь будет высоко оценена ..!
 – 
Pavan Raj Singh
9 Авг 2018 в 10:47