Grep Сопоставление и извлечение

Общепринятым соглашением является использование всего верхнего регистра для переменных окружения и всего нижнего регистра для локальных переменных скрипта. Таким образом, локальная переменная никогда не столкнется с переменной окружения.

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

run () {
  typeset -A _saved_variables
  _saved_variables[foo]=$foo _saved_variables[bar]=$bar
  local foo='not seen' bar='not seen either'
  …
  foo=$_saved_variables[foo] bar=$_saved_variables[bar] bash print.sh
}

Если вы хотите сохранить всю среду, вы можете выполнить typeset -px, чтобы распечатать ее в пригодном для разбора виде. Обратите внимание, что это не сработает, если вы экспортировали переменные, доступные только для чтения; в этом случае вам придется перебрать имена переменных и выбрать только те, которые не доступны только для чтения.

run () {
  _saved_variables=`typeset -px`
  local foo='not seen' bar='not seen either'
  …
  (eval $_saved_variables; exec bash print.sh)
}

Другой вариант - по-другому структурировать сценарий. Определите команду, которую вы хотите выполнить во вложенной оболочке, и выведите ее (соответствующим образом заключив в кавычки) для выполнения родительской оболочкой.

run () {
  # you can clobber variables here
  …
  printf %q "bash print.sh"
}
eval $(run)
10
08.06.2019, 19:29
10 ответов

Предполагая, что это связано с вашим предыдущим вопросом , вы идете по неверному пути. Вместо того, чтобы пытаться собрать кусочки скриптов, которые будут делать то, что вы хотите большую часть времени, и получать совершенно другой скрипт каждый раз, когда вам нужно сделать что-то хоть немного отличающееся, просто создайте 1 скрипт, который может анализировать ваш входной файл в массив(f[]ниже ), который сопоставляет ваши имена полей (теги )с их значениями, а затем вы можете делать с результатом все, что хотите, например учитывая этот входной файл из вашего предыдущего вопроса:

$ cat file
Feb             3       0:18:51 17.1.1.1                      id=firewall     sn=qasasdasd "time=""2018-02-03"     22:47:55        "UTC""" fw=111.111.111.111       pri=6    c=2644        m=88    "msg=""Connection"      "Opened"""      app=2   n=2437       src=12.1.1.11:49894:X0       dst=4.2.2.2:53:X1       dstMac=42:16:1b:af:8e:e1        proto=udp/dns   sent=83 "rule=""5"      "(LAN->WAN)"""

мы можем написать awk-скрипт, который создает массив значений, проиндексированных по их именам/тегам:

