使用Bash脚本修改配置文件

58

我正在编写一个bash脚本来修改一个包含多个键值对的配置文件。如何读取键并找到相应的值,进而进行可能的修改?


我在这里尝试回答了 https://dev59.com/1G025IYBdhLWcg3whWZm#47509618 - Venkateswara Rao
8个回答

120

修改单个值的一个猜测:

sed -c -i "s/\($TARGET_KEY *= *\).*/\1$REPLACEMENT_VALUE/" $CONFIG_FILE
假设目标键和替换值不包含任何特殊的正则表达式字符,且您的键值分隔符为“=”。请注意,“-c”选项是与系统相关的,您可能需要省略它才能执行sed。要了解如何进行类似替换的其他提示(例如,当REPLACEMENT_VALUE中包含“/”字符时),可以在这里找到一些很好的例子

18
在我的环境中,-c 似乎无效。当我去掉 -c 后,它能正常运行。谢谢。 - Progress Programmer
1
@prolink:ssh [opts] [user@]hostname command将登录然后运行给定的命令。如果命令足够复杂,您可能希望将其存储在远程脚本中,以便可以只运行ssh <host> path/to/script - Cascabel
6
如果在 Mac 环境下执行,请将 -c 删除并在 -i 后添加 '',就像这样 sed -i '' "s...." $CONFIG_FILE - Yang
9
非常有用的脚本。如果您所有的键都在行的开头,请考虑在 $TARGET_KEY 前面添加 ^,否则如果您有两个键像 even=2noteven=1 您将会同时更改它们。 - acm
2
如果之前不存在该值,那么添加该值怎么样? - Michael Chourdakis
显示剩余4条评论

23

希望这能对某人有所帮助。我创建了一个自包含的脚本,需要进行一些配置处理。

#!/bin/bash
CONFIG="/tmp/test.cfg"

# Use this to set the new config value, needs 2 parameters. 
# You could check that $1 and $2 is set, but I am lazy
function set_config(){
    sudo sed -i "s/^\($1\s*=\s*\).*\$/\1$2/" $CONFIG
}

# INITIALIZE CONFIG IF IT'S MISSING
if [ ! -e "${CONFIG}" ] ; then
    # Set default variable value
    sudo touch $CONFIG
    echo "myname=\"Test\"" | sudo tee --append $CONFIG
fi

# LOAD THE CONFIG FILE
source $CONFIG

echo "${myname}" # SHOULD OUTPUT DEFAULT (test) ON FIRST RUN
myname="Erl"
echo "${myname}" # SHOULD OUTPUT Erl
set_config myname $myname # SETS THE NEW VALUE

我怀疑这是否适用于TOML,甚至YAML或JSON,因为它们都有节(section)。但对于仅键值对,它可以正常工作。 - Debargha Roy

5
假设你有一个包含key=value对的文件,可能在=周围有空格,即使键或值包含特殊的正则表达式序列,你也可以使用awk随意删除、原地修改或追加键值对。
# Using awk to delete, modify or append keys
# In case of an error the original configuration file is left intact
# Also leaves a timestamped backup copy (omit the cp -p if none is required)
CONFIG_FILE=file.conf
cp -p "$CONFIG_FILE" "$CONFIG_FILE.orig.`date \"+%Y%m%d_%H%M%S\"`" &&
awk -F '[ \t]*=[ \t]*' '$1=="keytodelete" { next } $1=="keytomodify" { print "keytomodify=newvalue" ; next } { print } END { print "keytoappend=value" }' "$CONFIG_FILE" >"$CONFIG_FILE~" &&
mv "$CONFIG_FILE~" "$CONFIG_FILE" ||
echo "an error has occurred (permissions? disk space?)"

4
sed "/^$old/s/\(.[^=]*\)\([ \t]*=[ \t]*\)\(.[^=]*\)/\1\2$replace/" configfile

3

我不能单独为此负责,因为这是stackoverflow答案和irc.freenode.net#bash频道的帮助的结合体,但现在有了bash函数,可以设置和读取配置文件值:

# https://dev59.com/DnE95IYBdhLWcg3wE56C#2464883
# Usage: config_set filename key value
function config_set() {
  local file=$1
  local key=$2
  local val=${@:3}

  ensureConfigFileExists "${file}"

  # create key if not exists
  if ! grep -q "^${key}=" ${file}; then
    # insert a newline just in case the file does not end with one
    printf "\n${key}=" >> ${file}
  fi

  chc "$file" "$key" "$val"
}

function ensureConfigFileExists() {
  if [ ! -e "$1" ] ; then
    if [ -e "$1.example" ]; then
      cp "$1.example" "$1";
    else
      touch "$1"
    fi
  fi
}

# thanks to ixz in #bash on irc.freenode.net
function chc() { gawk -v OFS== -v FS== -e 'BEGIN { ARGC = 1 } $1 == ARGV[2] { print ARGV[4] ? ARGV[4] : $1, ARGV[3]; next } 1' "$@" <"$1" >"$1.1"; mv "$1"{.1,}; }

# https://unix.stackexchange.com/a/331965/312709
# Usage: local myvar="$(config_get myvar)"
function config_get() {
    val="$(config_read_file ${CONFIG_FILE} "${1}")";
    if [ "${val}" = "__UNDEFINED__" ]; then
        val="$(config_read_file ${CONFIG_FILE}.example "${1}")";
    fi
    printf -- "%s" "${val}";
}
function config_read_file() {
    (grep -E "^${2}=" -m 1 "${1}" 2>/dev/null || echo "VAR=__UNDEFINED__") | head -n 1 | cut -d '=' -f 2-;
}

起初我使用了被接受的答案提供的sed解决方案:https://dev59.com/DnE95IYBdhLWcg3wE56C#2464883/2683059,但是如果值中有斜杠字符,则会出现错误。

0
我已经做过这个:
new_port=$1
sed "s/^port=.*/port=$new_port/" "$CONFIG_FILE" > /yourPath/temp.x
mv /yourPath/temp.x "$CONFIG_FILE"

这将会在您选择 8888 作为 $1 参数时,在您的配置文件中把 port= 更改为 port=8888

0

通常使用grep和cut很容易提取信息:


cat "$FILE" | grep "^${KEY}${DELIMITER}" | cut -f2- -d"$DELIMITER"

要更新,你可以这样做:


mv "$FILE" "$FILE.bak"
cat "$FILE.bak" | grep -v "^${KEY}${DELIMITER}" > "$FILE"
echo "${KEY}${DELIMITER}${NEWVALUE}" >> "$FILE"

这显然不能保持键值对的顺序。添加错误检查以确保您不会丢失数据。


如果您的磁盘空间不足或由于任何原因无法创建“FILE”,那么您的解决方案将导致“FILE”丢失(需要手动将“FILE.bak”重命名为“FILE”才能恢复)。 - vladr

-1
假设您的配置文件格式如下:
CONFIG_NUM=4
CONFIG_NUM2=5
CONFIG_DEBUG=n

在你的Bash脚本中,你可以使用:
CONFIG_FILE=your_config_file
. $CONFIG_FILE

if [ $CONFIG_DEBUG == "y" ]; then
    ......
else
    ......
fi

$CONFIG_NUM$CONFIG_NUM2$CONFIG_DEBUG 是你需要的。

读取这些值后,将其写回将会很容易:

echo "CONFIG_DEBUG=y" >> $CONFIG_FILE

10
这不会替换价值,只是将其再次添加到底部,这完全依赖于配置文件是有效的Bash脚本,而且...哇,如果配置文件中有恶意内容怎么办? - Cascabel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接