Разделение нескольких полей на строку для разделения строк с помощью 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
краткое описание:
- внешний sed
sed -r 's@..x..@..y..@ge' file
наge
позволяет нам передать согласованную часть внешним командам - на
..y..
часть делается с помощью магииge
. Я пасв другой
sed
(viaecho
):sed "s# #\n #g"
этот sed заменяет все пространство на\n + + space
- в исходном файле, есть
\n
на каждой строке (окончание), поэтому есть пустые строки в результате шага 2 (выше шага), нам нужно удалить эти пустые строки"/^$/d"
- наконец, замена на шаге 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
вот однострочный (для некоторого определения "один"), который это делает. Он должен работать на любом sed, но я тестировал его только с gnu sed.
sed ':l;s/\(^\|\n\)\([^ \n]\) \([^ \n][^ \n]*\) / \
/;t l'
это буквальная новая строка после \
.
объяснение:
- буквальная новая строка может быть включена в замену, избегая ее обратной косой чертой.
- на
:l
делает метку под названиемl
. - на
t l
петли на этикеткеl
если подмена произведенный. - на
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 должно в конечном итоге дать вам необходимый формат.
Я знаю, что это, вероятно, не оптимальный ответ, я буду попробую уточнить, если это возможно.