$ cat tst.awk
{
    f["hdDate"] = $1 " " $2
    f["hdTime"] = $3
    f["hdIp"]   = $4
    sub(/^([^[:space:]]+[[:space:]]+){4}/,"")

    while ( match($0,/[^[:space:]]+="?/) ) {
        if ( tag != "" ) {
            val = substr($0,1,RSTART-1)
            gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
            f[tag] = val
        }

        tag = substr($0,RSTART,RLENGTH-1)
        gsub(/^"|="?$/,"",tag)

        $0 = substr($0,RSTART+RLENGTH)
    }

    val = $0
    gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
    f[tag] = val
}

и, учитывая, что вы можете делать со своими данными все, что хотите, просто ссылайтесь на них по именам полей, например. использование GNU awk для -eдля облегчения смешивания скрипта в файле с командным -строковым скриптом:

$ awk -f tst.awk -e '{for (tag in f) printf "f[%s]=%s\n", tag, f[tag]}' file
f[fw]=111.111.111.111
f[dst]=4.2.2.2:53:X1
f[sn]=qasasdasd
f[hdTime]=0:18:51
f[sent]=83
f[m]=88
f[hdDate]=Feb 3
f[n]=2437
f[app]=2
f[hdIp]=17.1.1.1
f[src]=12.1.1.11:49894:X0
f[c]=2644
f[dstMac]=42:16:1b:af:8e:e1
f[msg]="Connection"      "Opened"
f[rule]="5"      "(LAN->WAN)"
f[proto]=udp/dns
f[id]=firewall
f[time]="2018-02-03"     22:47:55        "UTC"
f[pri]=6

$ awk -f tst.awk -e '{print f["proto"]}' file
udp/dns

$ awk -f tst.awk -e 'f["proto"] ~ /udp/ {print f["sent"], f["src"]}' file
83 12.1.1.11:49894:X0
1
27.01.2020, 19:59

С помощью grep -oвам нужно точно сопоставить то, что вы хотите извлечь. Поскольку вы не хотите извлекать строку proto=, вы не должны сопоставлять ее.

Расширенное регулярное выражение, которое будет соответствовать либо tcp, либо udp, за которым следует косая черта и некоторая не-пустая буквенно-цифровая строка, будет

(tcp|udp)/[[:alnum:]]+

Применение этого к вашим данным:

$ grep -E -o '(tcp|udp)/[[:alnum:]]+' file
tcp/http
tcp/https
udp/dns

Чтобы убедиться, что мы делаем это только для строк, начинающихся со строкиproto=:

grep '^proto=' file | grep -E -o '(tcp|udp)/[[:alnum:]]+'

С помощью sedудаление всего до первого =и после первого пробела:

$ sed 's/^[^=]*=//; s/[[:blank:]].*//' file
tcp/http
tcp/https
udp/dns

Чтобы убедиться, что мы делаем это только для строк, начинающихся со строки proto=, вы можете вставить тот же шаг предварительной -обработки с grep, что и выше, или вы можете использовать

sed -n '/^proto=/{ s/^[^=]*=//; s/[[:blank:]].*//; p; }' file

Здесь мы подавляем вывод по умолчанию с помощью параметра -n, а затем запускаем подстановки и явный вывод строки только в том случае, если строка соответствует ^proto=.


С помощью awk, используя разделитель полей по умолчанию, а затем разбивая первое поле на =и печатая второй его бит:

$ awk '{ split($1, a, "="); print a[2] }' file
tcp/http
tcp/https
udp/dns

Чтобы убедиться, что мы делаем это только в строках, начинающихся со строки proto=, вы можете вставить тот же шаг предварительной -обработки с grep, что и выше, или вы можете использовать

awk '/^proto=/ { split($1, a, "="); print a[2] }' file
13
27.01.2020, 19:59

Использованиеawk:

awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input

$1 ~ "proto"гарантирует, что мы предпримем действия только со строками с protoв первом столбце

sub(/proto=/, "")удалит proto=из ввода

.

print $1распечатывает оставшийся столбец


$ awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input
tcp/http
tcp/https
udp/dns
6
27.01.2020, 19:59

Если вы используете GNU grep (для опции -P), вы можете использовать:

$ grep -oP 'proto=\K[^ ]*' file
tcp/http
tcp/https
udp/dns

Здесь мы сопоставляем строку proto=, чтобы убедиться, что извлекаем правильный столбец, но затем отбрасываем ее из вывода с флагом \K.

Вышеприведенное предполагает, что столбцы разделены -пробелом. Если табуляция также является допустимым разделителем, вы должны использовать \Sдля соответствия не -пробельным символам, поэтому команда будет:

grep -oP 'proto=\K\S*' file

Если вы также хотите защитить поля соответствия, где proto=является подстрокой, например thisisnotaproto=tcp/https, вы можете добавить границу слова с помощью \bследующим образом:

grep -oP '\bproto=\K\S*' file
10
27.01.2020, 19:59

Использование команды cut:

cut -b 7-15 foo.txt
3
27.01.2020, 19:59

Еще одно grepрешение:

grep -o '[^=/]\+/[^ ]\+' file

И аналогичный с sedпечатью только совпадающей захваченной группы:

sed -n 's/.*=\([^/]\+\/[^ ]\+\).*/\1/p' file
2
27.01.2020, 19:59
awk '{print $1}' filename|awk -F "=" '{print $NF}'
0
27.01.2020, 19:59

Вот еще одно довольно простое решение:

grep -o "[tc,ud]*p\\/.*  "   INPUTFile.txt  |   awk '{print $1}'
1
27.01.2020, 19:59

Другой awkподход:

$ awk -F'[= ]' '/=(tc|ud)p/{print $2}' file
tcp/http
tcp/https
udp/dns

Это установит разделитель полей awk либо на =, либо на пробел. Затем, если строка соответствует =, то либо ud, либо tc, за которыми следует p, выведите второе поле.

Другой sedподход (не переносим на все версии sed, но работает с GNUsed):

$ sed -En 's/^proto=(\S+).*/\1/p' file 
tcp/http
tcp/https
udp/dns

-nозначает «не печатать», а -Eвключает расширенные регулярные выражения, которые дают нам \Sдля «не -пробелов», +для «одного или нескольких» и круглых скобок. для захвата. Наконец, /pв конце заставит sed печатать строку только в том случае, если операция прошла успешно, то есть если было совпадение для оператора подстановки.

И жемчуг:

$ perl -nle '/^proto=(\S+)/ && print $1' file 
tcp/http
tcp/https
udp/dns

-nозначает «прочитать входной файл построчно и применить сценарий, заданный -e, к каждой строке». -lдобавляет новую строку к каждому printвызову (и удаляет выходящие новые строки из ввода ). Сам скрипт напечатает самый длинный фрагмент не пробельных символов -, найденный после proto=.

1
27.01.2020, 19:59
cat file| cut -f1 -d' '| cut -f2 -d'='
tcp/http
tcp/https
udp/dns

параметры резки:

  • -f-поле
  • -d-разделитель
0
27.01.2020, 19:59

Теги

Похожие вопросы