Я не могу определить правильный FPAT для получения каждого значения, разделенного запятыми. Использование bash в Debian 12.5... У меня есть файл PING.cfg с содержимым:
NODE01 = "router,10.0.0.1,5"
NODE02 = "dns1,68.6.16.30,35"
NODE03 = "dns2,1.1.1.1,35"
Они используются как переменные, полученные в нескольких сценариях. Я хочу, чтобы сценарий присваивал каждое из значений, разделенных запятыми, переменной в цикле for, может быть, что-то вроде этого:
for n in `grep NODE PING.cfg`
do
NODENAME=$(echo "$n" | awk -v FPAT='"[^"]+"' -F',' '{ print $1 }')
HOSTIP=$(echo "$n" | awk -v FPAT='"[^"]+"' -F',' '{ print $2 }')
echo "NODE is $NODENAME and HOSTIP is $HOSTIP"
done
Но awk, похоже, не читает кавычки с моим шаблоном FPAT, вывод для $1 включает всю переменную:
NODE is NODE01 = "router and HOSTIP is 10.0.0.1
Попробовал FPAT указать котировки.
Вы уверены, что используете GNU awk? I FPAT — это расширение GNU.
Однако как вы ожидаете, что ваш код будет работать с FPAT? Узор соответствует всей "dns1,68.6.16.30,35"
части линии. В каждой строке только один из них, так как же вы ожидаете, что $1
и $2
разделят хост и IP?
Похоже, вам действительно нужно извлечь часть строки между кавычками, а затем использовать split()
, чтобы разделить ее запятыми.
GNU Awk 3.1.5 и, надеюсь, mawk 1.3.4
Оба они устарели буквально лет на 20, и ни один из них не поддерживает FPAT (представленный в gawk 4.0). Можете ли вы обновить свою версию GNU awk? В настоящее время мы работаем над версией 5.3.0, которая содержит множество новых функций и исправлений ошибок.
@Allan Wind В файле есть еще какое-то случайное содержимое, помимо того, что показано, которое я хочу игнорировать в вводимых мной переменных. Не знаю, как смешать это при чтении всего файла.
Если ваш реальный ввод содержит случайный контент, который вы хотите игнорировать, обязательно включите такой случайный контент в образец ввода, чтобы в конечном итоге вы не получили решение неправильной проблемы.
Возможно, вместо этого обновите этот файл конфигурации, чтобы использовать формат, который просто и естественно использовать в Bash и Awk. Исправление других скриптов, которые уже используют этот файл, является разовой задачей и, возможно, также упростит их.
Я не думаю, что вы используете версию awk
, которая поддерживает FPAT
. Но даже если бы это было так, это не сделало бы того, что вы, кажется, ожидаете.
Вместо этого начните с использования кавычек в качестве разделителей полей. Тогда строка dns1,68.6.16.30,35
будет полем 2. Затем вы можете использовать split()
, чтобы разделить ее на разделители-запятые.
NODENAME=$(echo "$n" | awk -F'"' '{ split($2, a, /,/); print a[1] }')
HOSTIP=$(echo "$n" | awk -F'"' '{ split($2, a, /,/); print a[2] }')
Это решение не требует каких-либо расширений GNU, все это функции POSIX.
Портативность предпочтительна, поэтому ваше мнение о версии awk принято к сведению. Я готов переформатировать файл, но это многоцелевой файл, который мне также нужно использовать в других сценариях, чтобы использовать их в качестве переменных, поэтому цитирование каждого отдельного поля может испортить ситуацию. Моя цель — иметь возможность получать dns1 как одну переменную, а IP-адрес — как другую переменную.
Я никогда не говорил ничего, что предлагало бы переформатировать файл.
Я обновил ответ, чтобы показать, как назначать переменные оболочки в вашем скрипте, используя мою технику, вместо того, чтобы выполнять весь цикл в awk
.
Предполагая, что вам по какой-то причине нужны эти значения внутри цикла оболочки, это может быть то, что вам нужно, используя любой awk:
$ cat tst.sh
#!/usr/bin/env bash
while read -r nodename hostip rest; do
printf 'nodename is %s and hostip is %s\n' "$nodename" "$hostip"
done < <(awk -F'[",]' '/^NODE/{print $2, $3}' PING.cfg)
$ ./tst.sh
nodename is router and hostip is 10.0.0.1
nodename is dns1 and hostip is 68.6.16.30
nodename is dns2 and hostip is 1.1.1.1
Я использую имена переменных в нижнем регистре по причинам, описанным в разделе Правильная заглавная буква переменных Bash и сценария оболочки.
Спасибо, Эд, это очень ясный пример, и он делает именно то, что мне нужно.
Поскольку вы уже выполняете это через скрипт, просто используйте read для анализа каждой строки:
while IFS=, read NODE HOSTIP _
do
echo "NODE is ${NODE/*\"/} and HOSTIP is $HOSTIP"
done < PING.cfg
и пример запуска:
NODE is router and HOSTIP is 10.0.0.1
NODE is dns1 and HOSTIP is 68.6.16.30
NODE is dns2 and HOSTIP is 1.1.1.1
Поскольку bash помечен, вы также можете использовать регулярные выражения с группами захвата, доступ к которым можно получить через массив ${BASH_REMATCH[@]}
. Хорошо составленное регулярное выражение можно использовать даже для рассмотрения только строк, содержащих NODE
, заменяя первоначальный grep
фильтр:
while read -r line; do
[[ "$line" =~ ^(NODE[0-9]+)=\"([0-9a-z]+),([0-9.]+),([0-9]+)\"$ ]] &&
# BASH_REMATCH ╰────[1]───╯ ╰───[2]───╯ ╰──[3]──╯ ╰──[4]─╯
echo "NODE is ${BASH_REMATCH[2]} and HOSTIP is ${BASH_REMATCH[3]}"
done < PING.cfg
NODE is router and HOSTIP is 10.0.0.1
NODE is dns1 and HOSTIP is 68.6.16.30
NODE is dns2 and HOSTIP is 1.1.1.1
что-то вроде..., мои два бита..
awk -V
GNU Awk 5.0.1, API: 2.0 (GNU MPFR 4.0.2, GNU MP 6.2.0)
Copyright (C) 1989, 1991-2019 Free Software Foundation.
# using poster's supplied input ...
awk -F'"' '{split($2,bits,","); print "NODE is " bits[1] " and HOSTIP is " bits[2]}' < PING.cfg
NODE is router and HOSTIP is 10.0.0.1
NODE is dns1 and HOSTIP is 68.6.16.30
NODE is dns2 and HOSTIP is 1.1.1.1
Они используются как переменные, полученные из нескольких скриптов.
Если файл уже исходный (или его поиск не будет проблемой), вы можете получить доступ к переменным, начинающимся с NODE
, используя расширение параметра Bash${!prefix@}
:
. PING.cfg
for n in "${!NODE@}"; do readarray -td, vals <<< "${!n}"
echo "NODE is ${vals[0]} and HOSTIP is ${vals[1]}"
done
# or
for n in "${!NODE@}"; do IFS=, read -r node hostip value <<< "${!n}"
echo "NODE is $node and HOSTIP is $hostip"
done
# or
for n in "${!NODE@}"; do printf "NODE is %s and HOSTIP is %s\n" \
"$(cut -d, -f1 <<< "${!n}")" "$(cut -d, -f2 <<< "${!n}")"
done
NODE is router and HOSTIP is 10.0.0.1
NODE is dns1 and HOSTIP is 68.6.16.30
NODE is dns2 and HOSTIP is 1.1.1.1
Использовать одновременно
FPAT
и-F
не имеет смысла.FPAT
переопределяет-F
.