Замена переменных окружения в файле: awk или sed?

#sed #awk #sh

#sed #awk #sh

Вопрос:

У меня есть файл переменных окружения, которые я использую в сценариях оболочки, например:

 # This is a comment
ONE=1
TWO=2
THREE=THREE
# End
  

В моих сценариях я добавляю этот файл (предполагаю, что он называется ‘./vars’) в текущую среду и изменяю (некоторые) переменные на основе пользовательского ввода. Например:

 #!/bin/sh
# Read variables
source ./vars
# Change a variable
THREE=3
# Write variables back to the file??
awk 'BEGIN{FS="="}{print $1=$$1}' <./vars >./vars
  

Как вы можете видеть, я экспериментировал с awk для обратной записи переменных, sed тоже. Безуспешно. Последняя строка скрипта завершается ошибкой. Есть ли способ сделать это с помощью awk или sed (предпочтительно с сохранением комментариев, даже комментариев с символом ‘=’)? Или мне следует объединить ‘read’ с сокращением строки в цикле while или каким-либо другим волшебством? Если возможно, я бы хотел избежать perl / python и просто использовать инструменты, доступные в Busybox. Большое спасибо.

Редактировать: возможно, вариант использования мог бы прояснить, в чем моя проблема. Я сохраняю файл конфигурации, состоящий из объявлений переменных среды оболочки:

 # File: network.config
NETWORK_TYPE=wired
NETWORK_ADDRESS_RESOLUTION=dhcp
NETWORK_ADDRESS=
NETWORK_ADDRESS_MASK=
  

У меня также есть скрипт под названием ‘setup-network.sh ‘:

 #!/bin/sh
# File: setup-network.sh

# Read configuration
source network.config

# Setup network
NETWORK_DEVICE=none
if [ "$NETWORK_TYPE" == "wired" ]; then
  NETWORK_DEVICE=eth0
fi
if [ "$NETWORK_TYPE" == "wireless" ]; then
  NETWORK_DEVICE=wlan0
fi
ifconfig -i $NETWORK_DEVICE ...etc
  

У меня также есть скрипт под названием ‘configure-network.sh ‘:

 #!/bin/sh
# File: configure-network.sh

# Read configuration
source network.config

echo "Enter the network connection type:"
echo "  1. Wired network"
echo "  2. Wireless network"
read -p "Type:" -n1 TYPE

if [ "$TYPE" == "1" ]; then
  # Update environment variable
  NETWORK_TYPE=wired
elif [ "$TYPE" == "2" ]; then
  # Update environment variable
  NETWORK_TYPE=wireless
fi

# Rewrite configuration file, substituting the updated value
# of NETWORK_TYPE (and any other updated variables already existing
# in the network.config file), so that later invocations of
# 'setup-network.sh' read the updated configuration.
# TODO
  

Как мне переписать файл конфигурации, обновив только переменные, уже существующие в файле конфигурации, предпочтительно оставив комментарии и пустые строки нетронутыми? Надеюсь, это немного прояснит ситуацию. Еще раз спасибо.

Ответ №1:

Вы не можете использовать awk и выполнять чтение и запись из одного файла (это часть вашей проблемы).

Я предпочитаю переименовать файл перед перезаписью (но вы можете сохранить в tmp, а затем также переименовать).

 /bin/mv file file.tmp
awk '.... code ...' file.tmp > file
  

Если ваш env-файл становится больше, вы увидите, что он усекается при размере буфера вашей операционной системы.

Кроме того, не забывайте, что gawk (std в большинстве установок Linux) имеет встроенную среду array. Вы можете создать из этого все, что хотите

 awk 'END {
   for (key in ENVIRON) {
      print key "=" ENVIRON[key]
   }
}' /dev/null
  

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

Отредактируйте наиболее конкретно

    awk -F"=" '{
     if ($1 in ENVIRON) {
         printf("%s=%sn", $1, ENVIRON[$1])
     }
     # else line not printed or add code to meet your situation
   }' file > file.tmp
   /bin/mv file.tmp file
  

Правка 2
Я думаю, что ваши значения var =, возможно, потребуется export отредактировать, чтобы они были видны массиву среды awk.

И

 echo PATH=xxx| awk -F= '{print ENVIRON[$1]}'
  

выводит существующее значение PATH.

Я надеюсь, это поможет.

P.S. поскольку вы, похоже, новый пользователь, если вы получите ответ, который поможет вам, пожалуйста, не забудьте отметить его как принятый и / или поставить ему (или -) в качестве полезного ответа.

Комментарии:

1. Спасибо за подсказку, но опять же, я не думаю, что достаточно ясно выразился. Существует ли sh / sed / awk эквивалент: perl -ne ‘/^([^=] )/; вывести «$1=$ENV{$1}n»;’?

2. Еще раз спасибо. Ваша правка, похоже, больше похожа на то, что мне нужно, однако оператор ENVIRON [$ 1] не расширяется должным образом. Вы можете это перепроверить?

Ответ №2:

Я точно не знаю, что вы пытаетесь сделать, но если вы пытаетесь изменить значение переменной THREE ,

 awk -F"=" -vt="$THREE" '$1=="THREE" {$2=t}{print $0>FILENAME}' OFS="=" vars
  

Комментарии:

1. Извините, что не выразился яснее. Я хочу прочитать переменные из файла в среду (файл представляет собой фрагмент # 1). Затем я хочу обновить одну или две переменные в среде (обновление ТРЕХ — это только пример, это может быть любая из переменных), затем переписать файл с обновленными значениями. Итак: чтение, изменение, запись. Хитрость заключается только в том, чтобы записать обратно переменные, которые были в файле в первую очередь (не всю среду). И сохранение комментариев в исходном файле было бы неплохо.

Ответ №3:

Вы можете сделать это только с помощью bash:

 rewrite_config() {
    local filename="$1"
    local tmp=$(mktemp)

    # if you want the header
    echo "# File: $filename" >> "$tmp"

    while IFS='=' read var value; do
        declare -p $var | cut -d ' ' -f 3-
    done < "$filename" >> "$tmp"

    mv "$tmp" "$filename"
}
  

Используйте это как

 source network.config
# manipulate the variables
rewrite_config network.config
  

Я использую временный файл, чтобы поддерживать существование файла конфигурации как можно дольше.

Комментарии:

1. Спасибо, Гленн, это хорошее решение. Я не думал об использовании declare. Обратите внимание, что включение заголовка (как в вашем примере) блокирует последующие вызовы rewrite_config , но в остальном это хорошо.

2. Я перебирал различные eval решения, пока не прочитал о declare .