Разделение нескольких полей на строку для разделения строк с помощью sed, сохраняя префикс строки

в прошлую пятницу у меня возникла проблема, чтобы преобразовать текст в другой формат. На этой машине доступен только gnu sed, нет awk (странно, я знаю). И я ничего не знаю о Перл. поэтому я ищу только решение sed.

содержимое файла:

a  yao.com sina.com
b  kongu.com
c  polm.com unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com
d  kinge.net

требуемым выходом (должен быть новый файл) является:

a  yao.com 
a  sina.com
b  kongu.com
c  polm.com 
c  unee.net 
c  21cn.com 
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

Я много пробовал, также искал знаменитый sed oneliner, но я не могу этого сделать... кто-нибудь может мне помочь?

8 ответов


это нелегкая работа для sed, в частности, один лайнер. однако вы упомянули "gnu sed". Я вижу свет!

gnu sed поддерживает s/.../.../ge что полезно для этой ситуации:

kent$  sed -r 's@(^[a-z]+) (.*)@echo ""\|sed "s# #\n  #g"\|sed "/^$/d"@ge' file  
a  yao.com
a  sina.com
b  kongu.com
c  polm.com
c  unee.net
c  21cn.com
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

краткое описание:

  1. внешний sed sed -r 's@..x..@..y..@ge' file на ge позволяет нам передать согласованную часть внешним командам
  2. на ..y.. часть делается с помощью магии ge. Я пас в другой sed (via echo):sed "s# #\n #g" этот sed заменяет все пространство на \n + + space
  3. в исходном файле, есть \n на каждой строке (окончание), поэтому есть пустые строки в результате шага 2 (выше шага), нам нужно удалить эти пустые строки "/^$/d"
  4. наконец, замена на шаге 1 (внешний sed) может быть выполнена, и мы получим результат.

Регистрация info sed на s/../../ge

edit, добавлены двойные пробелы, как прокомментировал OP.


интересные задачи:

$ sed -r 's/(\w+\.\w+)/>  &/2g;:a s/^([a-z]+)(.*)>/\n/g;ta' file
a  yao.com 
a  sina.com
b  kongu.com
c  polm.com 
c  unee.net 
c  21cn.com 
c  iop.com 
c  foo.com 
c  bar.com 
c  baz.net 
c  happy2all.com
d  kinge.net

Edit:

он работает, используя две замены.

первый ставит > перед URL-адресами, которые нужно сгладить как символ удержания:

$ sed -r 's/(\w+\.\w+)/>  &/2g' file
a  yao.com >  sina.com
b  kongu.com
c  polm.com >  unee.net >  21cn.com >  iop.com >  foo.com >  bar.com ...
d  kinge.net

второй в основном заменяет холдинг > С новой строки (использует условное ветвление):

$ sed -r ':a s/^([a-z]+)(.*)>/\n/g;ta'

Как отмечали другие, решение sed сложно, поэтому я думал, что отправлю bash-dito:

#!/bin/bash

while read -a array
do
    for i in ${array[@]:1}
    do
        echo ${array[0]} $i
    done
done < input

выход:

a yao.com
a sina.com
b kongu.com
c polm.com
c unee.net
c 21cn.com
c iop.com
c foo.com
c bar.com
c baz.net
c happy2all.com
d kinge.net

Это может сработать для вас (GNU sed):

sed -r 's/^((\S+\s+)\S+)\s+/\n/;P;D' file

вот однострочный (для некоторого определения "один"), который это делает. Он должен работать на любом sed, но я тестировал его только с gnu sed.

sed ':l;s/\(^\|\n\)\([^ \n]\)  \([^ \n][^ \n]*\) /  \ 
  /;t l'

это буквальная новая строка после \.

объяснение:

  1. буквальная новая строка может быть включена в замену, избегая ее обратной косой чертой.
  2. на :l делает метку под названием l.
  3. на t l петли на этикетке l если подмена произведенный.
  4. на s команда работает с буфером пространства шаблонов, который изначально содержит входную строку. После s команда, буфер пространства шаблонов содержит результат подстановки, включая новую строку. Второй и последующие разы через цикл,s команда получает весь буфер пространства шаблонов, включая все новые строки, добавленные в более ранние подстановки.

