Сценарий оболочки - поиск и замена текста в нескольких файлах с использованием списка строк - PullRequest
8 голосов
/ 16 марта 2009

У меня есть файл "changesDictionary.txt", содержащий (переменное число) пар строк ключ-значение.

, например

"textToSearchFor" = "theReplacementText"

(Формат словаря не важен и может быть изменен по мере необходимости.)

Мне нужно перебрать содержимое данного каталога, включая подкаталоги. Для каждого файла с расширением ".txt" мы ищем каждого ключей в файле changesDictionary.txt, заменяя каждый найденный экземпляр значением строки замены.

т.е. поиск и замена по нескольким файлам, но с использованием списка условий поиска / замены, а не одного термина поиска / замены.

Как я мог это сделать? (Я изучил примеры одиночного поиска / замены, но не понимаю, как выполнять многократный поиск в файле.)

Реализация (bash, perl, что угодно) не важна, если я могу запустить ее из командной строки в Mac OS X. Спасибо за любую помощь.

Ответы [ 4 ]

6 голосов
/ 16 марта 2009

Я бы конвертировал ваш файл changesDictionary.txt в сценарий sed, используя ... sed:

$ sed -e 's/^"\(.*\)" = "\(.*\)"$/s\/\1\/\2\/g/' \
      changesDictionary.txt  > changesDictionary.sed

Примечание , любые специальные символы для регулярных выражений или выражений sed в вашем словаре будут неверно интерпретироваться sed, поэтому ваш словарь может содержать только самые примитивные функции поиска и замены, или вы Вам нужно будет поддерживать файл sed с правильными выражениями. К сожалению, в sed нет простого способа либо отключить регулярное выражение и использовать только совпадение строк, либо процитировать ваши поиски и замены как «литералы».

С помощью полученного сценария sed используйте команды find и xargs, а не find -exec, чтобы как можно быстрее преобразовать файлы с помощью сценария sed, обрабатывая их более одного за раз. .

$ find somedir -type f -print0 \
   | xargs -0 sed -i -f changesDictionary.sed

Примечание , опция -i для sed редактирует файлы «на месте», поэтому обязательно делайте резервные копии для безопасности или используйте -i~ для создания тильд-резервных копий.

Последнее замечание , использование поиска и замен может иметь непредвиденные последствия. Будут ли у вас поиски, которые являются подстрока других поисков? Вот пример.

$ cat changesDictionary.txt
"fix" = "broken"
"fixThat" = "Fixed"
$ sed -e 's/^"\(.*\)" = "\(.*\)"$/s\/\1\/\2\/g/' changesDictionary.txt  \
   | tee changesDictionary.sed
s/fix/broken/g
s/fixThat/Fixed/g
$ mkdir subdir
$ echo fixThat > subdir/target.txt
$ find subdir -type f -name '*.txt' -print0 \
   | xargs -0 sed -i -f changesDictionary.sed
$ cat subdir/target.txt
brokenThat

Должно ли "fixThat" стать "Fixed" или "brokenThat"? Заказ имеет значение для сценария sed. Точно так же поиск и замена могут быть найдены и заменены более одного раза - изменение «a» на «b», может быть позже изменено другим поиском и заменой с «b» на «c».

Возможно, вы уже рассмотрели оба этих вопроса, но я упоминаю об этом, потому что я пробовал то, что вы делали раньше, и не думал об этом. Я не знаю ничего, что просто делает правильную вещь для одновременного поиска и замены. Итак, вам нужно запрограммировать его на то, чтобы делать правильные вещи самостоятельно.

5 голосов
/ 16 марта 2009

Вот основные шаги, которые я бы сделал

  1. Скопируйте файл changesDictionary.txt
  2. В нем заменить "a" = "b" на эквивалентную строку sed: например, (используйте $ 1 для имени файла)

    sed -e 's / a / b / g' $ 1

    (вы могли бы написать скрипт для этого или просто сделать это вручную, если вам просто нужно сделать это один раз, и он не слишком большой).

  3. Если все файлы находятся в одном каталоге, вы можете сделать что-то вроде:

    ls * .txt | xargs scriptFromStep2.sh

  4. Если они находятся в подкаталогах, используйте find для вызова этого скрипта во всех файлах, что-то вроде

    найти. -name '* .txt' -exec scriptFromStep2.sh {} \;

Это не совсем точно, проведите несколько экспериментов, чтобы убедиться, что вы все поняли правильно - я бы использовал именно такой подход.

(но, если вы можете, просто используйте perl, было бы намного проще)

2 голосов
/ 26 марта 2011

Используйте этот инструмент, который написан на Perl - с довольно большим количеством наворотов - старенький, но положительный герой:

http://unixgods.org/~tilo/replace_string/

Особенности:

  • выполнить несколько операций поиска-замены или запроса-поиска-замены
  • выражения поиска-замены можно задавать в командной строке или читать из файла
  • обрабатывает несколько входных файлов
  • рекурсивно спуститься в каталог и выполнить несколько операций поиска / замены для всех файлов
  • пользовательские выражения perl применяются к каждой строке каждого входного файла
  • при желании запустить в режиме абзаца (для многострочного поиска / замены)
  • интерактивный режим
  • пакетный режим
  • опционально резервное копирование файлов и нумерация резервных копий
  • сохранить режимы / владельца при запуске от имени пользователя root
  • игнорировать символические ссылки, пустые файлы, защищенные от записи файлы, сокеты, именованные каналы и имена каталогов
  • опционально заменять строки, соответствующие или не соответствующие заданному регулярному выражению

Этот сценарий довольно широко использовался на протяжении многих лет для больших наборов данных.

1 голос
/ 06 августа 2009
#!/bin/bash
f="changesDictionary.tx"
find /path -type f -name "*.txt" | while read FILE 
do
    awk 'BEGIN{ FS="=" }
    FNR==NR{ s[$1]=$2;  next }
    {
       for(i in s){      
        if( $0 ~ i ){ gsub(i,s[i]) }
       }
       print $0
    }' $f $FILE  > temp
    mv temp $FILE
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...