使用bash

时间:2019-09-07 14:03:52

标签: bash

我正在编写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"
}

2 个答案:

答案 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