Команда Bash для поиска строки в файле и замены значения значением из другого файла - PullRequest
2 голосов
/ 25 февраля 2011

У меня есть два файла: a.) XmlFile.xml b.) Emails.txt

xmlFile.xml имеет следующую структуру, повторенную несколько раз

<gname>Office</gname>
<uname>person</uname>

emails.txt имеет список адресов электронной почты

email1@company.com
email2@company.com
...

Что я хочу сделать, это заменить "person" в xmlFile.xml на последующее значение, взятое из emails.txt

Я пытался

# while read email ; do sed  "s/person/$email/g" xmlFile.xml > xmlFile.new; done < emails.txt

Однако я получаю файл, в котором все "персональные" значения заменены на последнее электронное письмо от emails.txt

Спасибо, Filip

Ответы [ 3 ]

3 голосов
/ 25 февраля 2011
awk 'NR==FNR{e[i++]=$0;next} /person/{sub("person",e[j++])}1' emails.txt xmlFile.xml

Пояснение

  1. NR==FNR: Это верно только тогда, когда awk читает первый файл.По сути, он проверяет общее количество просмотренных записей (NR) по сравнению с входной записью в текущем файле (FNR).
  2. e[i++]=$0: создать массив с именем e , индекс которого увеличивается на 1 (i++) и значение who равно текущей записи $0.Этот массив будет содержать наши электронные письма
  3. next: игнорировать оставшуюся часть сценария, если это будет достигнуто, начать заново с новой входной записи
  4. /person/: выполнять последующий код только в том случае, еслитекущая запись соответствует регулярному выражению "person"
  5. sub("person",e[j++]): замените буквальное значение "person" на значение в нашем массиве e , которое мы создали ранее.Увеличьте этот массив j++ для следующей записи, которую мы сопоставим
  6. 1 : всегда возвращает значение true, по сути, ярлык для {print $0}, или выведите нашу текущую запись

Подтверждение концепции

$ cat emails.txt
email1@company.com
email2@company.com
email3@company.com
email4@company.com
email5@company.com
email6@company.com
email7@company.com
email8@company.com
email9@company.com

$ cat xmlFile.xml
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>
<gname>Office</gname>
<uname>person</uname>

$ awk 'NR==FNR{e[i++]=$0;next} /person/{sub("person",e[j++])}1' emails.txt xmlFile.xml
<gname>Office</gname>
<uname>email1@company.com</uname>
<gname>Office</gname>
<uname>email2@company.com</uname>
<gname>Office</gname>
<uname>email3@company.com</uname>
<gname>Office</gname>
<uname>email4@company.com</uname>
<gname>Office</gname>
<uname>email5@company.com</uname>
<gname>Office</gname>
<uname>email6@company.com</uname>
<gname>Office</gname>
<uname>email7@company.com</uname>
<gname>Office</gname>
<uname>email8@company.com</uname>
<gname>Office</gname>
<uname>email9@company.com</uname>

В приведенном выше сценарии предполагается, что person является буквальным значением.Если это не так, то ..

Заменить: /person/{sub("person",emails[j++])}
На: /<uname>/{sub(".*","<uname>"emails[j++]"</uname>")}

1 голос
/ 25 февраля 2011

Один из способов сделать это - использовать редактирование на месте:

while read email ; do sed -i "s/person/$email/;q" xmlFile.xml; done < emails.txt

Если в файле XML мало или ничего больше, чем то, что вы показываете, просто восстановите его:

sed -e 'i <gname>Office</gname>' -e 's|.*|<uname>&</uname>|' emails.txt > newxmlFile.xml

, даже не касаясь существующего xmlFile.xml.

Однако вам, вероятно, следует использовать синтаксический анализатор XML, такой как xmlstarlet.

0 голосов
/ 16 марта 2011

Вот как это сделать, используя bash & xmlstarlet!

IFS=$'\n' read -r -d "" -a array < emails.txt                   # read file with email addresses into array
n=$(xmlstarlet sel -T -t -v "count(//uname)" -n xmlFile.xml)    # count "uname" nodes in XML file
xmlFileStr="$(< xmlFile.xml)"                                   # read XML file into variable


if [[ $n -eq ${#array[@]} ]]; then   # if the number of nodes & email addresses is equal ...
   for ((i=1; i <= ${n}; i+=1)); do
      xmlFileStr="$(printf '%s' "$xmlFileStr" | xmlstarlet ed -P -t -u "//uname[${i}]" -v "${array[$((i-1))]}")"
   done
fi

printf '%s\n' "$xmlFileStr" > xmlFile.xml
cat xmlFile.xml
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...