Альтернативный подход awk
:
$ awk 'NR == 1{printf "%s", $0;next}{printf " %s\n%s", $1,$0}' input.txt
abc 123 abc
abc 789 bcd
bcd 456 acb
acb 135
Это работает просто: первая строка - это особый случай - мы печатаем ее без новой строки и говорим awk перейти к следующей строке без выполнения других блоков кода . После этого NR == 1 {printf "% s", $ 0; next}
пропускается, но другие части выполняют работу.
Помните, что до сих пор мы печатали отформатированную строку без символа новой строки. Таким образом, то, что делает printf "% s \ n% s", $ 1, $ 0
, теперь распечатывает первое слово (и поскольку не было новой строки, оно остается в той же строке вывода) , вставлена новая строка, а затем вся строка (но не заканчивается символом новой строки). Таким образом, следующее первое вставленное слово останется в той же строке. Процесс продолжается и продолжается, пока мы не дойдем до конца файла.
Возможное улучшение - включение блока END {print ""}
для вставки последней строки новой строки. В некоторых случаях, когда полученный файл должен обрабатываться другими скриптами, это может быть желательно.
Хотя пользователь специально запросил AWK, тот же подход с печатью форматированных строк может быть применен с другими языками, например Python. Альтернатива Python предоставлена для тех, кому интересно, как это может быть реализовано на других языках:
#!/usr/bin/env python
from __future__ import print_function
import sys
old = None
for index,line in enumerate(sys.stdin):
if index == 0:
print(line.strip(),end=" ")
continue
words = line.strip().split()
print(words[0] + "\n" + line.strip(),end=" ")
И использование примерно так:
$ ./append_first.py < input.txt
abc 123 abc
abc 789 bcd
bcd 456 acb
acb 135
Здесь применима та же идея о последней строке новой строки.
Подход awk:
$ awk -F'[/ ]' '{h=$2; for(i=1;i<=NF;i++){if($i=="open"){print h,$(i-1)}}}' file
10.0.0.99 135
10.0.0.99 139
10.0.0.99 445
10.0.0.99 554
10.0.0.99 10243
192.168.0.1 135
192.168.0.1 139
192.168.0.1 445
192.168.0.1 10243
Команда -F[/ ]
устанавливает разделитель поля ввода на /
или пробел. Это означает, что IP хоста будет $2
, а номер порта и слово "open" будут находиться в своих собственных полях. Поэтому мы можем перебрать все поля строки (for(i=1;i<=NF;i++){}
) и, если текущее поле open
, вывести имя хоста и предыдущее поле (порт): if($i=="open"){print h,$(i-1)}
.
Вы просили awk
, но это легко сделать с помощью Perl, поскольку он позволяет легко перебирать несколько совпадений regex.
$ perl -ne '$host = $1 if /^Host: (\S+)/; while(m,(\d+)/open,g)
{ print "$host\t$1\n" } ' < nmap.out
10.0.0.99 135
10.0.0.99 139
...
perl -ne # Run the code for each input line
'$host = $1 if /^Host: (\S+)/ # If the line starts with "Host: ",
# save the next non-whitespace string
while( m,(\d+)/open,g ) { # Go through all matches of `<digits>/open`
# on the line, catching the digits
print "$host\t$1\n" # Print the saved hostname and the matched digits.
}'
Я не думаю, что смог ясно изложить здесь свою точку зрения. Что Я хочу подчеркнуть, что вместо того, чтобы иметь явный цикл, мы можем
совмещать
регулярное выражение m//, чтобы сделать цикл для нас, ниже приведено реструктурированное регулярное выражение для большей ясности:
perl -lne '
/^
Host: \s+ (\S+) \s+ # stuff $1 with hostname
.+?: # seek the nearest colon :
(?: # setup a loop
\s+(\d+)\/open\/\S+ # this pattern should repeat
(?{ print "$1\t$2" }) # print hostname+digits foreach turn
)* # (?{ CODE }) does a code execution during regex search
/x;
' yourfile