Я пытаюсь сделать следующее:
У меня есть этот файл с именем testing.txt, который я хочу обновлять каждый раз, когда IP-адрес или адреса меняются в зависимости от имени (test1ip, test2ip):
127.0.0.1 localhost
somotherrandomip testing
192.168.0.36 test1ip
192.168.0.37 test2ip
Это то, что я пробовал.
#!/bin/bash
array=(
"192.168.0.34 test1ip"
"192.168.0.35 test2ip"
)
for i in "${array[@]}"; do
if ! grep -Fxq "$i" testing.txt
then
echo "ip-name=$i is not present, so adding it in testing.txt file"
echo "$i" >> testing.txt
else
echo "ip-name=$i is present in file, so nothing to do"
fi
done
Однако этот сценарий добавляет совершенно новую строку, если строка не найдена. Чего я хотел бы добиться, так это перезаписать строку, если test1ip или test2ip найдены, но IP-адрес изменился.
Ожидаемый результат:
127.0.0.1 localhost
somotherrandomip testing
192.168.0.34 test1ip
192.168.0.35 test2ip
Я также читал этот Как проверить, содержит ли строка подстроку в Bash, но, кажется, я не могу понять.
Любая помощь приветствуется!
Вот решение bash
+ awk
, которое эффективно справится с этой задачей:
#!/bin/bash
array=(
"192.168.0.34 test1ip"
"192.168.0.35 test2ip"
"192.168.0.33 test3ip"
)
awk '
FNR == NR {
aarr[$2] = $1
next
}
! ($2 in aarr)
END {
for (host in aarr)
print aarr[host], host
}
' <(printf '%s\n' "${array[@]}") testing.txt
127.0.0.1 localhost
somotherrandomip testing
192.168.0.33 test3ip
192.168.0.34 test1ip
192.168.0.35 test2ip
примечания:
bash
<( commands )
называется процесс-подстановка. Он создает файл, содержащий вывод commands
, который вы можете использовать в качестве аргумента.
awk
's FNR == NR
— это условие выбора первого файлового аргумента. В этом блоке я создаю ассоциативный массив, который преобразует имя хоста в его новый IP-адрес.
! ($2 in aarr)
означает печатать записи, для которых имя хоста нет в массиве перевода.
END
предназначен для печати массива перевода (новые IP-адреса имен хостов).
На моей машине работает следующее. Я изменил массив на ассоциативный, удалил параметр -x для grep и использовал sed
для редактирования файла на месте.
#!/bin/bash
#associative array
declare -A array=(
[test1ip] = "192.168.0.34"
[test2ip] = "192.168.0.35"
)
#Loop over keys of the array
#See parameter expansion in bash manpage
for i in "${!array[@]}"; do
if ! grep -Fq "$i" testing.txt
then
echo "ip-name=$i is not present, so adding it in testing.txt file"
echo "${array[$i]} $i" >> testing.txt
else
echo "ip-name=$i is present in file so running sed"
#Escape sed left hand side
#Adapted for extended regular expressions from https://unix.stackexchange.com/a/129063/309759
i=$(printf '%s\n' "$i" | sed 's:[][\/.^$*+?(){}|]:\\&:g')
#Excape right hand side
ipaddr=$(printf '%s\n' "${array[$i]}" | sed 's:[\/&]:\\&:g;$!s/$/\/')
#Replace old IP vith new IP
sed -Ei "s/[0-9]+(\.[0-9]+){3} +$i/$ipaddr $i/" testing.txt
fi
done
Спасибо @Nathan за помощь в этом.
Возможно, вам придется экранировать имена хостов, чтобы использовать их в sed
в качестве строковых литералов; например example.test
будет соответствовать examplestest
. Кроме того, хотя вы правильно исправили код @chiko, вы не указали, что следует избегать использования grep
и sed
внутри цикла for
.
Спасибо @Fravadona за вашу помощь и время.