#regex #variables #sed #awk
#регулярное выражение #переменные #sed #awk
Вопрос:
Я не уверен, смогу ли я сделать это исключительно с помощью sed:
Я пытаюсь изменить порядок строк следующим образом
GF:001,GF:00012,GF:01223<TAB>XXR
GF:001,GF:00012,GF:01223,GF:0666<TAB>XXXR3
Для
GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3
У кого-нибудь есть какие-нибудь подсказки? Мощность GF: XXXX меняется в зависимости от длины GF: XXXX.
Я застрял с sed -n '
, но я не могу ссылаться на первоначально подобранный шаблон в первую очередь. есть идеи? приветствия!
'/(XX.*)$/' {
s/,/t1n/
}' input
Обновление: Я думаю, что это невозможно сделать, просто используя sed. Итак, я использовал perl для этого:
perl -e 'open(IN, "< file");
while (<IN>) {
@a = split(/t/);
@gos = split(/,/, $a[0]);
foreach (@gos) {
print $_."t".$a[1];
}
close( IN );' > output
Но если кто-нибудь знает способ решить это просто с помощью sed
, пожалуйста, опубликуйте его здесь…
Ответ №1:
Это можно сделать в sed
, хотя я, вероятно, использовал бы Perl (или Awk, или Python) для этого.
Я не претендую на элегантность этого решения, но грубая сила и невежество иногда окупаются. Я создал файл с неоригинальным названием sed.script
, содержащий:
/(GF:[0-9]*),(.*)<TAB>(.*)/{
:redo
s/(GF:[0-9]*),(.*)<TAB>(.*)/1<TAB>3@@@@@2<TAB>3/
h
s/@@@@@.*//
p
x
s/.*@@@@@//
t redo
d
}
Я запустил его как:
sed -f sed.script input
где input
содержались две строки, показанные в вопросе. Это привело к выводу:
GF:001<TAB>XXR
GF:00012<TAB>XXR
GF:01223<TAB>XXR
GF:001<TAB>XXXR3
GF:00012<TAB>XXXR3
GF:01223<TAB>XXXR3
GF:0666<TAB>XXXR3
(Я взял на себя смелость намеренно неверно истолковать <TAB>
как последовательность из 5 символов вместо одного символа табуляции; вы можете легко исправить ответ, чтобы вместо этого обрабатывать фактический символ табуляции.)
Объяснение sed
скрипта:
- Найдите строки с более чем одним вхождением
GF:nnn
, разделенные запятыми (нам не нужно обрабатывать строки, содержащие одно такое вхождение). Выполняйте остальную часть скрипта только с такими строками. Все остальное передается (печатается) без изменений. - Создайте метку, чтобы мы могли вернуться к ней
- Разделите строку на 3 запоминаемые части. Первая часть — это исходная информация GF; вторая часть — любая другая информация GF; третья часть — поле после
<TAB>
. Замените это на первое поле,<TAB>
, третье поле, неправдоподобный шаблон маркера (@@@@@
), второе поле,<TAB>
, третье поле. - Скопируйте измененную строку в пространство удержания.
- Удалите шаблон маркера до конца.
- С принтами.
- Замените пространство удержания на пространство шаблона.
- Удалите все, вплоть до шаблона маркера.
- Если мы выполнили какую-либо работу, вернитесь к
redo
метке. - Удалите то, что осталось (оно уже было напечатано).
- Конец блока скрипта.
Это простой цикл, который уменьшает количество шаблонов на один на каждой итерации.
Комментарии:
1. действительно впечатляет! я думал, что sed недостаточно мощный для выполнения циклов, но если у вас есть конструктор GOTO, вы можете имитировать цикл. спасибо за доказательство, Джонатан!
2. Ах да, востребованная конструкция GOTO — мечта инженеров-программистов :-).
Ответ №2:
Вы можете сделать это прямолинейно с помощью awk:
$ awk '{gsub(/,/, "t" $NF "n");print}' input
В этом случае мы просто заменяем запятую символом табуляции, объединенным с последним полем ( NF
хранит количество полей записи; $NF
возвращает NF
-е поле), объединенным с новой строкой. Затем выведите результат.
Это тоже можно решить с помощью sed, похожим способом, но, ИМХО, немного лучше, чем решение Jonathan (которое, должен заметить, довольно сложное).
sed -n '
:BEGIN
h
s/,.*<TAB>/<TAB>/
p
x
s/^[^,]*,//
t BEGIN' input
Здесь мы определяем метку в начале скрипта:
:BEGIN
Затем мы копируем содержимое пространства шаблонов в пространство хранения:
h
Теперь мы заменяем все, начиная с первой запятой и заканчивая табуляцией, только табуляцией:
s/,.*<TAB>/<TAB>/
Мы печатаем результат…
p
… и извлеките содержимое области удержания:
x
Поскольку мы напечатали первую строку, которая содержит первый GF:XXX
шаблон, за которым следует последний XXR
шаблон, мы удаляем первый GF:XXX
шаблон из строки:
s/^[^,]*,//
Если выполняется замена, мы переходим к началу скрипта:
t BEGIN
И все снова применяется к той же строке, за исключением того, что теперь в этой строке больше нет первого GF:XXX
шаблона. OTOH, если замена не произведена, то обработка текущей строки завершена, и мы больше не переходим к началу.
Комментарии:
1. решение awk было быстрым,
real 0m6.496s user 0m1.555s sys 0m0.109s
решение sed было медленнееreal 0m27.177s user 0m23.080s sys 0m0.129s
для файла в 28 тысяч строк2. На самом деле это имеет большой смысл, поскольку sed должен выполнять итерации по каждому экземпляру шаблона в строке. Я опубликовал решение sed, потому что оно было в спецификации, но, вероятно, это не лучшее решение для данного случая. В любом случае, я думаю, что решение awk на самом деле лучше, но я нашел эту проблему отличным упражнением sed 🙂
Ответ №3:
Если вам не нужен sed, awk хорош в этом:
awk -F't|,' '{ i=1; do { printf("%st%sn",$i,$NF); i ;} while ( i<NF ); }' inputfile
Комментарии:
1. Спасибо! я сделал это с помощью perl … хотя стоит взглянуть на
awk
.
Ответ №4:
Ну, мне потребовалось 3 часа, чтобы сделать это
sed -re ':a; s/(GF:[0-9]*[^,]*),([^<]*)(<TAB>[A-Z]*)/13n23/g;ta; ' file.txt
Комментарии:
1. @EdMorton это потому, что у тебя за плечами было 30 лет, а у меня было 3 дня
2. Нет, это потому, что awk имеет четкий и простой синтаксис, в то время как sed для чего-либо более сложного, чем простые замены в одной строке, требуются Розеттский камень, 3 мудреца и кольцо-декодер Бэтмена.
3. @EdMorton, мне действительно нужно принять решение. Согласно вашему совету, могу ли я выполнить 90% задач с помощью awk в одиночку. Я действительно хочу использовать только один из них, чтобы я мог перейти к деталям, но не могу решить, какой именно. Если вы говорите, что 90% задач можно выполнить с помощью awk, то я соглашусь с этим
4. Вы можете выполнить 100% задач по обработке текста только в awk. однако grep, sed и т.д. Можно использовать немного быстрее / проще для небольших задач. Большинство сложных действий, которые вы можете выполнить в awk, вы также можете выполнить в sed, но результирующий awk будет понятным, простым, быстрым в написании и простым в обслуживании, в то время как на написание эквивалентного sed потребуется на порядки больше времени, и потребуется полная перезапись даже для самого незначительного изменения требований. Изучите awk — материал, для которого вы ДОЛЖНЫ использовать sed, настолько прост, что вам не нужно прилагать никаких усилий для его изучения, вы просто узнаете это из пары примеров.
5. Я только что добавил объяснение моего awk-скрипта для вашей пользы. Обратите внимание, что все, что я делаю, это объясняю пару наиболее фундаментальных концепций awk, и из этого я ожидаю, что вы сможете понять сценарий. Сравните это со сложностью и спецификой объяснений сценариев sed на этой странице и серьезно представьте, что вы пытаетесь построить или отладить как awk, так и sed-скрипты. Примечание: Я использую sed почти каждый день, так что не думайте, что я против sed, это отличный инструмент для того, в чем он хорош, а именно для простых замен в одной строке.
Ответ №5:
awk -F'[,t]' '{for (i=1;i<NF;i ) print $i"t"$NF}' file
Awk считывает по одной строке за раз (по умолчанию) и разбивает строку на поля. Я использую -F, чтобы указать awk разделить строку на поля через каждую запятую или табуляцию. NF — это количество полей в строке, $i — содержимое поля с номером i.