По комментариям:
Что же, это замечательно, что мой вариант отработал, потому что у меня:
nmap localhost | grep 'report\|open' | sed 's#.*(\|/.*##g' | sed ':a;N;$!ba;s/\n/,/g'
Выдаёт:
,25,135,445,1001,24800
То есть ip адрес куда-то обрезается, с чем это связано, я так и не понял. Возможно как-то связано с тем, что я запускаю команды под WSL.
Но‥ раз у вас отработало, предлагаю и дальше работать с тем что получилось.
Решение
Изначально хотел использовать круглые скобки, по ним можно легко найти начало и конец ip адреса, но у вас выходит, что где-то скобки есть, где-то их нет. Абстрагируемся от скобок, изменим sed 's#.*(\|/.*##g'
на s#.*for \|/.*##g
, подойдёт для случаев, если у вас только ip адреса, а не хосты, и дополним постобработку так, чтобы было похоже на csv:
nmap -sT -iL servers.csv | grep 'report\|open' | sed 's#.*for \|/.*##g' | sed ':a;N;$!ba;s/\n/,/g' | sed 's/(\|)//g' | sed 's/\(\.[[:digit:]]*\),/\1\t/g' | sed 's/,\([[:digit:]]*\.\)/\n\1/g'
Вывод:
10.156.26.82 80
10.146.40.91 80,443
10.136.66.88 80,443
По пайпам пошагово:
grep 'report\|open'
Выбираем строки где содержится ip адрес и открытые порты
sed 's#.*for \|/.*##g'
Убираем весь текст до ip адреса и после портов (/
и далее)
sed ':a;N;$!ba;s/\n/,/g'
Убираем все переносы строки, при этом последний останется, не надо в конце делать && echo " "
, что удобно, подробнее про решение здесь на английском SO, перевод объяснения оттуда:
sed
начинается с чтения первой строки, исключая новую строку, в пространство шаблона.
- Создаётся метка через
:a
.
- Добавляется новая строка и следующая строка в пространство шаблона через
N
.
- Если мы находимся перед последней строкой, делаем ветку на созданную метку
$!ba
($!
означает не делать этого на последней строке. Это необходимо для того, чтобы избежать повторного выполнения N
, что приведет к завершению скрипта, если больше не будет ввода!).
- Наконец, подстановка заменяет каждую новую строку запятой на пространстве шаблона (которым является весь поток).
sed 's/(\|)//g'
Убираем (
и )
, в принципе, этот шаг можно объединить с пунктом 2: sed 's#.*for \|/.*\|(\|)##g'
, но оставлю как есть для читаемости.
Финальные штрихи, добавляем табуляцию и перенос где нужно:
sed 's/\(\.[[:digit:]]*\),/\1\t/g'
- Ищем точку (
\.
) после которой следуют числа и запятая, то есть конец ip адреса. Всё что до запятой берём в группу.
- В замене используем группу
\1
, а запятую меняем на табуляцию \t
sed 's/,\([[:digit:]]*\.\)/\n\1/g'
- Здесь примерно то же самое, только запятую меняем перед ip адресом и не на табуляцию, а на перенос строки.
Получилась очень длинная команда, что не очень хорошо. Если есть более элегантное решение с awk
, было бы приятно увидеть его в соседнем ответе.