Общепринятым соглашением является использование всего верхнего регистра для переменных окружения и всего нижнего регистра для локальных переменных скрипта. Таким образом, локальная переменная никогда не столкнется с переменной окружения.
Я не думаю, что в 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)
Предполагая, что это связано с вашим предыдущим вопросом , вы идете по неверному пути. Вместо того, чтобы пытаться собрать кусочки скриптов, которые будут делать то, что вы хотите большую часть времени, и получать совершенно другой скрипт каждый раз, когда вам нужно сделать что-то хоть немного отличающееся, просто создайте 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
С помощью 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
Использование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
Если вы используете 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
Еще одно grep
решение:
grep -o '[^=/]\+/[^ ]\+' file
И аналогичный с sed
печатью только совпадающей захваченной группы:
sed -n 's/.*=\([^/]\+\/[^ ]\+\).*/\1/p' file
Вот еще одно довольно простое решение:
grep -o "[tc,ud]*p\\/.* " INPUTFile.txt | awk '{print $1}'
Другой 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=
.
cat file| cut -f1 -d' '| cut -f2 -d'='
tcp/http
tcp/https
udp/dns
параметры резки:
-f
-поле -d
-разделитель