【问题标题】:Substituting environment variables in a file: awk or sed?替换文件中的环境变量:awk 还是 sed?
【发布时间】:2011-08-18 03:58:02
【问题描述】:

我有一个环境变量文件,我在 shell 脚本中获取,例如:

# 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 来做到这一点(最好保留 cmets,甚至是带有 '=' 字符的 cmets)?或者我应该在一个while循环或其他一些魔法中将“读取”与字符串切割结合起来?如果可能的话,我想避免使用 perl/python,而只使用 Busybox 中可用的工具。非常感谢。

编辑:也许一个用例可以说明我的问题是什么。我保留了一个由 shell 环境变量声明组成的配置文件:

# 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

如何重写配置文件,只更新配置文件中已经存在的变量,最好保持 cmets 和空行不变?希望这能澄清一点。再次感谢。

【问题讨论】:

    标签: sed awk sh


    【解决方案1】:

    我不完全知道你想做什么,但如果你想改变变量 THREE 的值,

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

    【讨论】:

    • 抱歉没有更清楚。我想将文件中的变量读取到环境中(文件是 sn-p #1)。然后我想更新环境中的一两个变量(更新三个只是一个例子,它可以是任何变量),然后用更新的值重写文件。所以:读,改,写。诀窍是只写回最初在文件中的变量(而不是整个环境)。在原始文件中保留 cmets 会很好。
    【解决方案2】:

    您不能使用 awk 并从同一个文件读取和写入(这是您的问题的一部分)。

    我更喜欢在重写之前重命名文件(但您也可以保存到 tmp 然后重命名)。

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

    如果您的 env 文件变大,您会看到它在您的操作系统的缓冲区大小处被截断。

    另外,不要忘记 gawk(大多数 Linux 安装上的标准)有一个内置数组 ENVIRON。你可以从中创造你想要的东西

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

    当然,您可以在您的环境中获得一切,所以可能比您想要的更多。但可能是从您想要完成的事情开始的更好的地方。

    编辑 最具体的

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

    编辑 2 我认为您的 var=values 可能需要为 export -ed,以便它们对 awk ENVIRON 数组可见。

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

    打印 PATH 的现有值。

    我希望这会有所帮助。

    附:由于您似乎是新用户,如果您得到对您有帮助的答案,请记住将其标记为已接受,和/或给它一个 +(或 -)作为有用的答案。

    【讨论】:

    • 感谢您试一试,但我想我还是没说清楚。是否有 sh/sed/awk 等价于: perl -ne '/^([^=]+)/;打印 "$1=$ENV{$1}\n";'?
    • 再次感谢。您的编辑似乎更像我所追求的,但是 ENVIRON[$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
    

    我使用临时文件来尽可能长时间地保持配置文件的存在。

    【讨论】:

    • 感谢 Glenn,这是一个很好的解决方案。我没有想过使用声明。请注意,包含标头(如您的示例)会破坏对 rewrite_config 的后续调用,但除此之外很好。
    • 我一直在使用各种eval 解决方案,直到我读到declare
    猜你喜欢
    • 2014-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-13
    • 2021-08-01
    • 2012-12-08
    相关资源
    最近更新 更多