Страницы

Поиск по вопросам

Показаны сообщения с ярлыком sed. Показать все сообщения
Показаны сообщения с ярлыком sed. Показать все сообщения

вторник, 17 марта 2020 г.

Как склеить строки ?

#perl #shell #sed #linux


Вопрос в заголовке.
Только не все строки. Про tr -d '\n' мне известно.
Пример:
abc
;
fgh
;
"
qwe
rty
"
;
"
asd
"
;
end

В конвейере нужно превратить в:
abc;fgh;"qwe
rty";"asd";end\r

Другими словами, сделать нормальный CSV из вывода, где поля в отдельных строках,
разделитель полей ';' тоже в отдельной строке и поля, которые могут содержать \n, обрамлены
строками '"'. Последний \n надо заменить на \r\n
Для программы (на Си?) я бы  сформулировал задачу просто:
убрать все \n вне "...", а внутри "..." убрать первый сразу за '"'. (на Си писать
не надо, если "однострочник" не получится, то на нем сам как-нибудь напишу).
...
Пока замер на проблеме, что я никак не могу заставить работать что-нибудь типа 
s/\n;\n/;/

ни в sed, ни в perl (т.е. работать с выражением для 2-х строк)
P.S.
python 2.4 тоже подойдет.    


Ответы

Ответ 1



perl -0777 -lpe 'BEGIN{$\="\r\n"}s#\n;\n#;#gsm;s#;"\n#;"#gsm'

вторник, 25 февраля 2020 г.

Удаление знаков переноса с возвратом строки

#bash #sed


при выгрузке случился баг и после data3 добавился знак переноса строки \n
и все сьехало на две строки.  data3 обрамлен " (двойные ковычки)
файл очень большой 1 мил строк и в ручную не вариант переделывать
подскажите как через sed убрать перенос строки для того чтобы сьехавший хвост вытянулся
в одну строку
сейчас файл имеет такой вид

data1,data2,"data3
",data4
data1,data2,"data3
",data4
data1,data2,"data3
",data4


а должен быть таким 

data1,data2,"data3",data4
data1,data2,"data3",data4
data1,data2,"data3",data4


заранее спасибо 
    


Ответы

Ответ 1



