使用bash编辑包含节的配置文件

使用bash编辑包含节的配置文件,bash,Bash,我正在编写bash脚本来更改配置文件。配置文件包含节头,每个节可能包含相同的键/值对 示例配置文件: Header one key value key2 foobar Header two key bar 我想替换特定节标题的键值,而不在不同节上触碰同一个键。例如,用一些任意值替换“头1/键”的值“值”,而不更改“头2/键”的值栏 实际的节标题遵循示例中提供的模式(一些固定单词+非固定部分) A提出了使用sed进行价值置换的解决方案: # Set a configuration v

我正在编写bash脚本来更改配置文件。配置文件包含节头,每个节可能包含相同的键/值对

示例配置文件:

Header one
  key value
  key2 foobar
Header two
  key bar
我想替换特定节标题的键值,而不在不同节上触碰同一个键。例如,用一些任意值替换“头1/键”的值“值”,而不更改“头2/键”的值栏

实际的节标题遵循示例中提供的模式(一些固定单词+非固定部分)

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                                                                                                                                                                             
}
但是我不知道如何将替换限制在配置文件的某个部分

我想提出一个类似于的解决方案

# 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() {                                                                                                                                                                                      
  ???                                                                                                                                                                            
}
编辑: 有关实际配置文件的问题:假设在设置值时节标题的整个文本都是已知的。无论它被称为“头一”、“foo”或“foobar”

编辑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"
}

不能像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"

您当前的尝试有一些问题

# Your code
sed -i "s/^\s*($1\s*\).*\$/\1$2/" $3                                                                                                                                                                             
$1
之后,如果您至少需要一个空格(您不希望与键2匹配),请使用“+”。
如果要保留替换后的第一个空格,请将其放入匹配项中。
可能短键后会有额外的空格,所以在匹配中也要将空格放在键后。
您不想匹配
$
。带反斜杠的是一个字符。您不需要匹配行尾,
*
将匹配所有内容,直到行尾。
一个名称中有空格的配置文件怎么样?报价
$3

而且
\s
在普通
sed
中不起作用。试试
-r

我删除了
-I
,因此您可以在不更改文件的情况下进行测试:

sed -r "s/^(\s*$1\s+).*$/\1$2/" "$3"
如果要将此代码限制为一个节,请使用
/start/,/end/
。你怎么知道某个东西是标题?在您的示例中,标题行称为header,但在实际配置文件中并非如此。当标题看起来像
[section]
时,请更改下面的解决方案。beneat的解决方案假定所有不以空格开头的行都是一个标题

# 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
,但请自己选择。查看。

除非您描述配置文件的结构,否则此问题的任何答案都将基于假设,并且可能无法与实际输入一起工作
# 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}"
}