我正在编写bash脚本来更改配置文件。配置文件包含节标题,并且每个节可以包含相同的键/值对。
示例配置文件:
Header one
key value
key2 foobar
Header two
key bar
我要替换特定节标题的键值,而不用触摸其他节上的相同键。例如。在不更改“标题二/键”的值栏的情况下,将“标题一/键”的“值”替换为任意值
实际的节标题遵循示例中提供的模式(某些固定字+一个非固定部分)。
A提出了使用sed进行价值置换的解决方案:
# Set a configuration value
# Args:
# 1) The config key
# 2) The new value
# 3) The config file
function set_config_value() {
sed -i "s/^\s*($1\s*\).*\$/\1$2/" $3
}
但是Iam毫无头绪如何将替换限制在配置文件的特定部分。
我想提出一种与
类似的解决方案# Set a configuration value
# Args:
# 1) The config key
# 2) The new value
# 3) The config section header
# 4) The config file
function set_config_value() {
???
}
编辑: 对于有关实际配置文件的问题:设置值时,假定知道节标题的整个文本。无论它被称为“ header one”,“ foo”还是“ foobar bar bar”。
修改2: Sed可能不是执行此类任务的最佳工具。使用awk,假设键值对行以空格开头:
function set_confg_value() {
awk -i inplace -v section="$1" -v key="$2" -v value="$3" '
BEGIN {
in_section = 0
}
# Set the in_section flag when entering the requested section
$0~section {
in_section = 1
}
# Process matched section
$0~"^\\s+"key {
if (in_section) {
print " "$1" "value
skip = 1
}
}
# Reset in_section flag
$0~"(?!"section")^\\S" {
in_section = 0
}
# Print the rest
/.*/ {
if (skip)
skip = 0
else
print $0
}
' "$4"
}
答案 0 :(得分:1)
您当前的尝试存在一些问题。
# Your code
sed -i "s/^\s*($1\s*\).*\$/\1$2/" $3
在$1
之后,您需要至少一个空格(您不想匹配key2),请使用'+'。
您希望替换后保留第一个空格,将其放入匹配项中。
也许您在短键之后获得了额外的空格,因此也将匹配项中的键之后的空格也放入。
您不想匹配$
。用反斜杠是一个字符。您不需要匹配行尾,.*
将匹配所有内容,直到行尾。
名称中带有空格的配置文件如何?引用$3
。
并且\s
不能在普通sed
中工作。尝试-r
。
我删除了-i
,因此您可以在不更改文件的情况下进行测试:
sed -r "s/^(\s*$1\s+).*$/\1$2/" "$3"
当您想将此代码限制为一个部分时,请使用/start/,/end/
。您怎么知道标题是什么?在您的示例中,标题行称为标题,但实际配置文件中并非如此。当标题看起来像[section]
时,请更改下面的解决方案。最佳解决方案是假定所有不以空格开头的行都是标头。
# Set a configuration value
# Args:
# 1) The config key
# 2) The new value
# 3) The config section header
# 4) The config file
function set_config_value() {
key="$1"
val="$2"
header="$3"
file="$4"
# first test this without the -i flag
sed -ir "/^${header}$/,/^[^\s]/ s/^(\s*${key}\s+).*$/\1${val}/" "${file}"
}
此解决方案应适用于您的示例配置,但是当${key}
或${value}
具有特殊字符(尝试key=/
)时将失败。
您应该使用不试图理解给定字符串的解决方案。我的第一个想法是awk
,但请自行选择。看https://unix.stackexchange.com/q/137643/57293。
答案 1 :(得分:0)
您不能像sed
那样仅使用基于行的搜索结尾替换。您需要记住上下文,即您在文件中的位置。您必须逐行阅读文件,并跟踪所在的部分。如果位于正确的部分,请找到密钥,然后替换值。
#!/bin/bash
set -eu
# Set a configuration value
# Args:
# 1) The config key
# 2) The new value
# 3) The config section header
# 4) The config file
function set_config_value() {
key="$1"
new="$2"
header="$3"
file="$4"
tmpfile=$(mktemp)
headerRegex='^Header (\S+)$'
keyRegex="\s*$key\s*"
insection=""
IFS=''
while read -r line
do
# Check for the next header
if [[ "$line" =~ $headerRegex ]]
then
# Is it the one we're looking for?
if [ "$header" == "${BASH_REMATCH[1]}" ]
then
insection="1"
else
insection=""
fi
fi
# If we are in the right section:
if [ -n "$insection" ]
then
# and we look at the right key
if [[ "$line" =~ $keyRegex ]]
then
# change it
line=" $key $new"
fi
fi
# Print the line, either as it was or modified
echo "$line" >> "$tmpfile"
done < "$file"
mv "$tmpfile" "$file"
}
set_config_value "key" "new value" "two" "in.cfg"
它必须是bash吗,顺便说一句?这个Perl脚本要短得多,并且使用Perl的内联编辑(-i
标志),而不是手动将临时文件弄乱:
perl -pi -e 'if (m/Header (\w+)/) {
$insection = ($1 eq $section);
}
if ($insection && m/^(\s*$key\s*)/) {
$_ = "$1$value\n";
}
' -s -- -key=key -value="new value" -section=two in.cfg