Попробуйте sed 'N;s/\n"/"/' У меня вроде работает: [VladD@Kenga] [00:59:25] [~] {0,504}$> cat xx.txt data1,data2,"data3 ",data4 data1,data2,"data3 ",data4 data1,data2,"data3 ",data4 [VladD@Kenga] [00:59:32] [~] {0,505}$> sed 'N;s/\n"/"/' xx.txt data1,data2,"data3",data4 data1,data2,"data3",data4 data1,data2,"data3",data4 Для более сложных случаев (возможны «обыкновенные» строки) попробуйте так: sed '/^",/{H;x;s/\n//;x;d}; x' | sed '1d' Проверка: [VladD@Kenga] [01:35:47] [~] {0,539}$> cat xx.txt header "data1",data2,"data3 ",data4 intermediate data data1,"data2 ","data3 ",data4 data1,data2,"data3 ",data4 [VladD@Kenga] [01:35:52] [~] {0,540}$> sed '/^",/{H;x;s/\n//;x;d}; x' xx.txt header "data1",data2,"data3",data4 intermediate data data1,"data2","data3",data4 data1,data2,"data3",data4 [VladD@Kenga] [01:35:57] [~] {0,541}$> sed '/^",/{H;x;s/\n//;x;d}; x' xx.txt | sed '1d' header "data1",data2,"data3",data4 intermediate data data1,"data2","data3",data4 data1,data2,"data3",data4 Внимение: последняя строка должна заканчиваться переводом строки, иначе она будет «проглочена»! Объяснение: нам необходимо, когда мы видим строку, начинающуюся с кавычки, знать предыдущую строку, чтобы склеить их. Для этого мы «задерживаем» вывод строк, отправляя их в hold space вместо вывода, и выводя вместо этого предыдущую строку, лежащую там же (x). Для случая, когда строка начинается с кавычки (/^"/) начинаем действовать. В hold space лежит предыдущая строка, пристыковываем к ней текущую (H), и обмениваем hold space с pattern space (x), чтобы можно было обработать текст. Удаляем \n (s/\n//), и отправляем назад строку в hold space, чтобы проанализировать и вывести её на следующем цикле. Обрубок строки, который получился в pattern space, удаляем, и завершаем эту итерацию (d).

Ответ 2



если структура получившегося файла точно соответстует приведённому примеру (надо объдинить 1 и 2 строку, 3 и 4 и т.д.), то выражение можно упростить, примерно как в соседнем ответе: $ cat старый.файл | sed 'N;s/\n//' > новый.файл объяснение: для всех нечётных строк будет: прочитана следующая строка в конец pattern space из pattern space будет удалён символ перевода строки \n между этими двумя строками

Ответ 3



Мне проще написать такое на sh (или Си). Объединяем строки, если после заданного текста был вставлен \n : avp@avp-xub11:hashcode$ cat ts.sh #!/bin/sh IFS="" while read -r s1 do if echo $s1 | grep $1'$' >/dev/null ; then echo -n $s1 else echo $s1 fi done avp@avp-xub11:hashcode$ cat ttt header data1,data2,"data3 ",data4 intermediate data data1,"data2 ","data3 ",data4 data1,data2,"data3 ",data4 data1,"data3 " "data3 "data3 "data4 tailer avp@avp-xub11:hashcode$ ./ts.sh \"data3 < ttt header data1,data2,"data3",data4 intermediate data data1,"data2 ","data3",data4 data1,data2,"data3",data4 data1,"data3" "data3 "data3"data4 tailer avp@avp-xub11:hashcode$ IFS="" заставляет sh (или bash) не разбивать строку на слова, а ключ -r говорит read, что backslash это обычный символ (см. man 1 read).

Ответ 4



тут может попробуете команду tr ? tr '\n\",' ",' < input_filename sed хорошо, но советуют tr версия для sed sed ':a;N;$!ba;s/\",\n/\",/g' file :a создание метки 'a' N добавить следующую строку в формат $! если не конец строки переход в метку 'a' s substitute, /\",n/ regex для кавычки-запятой-новой_строки, /\",/ заменить с кавычки-запятой, /g замена глобальная (сколько раз есть, столько работай)

вторник, 28 января 2020 г.

Как узнать номер вхождения символа из регулярного выражения для заданной строки?

#регулярные_выражения #bash #sed


Как-то так:    

echo "абвгд" | sed 's/в/\1{фокус}/` # получим: aбв3гд


Т.е. буква "в" стоит третьей и этот то номер необходимо вычленить, это возможно?
    


Ответы

Ответ 1



Получилось как то так: echo "абвгд" | sed 's/\(.\)/\1\n/g' | sed -n 'p;/в/=' | sed -n 'H;${x;s/\n//g;p}' Первый sed разбивает строку на строки с отдельными символами. Второй - после строки с буквой "в" печатает ее номер. Третий - собирает символы обратно в строку. При наличии perl, это конечно было бы проще: echo "абвгд" | perl -Ca -pe 's/в/$&.(length($`)-1)/e'

Ответ 2



если делать одним вызовом интерпретатора sed, получается довольно многословно (но наверняка можно чуть-чуть сократить при острой необходимости). для подсчёта количества символов используется программа wc: $ echo "абвгд" | sed -r 's/в/&\n/;h;s/^/echo -n "/;s/\n.*/"|wc -m/e;G;s/^(.+)\n(.+)\n/\2\1/' небольшое пояснение по коду программы для интерпретатора sed: s/в/&\n/ — добавляем после искомого символа символ перевода строки \n. дальше мы будем использовать его как маркер h — сохраняем текущее содержимое pattern space (абв\nгд) в hold space (оно нам понадобится чуть позже) s/^/echo -n "/ — вставляем в начало pattern space строку echo -n " s/\n.*/"|wc -m/e: s/\n.*/"|wc -m/ — заменяем всё, начиная с символа \n (мы его добавили на первом шаге) до конца pattern space на строку "|wc -m. теперь в pattern space у нас получилось: echo -n "абв"|wc -m. если эту строку выполнить в оболочке, то она вернёт символ 3 (количество символов в строке абв) e — с помощью этого модификатора команды s мы как раз и передаём содержимое pattern space в оболочку, чтобы она выполнила это содержимое как команду. результат выполнения этой команды заменит текущее содержимое pattern space (и в pattern space у нас будет символ 3) G — эта команда добавляет в конец pattern space символ перевода строки, а затем содержимое hold space. в результате в patten space у нас получилась строка 3\nабв\nгд s/^(.+)\n(.+)\n/\2\1/ — выбираем всё до первого символа \n (т.е., строку 3) в первую группу, всё между первым и вторым символом \n (т.е., строку абв) — во вторую, а затем выводим группы в обратном порядке — сначала вторую, потом первую: абв, затем 3. а остаток строки (гд) остаётся нетронутым в результате получаем в pattern space строку абв3гд, что и требовалось.

Ответ 3



С использованием промежуточного "огорода" в виде awk: echo "abcde" | awk 'match($0, /d/) {print substr($0, 0, RSTART),RSTART,substr($0, RSTART+1, RLENGTH)}' | sed 's/ //g'

воскресенье, 12 января 2020 г.

Как удалить многострочный текст между тэгами?

#linux #регулярные_выражения #perl #sed


Как убрать весь многострочный текст между тэгами описания?

  
  Туалетная вода-спрей 
  <br>ВОСТОЧНЫЙ ФУЖЕР
  <br>Мужчина в стиле ориентирован на успех, целеустремлен и созидателен. Мужественность,
достоинство, уверенность – его главные черты,    наряду с чувственностью и романтичностью.
Он любит комфорт, красивые вещи,   роскошную жизнь и выражает себя в классическом пок$
 <br>Ключевые слова: Уверенный, динамичный, мужественный, благородный,  элегантный,
статусный, успешный



Вот этот вариант удаляет только те, которые в одной строке:

sed 's#\(\).*\(\)#\1'xxxxx'\2#g' test.xml > test2.xml


Александр Баракин предложил вот такой вариант:

$ sed '//,/<\/description>/{//!d}' test.xml > test2.xml


но текст не удаляется между тэгами в таком примере:
Пример
    


Ответы

Ответ 1



для данного конкретного случая, например, так: $ sed '//,/<\/description>/{//!d}' test.xml > test2.xml но лучше, действительно, воспользоваться более подходящим инструментом для работы с xml. если вдруг и строки-ограничители требуется убрать, то {//!d} надо заменить просто на d: $ sed '//,/<\/description>/d' test.xml > test2.xml дополнение для удаления текста (в той же строке) и после начального тега, и перед конечным, программу для sed, естественно, придётся значительно удлинить: $ sed '//,/<\/description>/{//!d;s/\(\).*/\1/;s/.*\(<\/description>\)/\1/}' test.xml > test2.xml поэтому ещё раз напомню, что для обработки xml лучше воспользоваться каким-нибудь более специализированным инструментом.

понедельник, 30 декабря 2019 г.

Закрыть в кавычки текст

#linux #парсер #sed


Есть файл на 100к строк с примерным содержимым:

285570,KW,,,"",0.0000,0.0000,,
285590,KW,KU,Kayfan,"",0.0000,0.0000,,
285603,KW,FA,"Janub as Surrah","",0.0000,0.0000,,
285629,KW,HA,Hawalli,"",0.0000,0.0000,,
285663,KW,HA,Bayan,"",0.0000,0.0000,,
285704,KW,HA,"As Salimiyah","",0.0000,0.0000,,
285713,KW,KU,"Ash Shuwaykh","",0.0000,0.0000,,
285716,KW,AH,"Ash Shu`aybah","",0.0000,0.0000,,
285726,KW,HA,"Ar Rumaythiyah","",0.0000,0.0000,,
285728,KW,AH,"Ar Riqqah","",0.0000,0.0000,,
285787,KW,KU,"Kuwait City","",0.0000,0.0000,,
285788,KW,KU,,"",0.0000,0.0000,,
285799,KW,JA,"Al Jahra","",0.0000,0.0000,,
285803,KW,JA,"Al Hujayjah","",0.0000,0.0000,,
285810,KW,MU,"Al Funaytis","",0.0000,0.0000,,
285811,KW,AH,"Al Fahahil","",0.0000,0.0000,,
285815,KW,FA,"Al Farwaniyah","",0.0000,0.0000,,
285839,KW,AH,"Al Ahmadi","",0.0000,0.0000,,
285855,KW,,"Ad Dawhah","",0.0000,0.0000,,
285856,KW,KU,"Ad Dasmah","",0.0000,0.0000,,
285866,KW,FA,"Abraq Khaytan","",0.0000,0.0000,,
286091,OM,MA,"Al `Udhaybah ash Shamaliyah","",0.0000,0.0000,,
286245,OM,SJ,Sur,"",0.0000,0.0000,,
286282,OM,BS,Sohar,"",0.0000,0.0000,,
286592,OM,SS,"Samad ash Sha'n","",0.0000,0.0000,,
286621,OM,ZU,Salalah,"",0.0000,0.0000,,
286647,OM,BS,Saham,"",0.0000,0.0000,,
286696,OM,MA,Ruwi,"",0.0000,0.0000,,
286726,OM,SS,"Ar Raddah","",0.0000,0.0000,,


Подскажите пожалуйста, как закрыть в кавычки символы между запятыми там, где их ещё
нет, кроме первого и трёх последних столбцов, чтоб получилось что-то типа такого: 285603,"KW","FA","Janub
as Surrah","",0.0000,0.0000,,

UPD

Так же в кавычках могут быть запятые и не ascii символы: 286091,OM,MA,"Al Udhaybah,
ash, Shamaliyah","",0.0000,0.0000,,
    


Ответы

Ответ 1



Если в кавычках нет запятых, то тогда можно через перловый однострочник cat test.txt | perl -F, -lane '@F=map{ m/^"/?"$_":"\"$_\""} @F; print join(",", @F);' UPD: Если хочется хорошо делать, то лучше использовать полноценный парсер. В репах убунту/дебиана (а наверно и других) точно есть пакет csvtool. С ним задача станет простой и легкой. csvtool format '"%1","%2","%3","%4","%5","%6","%7","%8","%9"\n' /tmp/test.txt Эта тула умеет ещё много чего - фильтровать, складывать вместе. В вопросе появилось, что нужно что бы в кавычки не брало первое и три последних. Это легко исправляется csvtool format '%1,"%2","%3","%4","%5","%6",%7,%8,%9\n' /tmp/test.txt

Ответ 2



awk -F ',' \ '{gsub("\"","");printf "%s,\"%s\",\"%s\",\"%s\",\"%s\",%s,%s,%s,%s\n",$1,$2,$3,$4,$5,$6,$7,$8,$9}' \ test.txt 1) Удаляем все кавычки 2) Форматируем как нужно UPD awk -F ',' \ '{gsub("\"","");printf "%s,\"%s\",\"%s\",\"",$1,$2,$3;for(i=4;i<=(NF-5);i++)printf "%s,", $i;printf "\b\",\"%s\",%s,%s,%s,%s\n",$(NF-4),$(NF-3),$(NF-2),$(NF-1),$NF}' \ test.txt Так вообще будет пофигу что у тебя в 4м поле UUPD awk -F ',|","|,"|",' \ '{printf "%s,\"%s\",\"%s\",\"",$1,$2,$3;for(i=4;i<=(NF-5);i++)printf "%s,", $i;printf "\b\",\"%s\",%s,%s,%s,%s\n",$(NF-4),$(NF-3),$(NF-2),$(NF-1),$NF}' \ test.txt А вот так даже сохранит кавычки внутри поля