cat inputFile.txt | sed -e 's/\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)\([^\ ]*\)\(\ *\)/ \n \n \n /' | grep -vE "^..$"

работает на моем Ubuntu 12.10.

объяснение:

  • делит его на 2 группы: Группа С текстом и группа с пустыми символами
  • повторяет группу 1 (с первым символом) и даже группы (с текстом)
  • в настоящее время работает для 4 текстов, разделенных пустыми символами

наконец, удаляет строки, содержащие пустую" вторую " группу.

еще одна попытка с BASH (выполнить как "script.sh inputFile.txt"):

#!/bin/bash

firstParams=`cat  | sed -e 's/\([^\ ]*\)\(.*\)//'`
count=1
for MY1 in $firstParams
do
    # print line number ${count} and filter params from the second one forth
    restParams=`cat  | sed -n "${count}p" | sed -e 's/\([^\ ]*\)\(.*\)//'`
    for MY2 in $restParams
    do
        echo "$MY1 $MY2"
    done
    count=$(($count+1))
done

вот истинный сценарий sed-only, который работает. Я написал его ниже как файл, который вызывается sed в командной строке, но все это может быть набрано в командной строке или введено в отдельный скрипт:

сохраните следующее Как sedscript (или как вы хотите его назвать). Объяснение следует за выводом.

:start
    h
    s/\(.\ \ [^ ]*\).*//
    t continue
    d
:continue
    p
    x
    s/\(.\ \)\ [^ ]*\(\ .*\)//
    t start
    d

теперь бегите sed -f sedscript myfile.txt

с вашим примером выше, сохраненным как myfile.txt, следующее вывод:

a  yao.com
a  sina.com
b  kongu.com
c  polm.com
c  unee.net
c  21cn.com
c  iop.com
c  foo.com
c  bar.com
c  baz.net
c  happy2all.com
d  kinge.net

Sed имеет буфер шаблонов (где вы обычно работаете с s/a/b/ виды команд) и удержать буфер. В этом сценарии информация обменивается взад и вперед в буфер хранения, чтобы сохранить неотредактированную часть строки во время работы над другой частью.

:start = метка для включения прыжков

h = замените буфер шаблона (текущая строка) в буфер удержания

s/\(.\ \ [^ ]*\).*// = пока полная линия безопасна в трюме буфер, очистите все после первого домена, оставив первую желаемую строку (например, "a yao.com").

t continue = если предыдущая команда привела к подстановке, перейдите к метке "продолжить"

d = если мы не прыгнули, значит, мы закончили. Удалите буфер шаблонов и перейдите к следующей строке файла.

:continue = метка для предыдущего прыжка

p = распечатать буфер шаблонов (например, " a yao.com")

x = замените буфер шаблона буфером удержания (также можно использовать g просто скопировать буфер удержания поверх буфера шаблона)

s/\(.\ \)\ [^ ]*\(\ .*\)// = полная исходная строка теперь заменена на буферную полосу шаблона с домена, с которым мы только что имели дело (например, "yao.com")

t start = если это был не последний домен, запустите сценарий с новой, сокращенной строкой.

d = если это был последний домен, удалить буфер шаблона и перейдите к следующей строке в файле.


можно использовать

sed -r -n 's/^([a-z])\ \ ([0-9a-z.]*)\ ([0-9a-z .]*)/  \n  /p'

он преобразует каждую строку в форме

c  polm.com unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com

на

c  polm.com
c  unee.net 21cn.com iop.com foo.com bar.com baz.net happy2all.com

каждый раз, когда он запускается.

поэтому в следующий раз, когда он будет запущен на выходе предыдущего sed, это станет

c  polm.com
c  unee.net
c  21cn.com iop.com foo.com bar.com baz.net happy2all.com

и так далее.

таким образом, нажатие вывода предыдущего sed в новый sed должно в конечном итоге дать вам необходимый формат.

Я знаю, что это, вероятно, не оптимальный ответ, я буду попробую уточнить, если это возможно.