Многострочный поиск и замена

У меня есть неструктурированный файл, и я хотел бы найти и заменить шаблон строк.

  • Должен заменить строку, которая существует между строками SELECT и FROM; тот, кто находится за пределами этого шаблона, должен оставаться как есть.

Формат файла такой

col4 is required to be upper so
make col4 upper
abc 12345 !$% DATA SELECT 
col1 as col1,
col2 as col2.
col3,
sch.col4 as col4,
sch.tab.col4 as col4_1,
col4,
col5 FROM sch.tab
xyz 34354 ^&* DATA SELECT
col5 as col5,
col3,
col4,
col4 as col4,
col4 FROM
blah blah blah

Я хочу заменить:

  • col4, с upper(col4) as col4,
  • sch.col4 с upper(sch.col4)
  • sch.tab.col4 с upper(sch.tab.col4)
  • col4(если col4 находится в конце запроса выбора) с upper(col4) as col4

Файл находится на сервере Linux, и я попытался использовать sed и awk, чтобы сузить строки, содержащие col4, но не смог двигаться дальше.

Я смог определить один шаблон, используя ниже

awk '/SELECT/,/FROM/' test_file.txt | awk '/col4/{print $0, NR}' | awk -F AS '{print $1}' 

Найдите текст между SELECT и FROM
Определите строки, в которых есть col4
напечатать первое поле

sed -n -e '/SELECT/,/FROM/p' -e 's/\(\([a-zA-Z]\{1,\}\.\)\{0,\}\)col4/upper(\0)/g' test_file.txt

и используя sed

Действительный:

col4 is required to be upper so
make col4 upper
abc 12345 !$% DATA SELECT
col1 as col1,
col2 as col2.
col3,
sch.col4 as col4,
sch.tab.col4 as col4_1,
col4,
col5 FROM sch.tab
xyz 34354 ^&* DATA SELECT
col5 as col5,
col3,
col4,
col4 as col4,
col4 FROM
blah blah blah

Ожидаемый результат:

col4 is required to be upper so
make col4 upper
abc 12345 !$% DATA SELECT
col1 as col1,
col2 as col2.
col3,
upper(sch.col4) as col4,
upper(sch.tab.col4) as col4_1,
upper(col4) as col4,
col5 FROM sch.tab
xyz 34354 ^& DATA SELECT
col5 as col5,
col3,
upper(col4) as col4,
upper(col4) as col4,
upper(col4) as col4 FROM
blah blah blah

Любая помощь высоко ценится!!

Python также работает для вас?

Juan C 17.05.2019 17:39

@Juan C Привет, да, Python 3 тоже работает.

Carpediem 17.05.2019 17:47

@oguzismail Извинения. Это мой первый пост, отредактированный сейчас.

Carpediem 17.05.2019 17:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
127
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я думаю, это, по крайней мере, 95% делает это. Пожалуйста, сообщите мне, если есть ошибка:

with open('ej.txt', 'r') as file:
    string=file.read().replace('\n',' ')


import re

matches=re.findall(r'SELECT.*?FROM',string)
replacements = {"col4,":"upper(col4) as col4,",
             "sch.col4":"upper(sch.col4)",
             "sch.tab.col4":"upper(sch.tab.col4)",
             "col4 as col4,": "upper(col4) as col4,"}
new_matches=[]
for match in matches:
    for k,v in replacements.items():
        match=match.replace(k,v)
    new_matches.append(match)


for k,v in {k:v for k,v in zip(matches,new_matches)}.items() :
    string=string.replace(k,v)

string

Спасибо Хуан! Я попробую с этим кодом и отпишусь о результатах.

Carpediem 17.05.2019 19:38

По большей части это сработало, но поскольку файл читался как одна строка, мне трудно вернуть его в исходный формат. Есть ли способ сохранить формат как есть и обновить на месте? Еще раз спасибо за помощь!

Carpediem 17.05.2019 21:37

Вот короткий awk-скрипт, выполняющий ваш запрос:

awk '/SELECT/,/FROM/ {$0=gensub(/^[^[:space:]]*col4/,"upper(\\0)",-1);}1' input.txt

Результат:

abc 12345 !$% DATA SELECT
col1 as col1,
col2 as col2.
col3,
upper(sch.col4) as col4,
upper(sch.tab.col4) as col4_1,
upper(col4),
col5 FROM sch.tab
xyz 34354 ^&* DATA SELECT
col5 as col5,
col3,
upper(col4),
upper(col4) as col4,
upper(col4) FROM
blah blah blah

Объяснение:

/SELECT/,/FROM/ включительно диапазон, выбирая каждую строку от /SELECT/ до /FROM/

$0=gensub(***) обновить текущую строку с заменами из gensub()

/^[^[:space:]]*col4/ поиск не пробельного префикса для col4 в начале строки

upper(\\0)",-1 заменить найденное совпадение на верхнее ('найденное совпадение') только первое совпадение

1 распечатать текущую строку. 1

Спасибо! Это делает часть этого, но я хочу заменить «col4» на «upper (col4) as col4», «sch.col4» на «upper (sch.col4)», «sch.tab.col4» на «upper (sch .tab.col4)" "col4"(если col4 находится в конце запроса выбора) с upper(col4) как col4

Carpediem 17.05.2019 19:27

Ваше описание необходимых вам преобразований является неполным (например, вы говорите, что хотите изменить col4, на upper(col4) as col4,, но строка 7 ожидаемого вывода не отражает этого), поэтому я отложил это и просто написал это, но оно даст желаемый результат из предоставленного вами ввода (используя GNU awk для третьего аргумента для соответствия()), и, надеюсь, это то, что вы действительно хотите:

$ cat tst.awk
/SELECT/ { inBlock=1 }
inBlock {
    if ( match($0,/^((sch\.(tab\.)?)?col4\>)( as .*)/,a) ) {
        $0 = "upper(" a[1] ")" a[4]
    }
    else if ( match($0,/^(col4\>)(.*)/,a) ) {
        $0 = "upper(" a[1] ") as " a[1] a[2]
    }
}
/FROM/   { inBlock=0 }
{ print }

$ awk -f tst.awk file
col4 is required to be upper so
make col4 upper
abc 12345 !$% DATA SELECT
col1 as col1,
col2 as col2.
col3,
upper(sch.col4) as col4,
upper(sch.tab.col4) as col4_1,
upper(col4) as col4,
col5 FROM sch.tab
xyz 34354 ^&* DATA SELECT
col5 as col5,
col3,
upper(col4) as col4,
upper(col4) as col4,
upper(col4) as col4 FROM
blah blah blah

Спасибо Эд! Это решение помогло мне.

Carpediem 28.05.2019 19:45

Пожалуйста. См. stackoverflow.com/help/someone-answers, чтобы узнать, что делать дальше.

Ed Morton 28.05.2019 19:49
Ответ принят как подходящий

С СЭД:

sed '/SELECT/,/FROM/ {s/as col4 *//;s/\([A-Za-z]*\.\)\{0,\}col4/upper(&) as col4/;}' file

Пояснения:

  • s/as col4 *//: существующий as col4 удален, чтобы предотвратить дублирование после второй замены
  • \([A-Za-z]*\.\)\{0,\}col4: поиск 0 или более комбинаций букв и точек, за которыми следует col4
  • upper(&) as col4/;: заменить новым текстом (соответствующая строка вставляется с помощью &)

Спасибо @SLePort. Это решение отлично сработало для меня.

Carpediem 28.05.2019 19:44

Другие вопросы по теме