Ответ 3



Или сложнее вариант, явно можно упростить, но это и вы можете посмотреть как: sed -E 's#^([0-9]+),([a-zA-Z ]+?),([a-zA-Z ]+?),([a-zA-Z ]+?)#\1,\"\2\",\"\3\",\"\4\"#;s#\"\"\"#\"#' test.txt

среда, 25 декабря 2019 г.

Как использовать перенос строки в левой части sed?

#linux #shell #sed


Отслеживаю логи через tailf (tail -f). Для удобства отображения хотелось бы после
каждой строки добавлять пустую строку.

Например: 

$ tailf /var/log/some.log
log1
log2
log3

$ tailf /var/log/some.log | magic
log1

log2

log3


Пробовал через sed двумя способами, но получаю то же без изменений. Идея в том, чтобы
поматчить перенос строки и заменить его на два переноса. Общий синтаксис такой: sed
's/substitute_this/to_this/g'

Способ 1: вставить перенос строки как $'\n'.

$ tailf /var/log/some.log | sed "s/$'\n'/test/g"
log1
log2
log3


Похоже, что $'\n' не матчится на переносы строк в моем логе.

Способ 2: вставить перенос строку как перенос строки при вводе команды:

$ tailf /var/log/some.log | sed "s/
> /test/g"
sed: -e expression #1, char 2: unterminated `s' command

