парсинг и замена некоторых строк в двух файлах - PullRequest
1 голос
/ 05 ноября 2011

Я хочу запустить скрипт оболочки с таким использованием:

./run A.txt B.xml

A.txt содержит некоторую статистику:

Accesses = 1
Hits = 2
Misses = 3
Evictions = 4
Retries = 5

B.xml выглядит так:

<stat name="total_accesses" value="0"/>
<stat name="total_misses" value="0"/>
<stat name="conflicts" value="0"/>  

Я хочу заменить некоторые статистические данные в B.xml из A.txt.Например, я хочу

1- find "Accesses" in A.txt
2- find "total_accesses" in B.xml
3- replace 0 with 1
1- find "Misses" in A.txt
2- find "total_misses" in B.xml
3- replace 0 with 3

Так что B.xml будет выглядеть так:

<stat name="total_accesses" value="1"/>
<stat name="total_misses" value="3"/>
<stat name="conflicts" value="0"/>  

Я хочу сделать это с помощью команды «sed» оболочки.Однако я нахожу это довольно сложным, поскольку регулярное выражение трудно понять.

Помогает ли мне «седь» в этой проблеме, или мне нужно найти другой путь?

Ответы [ 3 ]

1 голос
/ 06 ноября 2011

хотя вы можете взломать это в командной строке, я бы порекомендовал этого не делать.

XML слишком хрупок для такой обработки - используйте правильную библиотеку XML и анализируйте XML перед манипулированиемЭто.В противном случае вы могли бы легко получить сломанный XML.например, напишите скрипт на Ruby, Python или Perl и используйте библиотеку XML.

1 голос
/ 06 ноября 2011

Это может быть немного тяжеловесно для такого простого случая, но вот сценарий Python, который выполняет эту работу:

#!/usr/bin/env python
import sys
import xml.etree.ElementTree as etree

# read A.txt; fill stats
stats = {}
for line in open(sys.argv[1]):
    if line.strip():
        name, _, count = line.partition('=')
        stats["total_"+name.lower().strip()] = count.strip()

# read B.xml; fix to make it a valid xml; replace stat[@value]
root = etree.fromstring("<root>%s</root>" % open(sys.argv[2]).read())
for s in root:
    if s.get('name') in stats:
        s.set('value', stats[s.get('name')])
    print etree.tostring(s),

Пример

$ python fill-xml-template.py A.txt B.xml 
<stat name="total_accesses" value="1" />
<stat name="total_misses" value="3" />
<stat name="conflicts" value="0" /> 

Для обработки входных файловпостепенно или для внесения изменений на месте вы можете использовать следующее:

#!/usr/bin/env python
import fileinput
import sys
import xml.etree.ElementTree as etree

try: sys.argv.remove('-i')
except ValueError: 
    inplace = False
else: inplace = True # make changes inplace if `-i` option is specified

# read A.txt; fill stats
stats = {}
for line in open(sys.argv.pop(1)):
    if line.strip():
        name, _, count = line.partition('=')
        stats["total_"+name.lower().strip()] = count.strip()

# read input; replace stat[@value]
for line in fileinput.input(inplace=inplace):
    s = etree.fromstring(line)
    if s.get('name') in stats:
        s.set('value', stats[s.get('name')])
    print etree.tostring(s)

Пример

$ python fill-xml-template.py A.txt B.xml -i

Может считывать из stdin или обрабатывать несколько файлов:

$ cat B.xml | python fill-xml-template.py A.txt
<stat name="total_accesses" value="1" />
<stat name="total_misses" value="3" />
<stat name="conflicts" value="0" />
1 голос
/ 05 ноября 2011

Вот сценарий оболочки, который делает то, что вы хотите:

<code>
#!/bin/bash
while read line
do
    key=`echo $line | cut -d' ' -f1`
    value=`echo $line | cut -d' ' -f3`
    xmlLine=`grep -i $key $2`
    if [ -n "$xmlLine" ]; then
        for num in `seq 5`
        do
            field[${num}]=`echo "$xmlLine" | cut -d'"' -f${num}`
        done
        echo ${field[1]}\"${field[2]}\"${field[3]}\"$value\"${field[5]}
    fi
done 

Вы можете скопировать его в файл, скажем A.sh, дать ему разрешение на запуск (chmod +x A.sh) и затем:

./A.sh A.txt B.xml
</code>

Обратите внимание, что этот код не подходит для производства, и регулярные выражения имеют первостепенное значение для этих сценариев.

...