#awk #sed #grep
Вопрос:
Мне нужно определить заголовок списка записей конфигурации в файле. Там не предсказуемо, это может быть любая строка, но это всегда будет строка, которая начинается ближе слева, чем другие (исключая «выход»).:
Вот пример:
vpls 2662 customer 1 v-vpls vlan 2662 create
description "RES_2662"
mac-move
allow-res-res
allow-reg-res
exit
stp
shutdown
exit
ingress
qos 2
exit
sap lt:1/1/1:2662 create
description "RES_2662"
enable-stats
no shutdown
exit
sap lag-1:2662 create
no shutdown
exit
no shutdown
exit
vpls 2663 customer 1 v-vpls vlan 2663 create
description "RES_2663"
mac-move
allow-res-res
allow-reg-res
exit
stp
shutdown
exit
ingress
qos 2
exit
sap lt:1/1/1:2663 create
description "RES_2663"
enable-stats
no shutdown
exit
sap lag-1:2663 create
no shutdown
В этом случае мне нужно уметь идентифицировать две строки, которые начинаются с:
vpls 266X customer 1 v-vpls vlan 266X create
Сценарий должен знать, что это те строки, которые я ищу.
На выходе не всегда будут отображаться пробелы слева, как в этом примере:
port vlan-port:1/1/1/3/7/4/4:824
admin-up
severity no-value
exit
port vlan-port:1/1/1/3/7/4/4:1224
admin-up
severity no-value
exit
В этом случае желаемыми строками являются:
port vlan-port:x/x/x/x/x/x/x/x
Я не знаю, можно ли это сделать с помощью grep/sed/awk.
Спасибо за вашу помощь.
Ответ №1:
Следующее будет работать с использованием любого awk в любой оболочке на каждом блоке Unix и сохранит порядок строк ввода для вывода, если это имеет значение:
$ cat tst.awk
$1 != "exit" {
match($0,/^ */)
if ( (min == "") || (RLENGTH <= min) ) {
min = RLENGTH
lines[min, cnt[min]] = $0
}
}
END {
for (i=1; i<=cnt[min]; i ) {
print lines[min,i]
}
}
$ awk -f tst.awk file
vpls 2662 customer 1 v-vpls vlan 2662 create
vpls 2663 customer 1 v-vpls vlan 2663 create
Ответ №2:
Я подозреваю, что есть лучшие способы сделать это, но моей первой мыслью было следующее. Возможно, вы могли бы начать с чего-то подобного и улучшить его.
minl=$(awk '{match($0, /^ */);if (NR==1 || RLENGTH<minl) {minl=RLENGTH}} END{print minl}' test.txt)
sed -n "/^[ ]{${minl}}[^ ]/p" test.txt | grep -v "exit"
Первая строка используется awk
для получения минимального количества пробелов в начале строки для файла.
Вторая строка используется sed
для сопоставления строк, начинающихся с количества пробелов, вычисленных в первой строке. Я передал результат этого по grep -v "exit"
каналу, чтобы избавиться от выходных линий… возможно, вам потребуется более строгая проверка, может ли допустимая строка вывода содержать текст «выход».
Ответ №3:
Еще одно возможное решение
# gets the number of leading spaces 1
n=$(sort -r file.txt | sed -nE '1s/(^ *).*/1/p' | wc -c | tr -d ' ')
# filter the file
egrep -vE "^ {$n,}|^ *exit" file.txt
Комментарии:
1. Это хорошо работает с первым примером, но не со вторым…
2. для меня это производит
port vlan-port:1/1/1/3/7/4/4:824 port vlan-port:1/1/1/3/7/4/4:1224
то, что кажется ожидаемым, каков ваш результат?
Ответ №4:
Допущения:
- начальный пробел состоит исключительно из пробелов (т. е. Без вкладок, без непечатаемых символов).
Одна awk
идея, в которой мы сохраняем массив этих строк с (текущим) минимальным количеством начальных пробелов, сбрасывая массив всякий раз, когда мы находим строку с меньшим количеством (т. Е. «новым» минимальным) начальных пробелов:
awk '
BEGIN { min = 9999999 }
/^$/ { exit } # skip blank lines
/exit/ { if {NF==1) next } # skip lines with single field "exit"
{ n = match($0,/[^ ]/) # find index of first non-space
if ( n < min ) { # if a new minimum is found then ...
delete arr # delete the array and ...
i = 1 # reset the array index and ...
min = n # reset the min
}
if ( n == min ) # if current row matches with "min" then ...
arr[i ] = $0 # save the row in our array; increment the index
}
END { for (j=1;j<i;j ) # loop through entries in array
print arr[j]
}
' file.dat
Для 1-го набора данных OP это генерирует:
vpls 2662 customer 1 v-vpls vlan 2662 create
vpls 2663 customer 1 v-vpls vlan 2663 create
Для 2-го набора данных OP это генерирует:
port vlan-port:1/1/1/3/7/4/4:824
port vlan-port:1/1/1/3/7/4/4:1224
Ответ №5:
Попробуйте это perl
и awk
комбинацию:
$ perl -ne ' /(^.s*)/ and !/^s*exit/ and print length($1), $_ ' fernando.txt | sort -n | awk ' { f=$1; p=NR==1?f:p; sub(/^[0-9] /,"",$0);if(f==p) print } '
vpls 2662 customer 1 v-vpls vlan 2662 create
vpls 2663 customer 1 v-vpls vlan 2663 create