$ tailf /var/log/some.log | sed "s/\
/test/g"
sed: -e expression #1, char 0: no previous regular expression


Если бы это был shell-скрипт, можно было бы использовать хак:

newline='
'


Но я-то хочу "на ходу" использовать. Сделать фукцию или alias тоже не подойдёт, т.к.
я работаю с большим количеством хостов и не имею возможности индивидуально настраивать
.bashrc.

Собственно, вопрос: что должно быть на месте magic?
    


Ответы

Ответ 1



Еще вариант с sed: tailf foo.log init.lxc 1455930731.917 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455930758.674 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986152.381 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986234.349 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy tailf foo.log | sed 'a\ ' init.lxc 1455930731.917 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455930758.674 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986152.381 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986234.349 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy

Ответ 2



вам надо в конце строки (а sed работает именно построчно) вставлять символ перевода строки \n. якорем (метасимволом, квантификатором), обозначающим конец строки, в большинстве диалектов регулярных выражений обычно служит символ $: $ tail -f /var/log/some.log | sed 's/$/\n/' для изоляции от «вмешательства» оболочки программу для sed лучше заключать в одинарные кавычки (да, s/$/\n/ — это именно программа, интерпретируемая программой sed).

воскресенье, 22 декабря 2019 г.

Удалить строки, содержащие подряд (или не подряд) три и более любые цифры (буквы)

#linux #perl #sed #grep #awk


Вопрос разбивается на несколько похожих, дабы не создавать каждый раз отдельно.
Используемые символы в строках: все цифры, все строчные и заглавные буквы английского
алфавита. Строки вида:

a22tbf645
92STbfF4W
92rtRe7Ev
gyue73Pr4
u8t9D03gE
a2t4TA6Kk
Lj3D2Jrs1


желаемый ответ после применения одновременно всего указанного ниже: Lj3D2Jrs1

1.a Удалить строки, содержащие на любых позициях три или более любые разные цифры,
идущие подряд.

1.b Удалить строки, содержащие на любых позициях три или более любые строчные буквы,
идущие подряд.

1.с Удалить строки, содержащие на любых позициях три или более любые заглавные буквы,
идущие подряд.

2.a Удалить строки, содержащие на любых позициях четыре или более любые цифры вообще
в строке.

2.b Удалить строки, содержащие на любых позициях четыре или более любые строчные
буквы вообще в строке.

2.c Удалить строки, содержащие  четыре или более любые заглавные буквы вообще в строке.


Удалить строки, содержащие на любых позициях одинаковые буквы подряд в разных регистрах,
например aA Aa


Можно ли объединить все эти условия в одну команду?
Предпочтительно sed, awk, grep, tr, cut, perl, python, может что-то еще - упор на
скорость обработки- большие массивы.(чем кстати побыстрее?)
спасибо
    


Ответы

Ответ 1



Решение с использованием простой машины состояний (детерминированного конечного автомата) было написано "в лоб" на Delphi, затем я его, как сумел, почти напрямую перевёл на Python, так что код громоздкий, явно не pythonic и производительность может страдать оттого, что я не использую какие-то встроенные штучки и не особо знаю, что влияет на производительность Python. Например, время сократилось с 41 до 33 секунд, когда я заменил выделение символа for i in range(len(line)): ch = line[i] на for ch in line: Для файла из 10 миллионов строк по 9 случайных символов (110 мегабайт), принадлежащих указанным множествам, нативный (Delphi) код под Windows на Celeron 2.8 ГГц затрачивает около 4-5 секунд (3-4 секунды на загрузку данных с HDD и около секунды на саму обработку). Данный код (Python 3.6) тратит на всё 33 секунды (из них 11 сек. на загрузку файла, если просто прочитать все строки и ничего не делать). Код проходит по каждой строке, считая количество больших, малых букв и цифр и прерывает обработку строки, если какой-либо из счётчиков превышает предел (3) (условия 2). state - это текущее состояние, обозначает, какого типа символ обрабатывался ранее. 0 - неопределённо, 1 - цифра, 2 - заглавная, 3 - маленькая буква. Если state не меняется, то проверяется длина серии seriescount из символов текущего типа (условия 1) Если state меняется с 2 на 3 или наоборот, то проверяется, не был ли прошлый символ копией текущего в другом регистре (условие 3). def dellines(): infile = open("d:\m1.txt", "r") outfile = open("d:\m2.txt", "w") for line in infile: seriescnt = 0 bigcnt = 0 smallcnt = 0 digitcnt = 0 state = 0 for ch in line: if (ch >= "0") and (ch <= "9"): if (state == 1): seriescnt += 1 if (seriescnt > 2): state = 0 break else: seriescnt = 1 digitcnt += 1 if (digitcnt > 3): state = 0 break state = 1 elif (ch >= "A") and (ch <= "Z"): if (state == 3): if (ord(lastch) == ord(ch) + 32): state = 0 break if (state == 2): seriescnt += 1 if (seriescnt > 2): state = 0 break else: seriescnt = 1 bigcnt += 1 if (bigcnt > 3): state = 0 break lastch = ch state = 2 elif (ch >= "a") and (ch <= "z"): if (state == 2): if (ord(lastch) == ord(ch) - 32): state = 0 break if (state == 3): seriescnt += 1 if (seriescnt > 2): state = 0 break else: seriescnt = 1 smallcnt += 1 if (smallcnt > 3): state = 0 break lastch = ch state = 3 if (state): outfile.write(line) outfile.close() infile.close() start = time.time() dellines() end = time.time() print(end - start)

Ответ 2



Вот (вероятно) малоэффективный и откровенно тупой, но рабочий вариант на классических регулярных выражениях: grep -v\ -e '[[:digit:]]\{3,\}'\ -e '[[:lower:]]\{3,\}'\ -e '[[:upper:]]\{3,\}'\ -e '\([[:digit:]].*\)\{4,\}'\ -e '\([[:lower:]].*\)\{4,\}'\ -e '\([[:upper:]].*\)\{4,\}'\ $(for i in {a..z}; do echo "-e $i${i^^} -e ${i^^}$i"; done)

Ответ 3



Версия на Python за основу взяты ответы из СО. Создаем для примера файл из случайной выборки 100М с заданным патерном из вопроса: [a-zA-Z0-9]{9} Характеристики машины: ~$ uname -r; grep -im1 "model name" /proc/cpuinfo 4.17.3-200.fc28.x86_64 model name : Intel(R) Core(TM) i7-3770S CPU @ 3.10GHz Исправленный вариант только на регулярных выражениях без выполнения условия 3 ~$ cat lines_filter_re.py #!/usr/bin/env python3 import sys import re import fileinput for line in fileinput.input(): pat_re = re.compile(r"""" ([0-9]){3} # Any 3 sequensial numbers |([a-z]){3} # Any 3 lsequensia lower case letters |([A-Z]){3} # Any 3 sequensia lupper case letters |(.*[0-9].*){4} # any 4 digits in any place |(.*[A-Z].*){4} # any 4 upper case ltters at any place |(.*[a-z].*){4} # any 4 loser case lttters at any place """, re.VERBOSE) if not pat_re.findall(line): sys.stdout.write(line) # end of script ~$ cat test.txt; echo; ./lines_filter_re.py < test.txt a22tbf645 92STbfF4W 92rtRe7Ev gyue73Pr4 u8t9D03gE a2t4TA6Kk aAb12cAB1 Lj3D2Jrs1 --------- a2t4TA6Kk aAb12cAB1 Lj3D2Jrs1 ~$ wc -l file_9w_100M 6180836 file_9w_100M ~$ time ./lines_filter_re.py >/dev/null < file_9w_100M real 1m10.668s user 1m10.424s sys 0m0.060s Метод на Rust, работает x15 раз быстрей: extern crate regex; use regex::RegexSet; use std::io::{stdin, BufRead}; fn main() { let set = RegexSet::new(&[ r"([0-9]){3}", r"([a-z]){3}", r"([A-Z]){3}", r"(.*[0-9].*){4}", r"(.*[A-Z].*){4}", r"(.*[a-z].*){4}", ]).unwrap(); let stdin = stdin(); let test_string = "Test me once"; println!("{}", set.is_match(test_string)); for line in stdin.lock().lines() { let mut line_str = line.unwrap(); if set.is_match(&mut line_str) { continue; } else { println!("{}", line_str); } } } // End of main.rs Тесты ~$ time ./lfr < ../../../../Python/test.txt true a2t4TA6Kk aAb12cAB1 Lj3D2Jrs1 real 0m0.004s user 0m0.002s sys 0m0.002s ~$ time ./lfr >/dev/null < ../../../../Python/file_9w_100M real 0m2.934s user 0m2.866s sys 0m0.062s Ссылки https://stackoverflow.com/questions/34443946 Как вырезать только строки, которые не имеют повторяющихся символов (cut/sed/awk) или Python?

пятница, 20 декабря 2019 г.

Найти отстутствующий в последовательности номер

#linux #bash #perl #sed #awk


После работы некоторого скрипта, в файл записывается примерно следующее:

1 ---> something
2 ---> something
3 ---> something
...
18254 --> something


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

Покажу на примере:

...
4200 ---> something
4201 ---> something
4202 ---> something
4203 ---> something
4204 ---> something
4206 ---> something


Вопрос: как проверить, какого номера строки (или просто, какого номера нет в файле)
нет в файле? И возможно ли?
    


Ответы

Ответ 1



Можно сделать как-то так: awk -v var="$((`head -n 1 file | awk '{print $1}'`))" 'BEGIN {p=var} {for(i=p+1; i<$1; i++) print i} {p=$1}' file Результат будет: 4205 Сначала мы забираем первое число, которое у нас есть в файле. Если этого не делать, то счет будет с 1. Просто у вас из вопроса не ясно, с какого числа начинается счет. Потом через awk мы просто проверяем последовательность чисел. И если числа нет, то выводим его на экран. Для случая, если ВСЕГДА последовательность чисел начинается с 1 awk '{for(i=p+1; i<$1; i++) print i} {p=$1}' file

Ответ 2



seq $(grep -oP '^\d+' input.txt | tail -1) | diff - <(grep -oP '^\d+' input.txt) | grep '<' Сперва находим последнюю пронумерованную строку (grep -oP '^\d+' input.txt | tail -1) Далее создаем последовательность без потери чисел (seq см.предыдущее) Сравниваем полученную последовательность с той, что имеется в файле (см. предыдущее | diff - <(grep -oP '^\d+' input.txt) Выводим отсутствующие числа (см. предыдущее | grep '<') Команду grep -oP '^\d+' input.txt можно вывести в переменную, чтобы не тратиться на повторное чтение данных из файла. В случае поиска отсутствия диапазонов чисел лучше использовать awk. Дополнение # заносим номера строк в переменную n=($(grep -oP '^\d+' input.txt)) # выводим различия целочисленных последовательностей echo $(seq ${n[-1]}) ${n[@]} | tr ' ' '\n' | sort -n | uniq -u

среда, 11 декабря 2019 г.

sed - несколько нестандартных задач по переделке строки

#linux #sed


Подскажите, как реализовать с помощью sed :


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

Foo bar, 38-popugaev.   >   F.o.o.b.a.r.,.3.8.-.p.o.p.u.g.a.e.v.

Foo bar, 38-popugaev.   >   F-o-o-b-a-r-,-3-8-p-o-p-u-g-a-e-v-.-
То же, но после последнего знака не должно быть как ни этого символа, так и ни какого
знака пунктуации:

Foo bar, 38-popugaev.   >   F.o.o.b.a.r.,.3.8.-.p.o.p.u.g.a.e.v

Foo bar, 38-popugaev.   >   F-o-o-b-a-r-,-3-8-p-o-p-u-g-a-e-v
Чередовать всю строку то заглавной буквой, то строчной, (как вариант наоборот), при
этом игнорируя регистры самой строки:

Foo bar, 38-popugaev.   >   FoO bAr, 38-PoPuGaEv.

Foo bar, 38-popugaev.   >   fOo BaR, 38-pOpUgAeV.
То же, но при этом каждое слово в строке должно начинаться с определенного регистра,
игнорируя строгую очередность все строки в пользу очередности каждого слова (первый
вариант - каждое слово должно начинаться с большой буквы, затем чередование, второй
вариант - с маленькой и затем чередование):

Foo bar, 38-popugaev.   >   FoO BaR, 38-PoPuGaEv.

Foo bar, 38-popugaev.   >   fOo bAr, 38-pOpUgAeV.


Можно и как отдельный скрипт каждый раз, чтобы запускать sed -f script.sed

Спасибо )
    


Ответы

Ответ 1



Тема достаточно старовата, но тоже поучаствую, 3 пункт: echo "Foo bar, 38-popugaev" | sed -r 's/([а-Яa-Z]{0,1}[^a-Zа-Я]{0,1})([а-Яa-Z]{0,1}[^a-Zа-Я]{0,1})/\U\1\L\2/g' FoO bAr, 38-PoPuGaEv echo "Foo bar, 38-popugaev" | sed -r 's/([а-Яa-Z]{0,1}[^a-Zа-Я]{0,1})([а-Яa-Z]{0,1}[^a-Zа-Я]{0,1})/\L\1\U\2/g' fOo BaR, 38-pOpUgAeV Четвертый пункт отличается от третьего пункта лишь пробелом в исключении: echo "Foo bar, 38-popugaev" | sed -r 's/([а-Яa-Z]{0,1}[^a-Zа-Я ]{0,1})([а-Яa-Z]{0,1}[^a-Zа-Я ]{0,1})/\U\1\L\2/g' FoO BaR, 38-pOpUgAeV echo "Foo bar, 38-popugaev" | sed -r 's/([а-Яa-Z]{0,1}[^a-Zа-Я ]{0,1})([а-Яa-Z]{0,1}[^ a-Zа-Я]{0,1})/\L\1\U\2/g' fOo bAr, 38-PoPuGaEv

Ответ 2



можно и так, например: #!/bin/bash s='Foo bar, 38-popugaev.' camel1='s/([[:alpha:]])([^[:alpha:]]*)([[:alpha:]])([^[:alpha:]]*)/\U\1\2\L\3\4/g' camel2='s/([[:alpha:]])([^[:alpha:]]*)([[:alpha:]])([^[:alpha:]]*)/\L\1\2\U\3\4/g' f1() { echo ${s} | sed -r "s/[ ${1}]+/${1}/g;s/./&${1}/g;s/\\${1}\\${1}//g" } f2() { f1 ${1} | sed 's/[[:punct:]]*$//' } f3() { echo ${s} | sed -r ${1} } f4() { echo ${s} | sed -r ":s;h;s/^(\W*)\n(.*)/\2\1/;to;s/^(\W*\w+).*/\1/;${1};H;g;s/^\W*\w+//;ts;bs;:o;s/\n//g" } echo 1 f1 "." f1 "-" echo 2 f2 "." f2 "-" echo 3 f3 ${camel1} f3 ${camel2} echo 4 f4 ${camel1} f4 ${camel2} вывод, например: 1 F.o.o.b.a.r.,.3.8.-.p.o.p.u.g.a.e.v. F-o-o-b-a-r-,-3-8-p-o-p-u-g-a-e-v-.- 2 F.o.o.b.a.r.,.3.8.-.p.o.p.u.g.a.e.v F-o-o-b-a-r-,-3-8-p-o-p-u-g-a-e-v 3 FoO bAr, 38-PoPuGaEv. fOo BaR, 38-pOpUgAeV. 4 Foo Bar, 38-PoPuGaEv. fOo bAr, 38-pOpUgAeV.

четверг, 6 июня 2019 г.

Как склеить строки ?

Вопрос в заголовке. Только не все строки. Про tr -d '
' мне известно. Пример: abc ; fgh ; " qwe rty " ; " asd " ; end В конвейере нужно превратить в: abc;fgh;"qwe rty";"asd";end
Другими словами, сделать нормальный CSV из вывода, где поля в отдельных строках, разделитель полей ';' тоже в отдельной строке и поля, которые могут содержать
, обрамлены строками '"'. Последний
надо заменить на
Для программы (на Си?) я бы сформулировал задачу просто: убрать все
вне "...", а внутри "..." убрать первый сразу за '"'. (на Си писать не надо, если "однострочник" не получится, то на нем сам как-нибудь напишу). ... Пока замер на проблеме, что я никак не могу заставить работать что-нибудь типа s/
;
/;/ ни в sed, ни в perl (т.е. работать с выражением для 2-х строк) P.S. python 2.4 тоже подойдет.


Ответ

perl -0777 -lpe 'BEGIN{$\="
"}s#
;
#;#gsm;s#;"
#;"#gsm'

понедельник, 13 мая 2019 г.

Удаление знаков переноса с возвратом строки

при выгрузке случился баг и после data3 добавился знак переноса строки
и все сьехало на две строки. data3 обрамлен " (двойные ковычки) файл очень большой 1 мил строк и в ручную не вариант переделывать подскажите как через sed убрать перенос строки для того чтобы сьехавший хвост вытянулся в одну строку сейчас файл имеет такой вид
data1,data2,"data3 ",data4 data1,data2,"data3 ",data4 data1,data2,"data3 ",data4
а должен быть таким
data1,data2,"data3",data4 data1,data2,"data3",data4 data1,data2,"data3",data4
заранее спасибо


Ответ

Попробуйте
sed 'N;s/
"/"/'
У меня вроде работает:
[VladD@Kenga] [00:59:25] [~] {0,504}$> cat xx.txt data1,data2,"data3 ",data4 data1,data2,"data3 ",data4 data1,data2,"data3 ",data4
[VladD@Kenga] [00:59:32] [~] {0,505}$> sed 'N;s/
"/"/' xx.txt data1,data2,"data3",data4 data1,data2,"data3",data4 data1,data2,"data3",data4
Для более сложных случаев (возможны «обыкновенные» строки) попробуйте так:
sed '/^",/{H;x;s/
//;x;d}; x' | sed '1d'
Проверка:
[VladD@Kenga] [01:35:47] [~] {0,539}$> cat xx.txt header "data1",data2,"data3 ",data4 intermediate data data1,"data2 ","data3 ",data4 data1,data2,"data3 ",data4
[VladD@Kenga] [01:35:52] [~] {0,540}$> sed '/^",/{H;x;s/
//;x;d}; x' xx.txt
header "data1",data2,"data3",data4 intermediate data data1,"data2","data3",data4 data1,data2,"data3",data4
[VladD@Kenga] [01:35:57] [~] {0,541}$> sed '/^",/{H;x;s/
//;x;d}; x' xx.txt | sed '1d' header "data1",data2,"data3",data4 intermediate data data1,"data2","data3",data4 data1,data2,"data3",data4
Внимение: последняя строка должна заканчиваться переводом строки, иначе она будет «проглочена»!

Объяснение: нам необходимо, когда мы видим строку, начинающуюся с кавычки, знать предыдущую строку, чтобы склеить их. Для этого мы «задерживаем» вывод строк, отправляя их в hold space вместо вывода, и выводя вместо этого предыдущую строку, лежащую там же (x).
Для случая, когда строка начинается с кавычки (/^"/) начинаем действовать. В hold space лежит предыдущая строка, пристыковываем к ней текущую (H), и обмениваем hold space с pattern space (x), чтобы можно было обработать текст. Удаляем
(s/
//), и отправляем назад строку в hold space, чтобы проанализировать и вывести её на следующем цикле. Обрубок строки, который получился в pattern space, удаляем, и завершаем эту итерацию (d).

четверг, 29 ноября 2018 г.

Как использовать перенос строки в левой части sed?

Отслеживаю логи через tailf (tail -f). Для удобства отображения хотелось бы после каждой строки добавлять пустую строку.
Например:
$ tailf /var/log/some.log log1 log2 log3
$ tailf /var/log/some.log | magic log1
log2
log3
Пробовал через sed двумя способами, но получаю то же без изменений. Идея в том, чтобы поматчить перенос строки и заменить его на два переноса. Общий синтаксис такой: sed 's/substitute_this/to_this/g'
Способ 1: вставить перенос строки как $'
'
$ tailf /var/log/some.log | sed "s/$'
'/test/g" log1 log2 log3
Похоже, что $'
' не матчится на переносы строк в моем логе.
Способ 2: вставить перенос строку как перенос строки при вводе команды:
$ tailf /var/log/some.log | sed "s/ > /test/g" sed: -e expression #1, char 2: unterminated `s' command
$ tailf /var/log/some.log | sed "s/\ /test/g" sed: -e expression #1, char 0: no previous regular expression
Если бы это был shell-скрипт, можно было бы использовать хак
newline=' '
Но я-то хочу "на ходу" использовать. Сделать фукцию или alias тоже не подойдёт, т.к. я работаю с большим количеством хостов и не имею возможности индивидуально настраивать .bashrc
Собственно, вопрос: что должно быть на месте magic?


Ответ

Еще вариант с sed:
tailf foo.log
init.lxc 1455930731.917 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455930758.674 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986152.381 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy init.lxc 1455986234.349 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy
tailf foo.log | sed 'a\ '
init.lxc 1455930731.917 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy
init.lxc 1455930758.674 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy
init.lxc 1455986152.381 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy
init.lxc 1455986234.349 ERROR lxc_initutils - initutils.c:mount_fs:36 - failed to mount /proc : Device or resource busy