Linux 如何使用 sed 更改我的配置文件,具有灵活的键和值?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5955548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 00:53:36  来源:igfitidea点击:

How do I use sed to change my configuration files, with flexible keys and values?

linuxbashsed

提问by prolink007

I want to search a configuration file for this expression: "central.database". I then want to change the setting associated with "central.database" to "SQLTEST".

我想搜索这个表达式的配置文件:“central.database”。然后我想将与“central.database”关联的设置更改为“SQLTEST”。

The layout of the config file would look like this initially:

配置文件的布局最初如下所示:

central.database = SQLFIRSTTEST

This is what i want it to look like after the sed replacement:

这就是我希望它在 sed 替换后的样子:

central.database = SQLTEST

I am doing this in a bash script, any suggestions, recommendations or alternative solutions are welcome!

我在 bash 脚本中执行此操作,欢迎提供任何建议、建议或替代解决方案!

(Actually both central.databaseand SQLTESTcome from bash variables here.)

(实际上,这两个central.databaseSQLTEST来自bash的变量在这里。)



My current code (third attempt):

我当前的代码(第三次尝试):

sshRetValue=$(ssh -p "35903" -i $HOME/sshids/idrsa-1.old  <<EOF
        sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*$/$CENTRAL_DB_VALUE/" /home/testing.txt;
        echo $?
EOF
)

Error message:

错误信息:

Pseudo-terminal will not be allocated because stdin is not a terminal.
sed: -e expression #1, char 58: unknown option to `s'
-bash: line 3: EOF: command not found

采纳答案by sapht

Here's an example expression:

这是一个示例表达式:

sed -i 's/^\(central\.database\s*=\s*\).*$/SQLTEST/' file.cfg

If you want to match stuff with /in it, you can use another delimiter:

如果你想匹配里面的东西/,你可以使用另一个分隔符:

sed -i 's#^\(cent/ral\.data/base\s*=\s*\).*$#SQL/TEST#' file.cfg

Or with variable expansion:

或者使用变量扩展:

VAL="SQLTEST"
sed -i "s/^\(central\.database\s*=\s*\).*$/$VAL/" file.cfg

In your example:

在你的例子中:

sshRetValue=`sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*$/$CENTRAL_DB_VALUE/" /home/testing.txt`;

There's a \1 before $CENTRAL_DB_NAME that's invalid. Also, sed doesn't print it's return value. This is the preferred way to check return values:

$CENTRAL_DB_NAME 之前有一个无效的 \1。此外, sed 不打印它的返回值。这是检查返回值的首选方法:

sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*$/$CENTRAL_DB_VALUE/" /home/testing.txt;
sed_return_value=$?

And ultimately piping to ssh (not tested):

并最终通过管道传输到 ssh(未测试):

sed_return_value=$(ssh server <<EOF
    sed -i "s/^\($CENTRAL_DB_NAME\s*=\s*\).*$/$CENTRAL_DB_VALUE/" /home/testing.txt;
    echo $?
EOF
)

The -i is for replacing data in the input file. Otherwise sed writes to stdout.

-i 用于替换输入文件中的数据。否则 sed 写入标准输出。

Regular expressions are a field of their own. It would be impossible to explain them in depth in a stackoverflow answer, unless there is some specific function that's eluding you.

正则表达式是它们自己的领域。在 stackoverflow 答案中不可能对它们进行深入解释,除非有一些特定的功能让你望而却步。

回答by John Kugelman

sed -i -e '/central\.database =/ s/= .*/= new_value/' /path/to/file

Explanation:

解释:

  • -itells sed to save the results to the input file. Without it sed will print the results to stdout.
  • /central\.database =/matches lines that contain the string between slashes: central.database =. The .is escaped since it's a special character in regex.
  • The s/OLD/NEW/part performs a substitution. The OLD string is a regular expression to match and the NEWpart is the string to substitute in.
  • In regular expressions, .*means "match anything". So = .*matches an equal sign, space, and then anything else afterward.
  • -i告诉 sed 将结果保存到输入文件中。没有它 sed 会将结果打印到标准输出。
  • /central\.database =/匹配包含斜杠之间字符串的行:central.database =。将.被转义,因为它是在正则表达式特殊字符。
  • s/OLD/NEW/部分执行一个小号ubstitution。OLD 字符串是要匹配的正则表达式,NEW部分是要替换的字符串。
  • 在正则表达式中,.*表示“匹配任何内容”。So= .*匹配等号、空格,然后匹配其他任何内容。

回答by user5433596

If you want to replace between 2 property files you can use this:

如果要在 2 个属性文件之间替换,可以使用以下命令:

awk -F= 'NR==FNR{A[]=;next} in A{=A[]}1' OFS='\=' /tmp/masterfile /opt/props/finalfile.properties > /tmp/tmp.txt && mv -f /tmp/tmp.txt /opt/props/finalfile.properties

回答by fedorqui 'SO stop harming'

I like using awkfor this, since it is quite easy to understand what it is doing and takes care very well of the separator (=) and also the fact that it must be done to an uncommented line:

我喜欢使用awk它,因为它很容易理解它在做什么,并且很好地处理了分隔符 ( =) 以及它必须对未注释的行进行的事实:

awk -v var="my_var" -v new_val="NEW VALUE" \  # set the vars
    'BEGIN{FS=OFS="="}                        # set separator to =
     match(, "^\s*" var "\s*") {          # check if it matches
         =" " new_val                       # if so, replace the line
     }1' conf_file                            # print all lines

This uses match()to check if the pattern occurs in any given line. If it does, it performs the replacement with the given value.

这用于match()检查模式是否出现在任何给定的行中。如果是,则使用给定值执行替换。

For example:

例如:

$ cat conf
hello
my_var= SOME VALUE
#my_var = ANOTHER VALUE
bye

Let's change the value in my_varto NEW VALUE:

让我们将值更改my_varNEW VALUE

$ awk -v var="my_var" -v new_val="NEW VALUE" 'BEGIN{FS=OFS="="}match(, "^\s*" var "\s*") {=" " new_val}1' conf
hello
my_var= NEW VALUE
#my_var = ANOTHER VALUE
bye

It is also possible to set the values in shell variables and then use them with -v:

也可以在 shell 变量中设置值,然后将它们用于-v

$ var="my_var"
$ new_value="NEW VALUE"
$ awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match(, "^\s*" var "\s*") {=" " new_val}1' conf

And you can of course put all of this within a shell function that you then call normally:

您当然可以将所有这些放在一个 shell 函数中,然后您可以正常调用该函数:

#!/bin/bash

replace () {
   file=
   var=
   new_value=
   awk -v var="$var" -v new_val="$new_value" 'BEGIN{FS=OFS="="}match(, "^\s*" var "\s*") {=" " new_val}1' "$file"
}

# Call the replace() function with the necessary parameters
replace "conf" "my_var" "NEW VALUE" 

Upon execution, this returns

执行后,这将返回

hello
my_var= NEW VALUE
#my_var = ANOTHER VALUE
bye

While you can also make the script receive the parameters in a way like: ./script.sh "conf_file" "var_to_replace" "NEW VALUE"to then pass them to the function.

虽然您也可以让脚本以如下方式接收参数:./script.sh "conf_file" "var_to_replace" "NEW VALUE"然后将它们传递给函数。

回答by Venkateswara Rao

I used this script for keeping the priorities..

我用这个脚本来保持优先级..

The arguments $1 will have folder in which multiple config files exist. $2 will have properties which need to be replaced in $1 path and sub paths files #3 will have properties which need to be override on top of $2

参数 $1 将包含其中存在多个配置文件的文件夹。$2 将具有需要在 $1 路径和子路径文件中替换的属性 #3 将具有需要在 $2 之上覆盖的属性

It also has hidden logic to check for existence of environment variables for the keys exist in $2 and $3 and give priority to that.

它还有隐藏的逻辑来检查 $2 和 $3 中存在的键的环境变量是否存在并优先考虑。

i.e If a key exist in environment that would be highest priority. Next to that would $3 and next to that would $1 file.

即,如果密钥存在于最高优先级的环境中。旁边是 3 美元,旁边是 1 美元的文件。

#!/bin/bash
#Usage is propertyReplacer <CONFIG_FOLDER_PATH> <CONFIG_FILE_2ND_PRIORITY> <CONFIG_FILE_1ST_PRIORITY>
function propertyReplacer() {

  filePathToAct=""
  propertiesFilePath=""
  propertiesSecureFilePath=""

  declare -A keyValues

  while IFS='=' read -r key value; do
    if [  "$key" == "" ]; then
      continue
    elif [[  "$key" =~ ^#.*$ ]]; then
      continue
    else
      echo $key " --> " $value
      keyValues[$key]=$value
    fi
  done < "$propertiesFilePath"

  if [ ! -f "$propertiesSecureFilePath" ]; then
    continue
  else
    while IFS='=' read -r key value; do
      if [  "$key" == "" ]; then
        continue
      elif [[  "$key" =~ ^#.*$ ]]; then
        continue
      else
        echo $key " --> " $value
        keyValues[$key]=$value
      fi
    done < "$propertiesSecureFilePath"
  fi

  for key in ${!keyValues[@]}; do
    envProp=${key//[@]/}
    if [  "$(eval echo '$'$envProp)" == "" ]; then
      echo "Environment key not exist" $envProp
    else
      value=$(eval echo '$'$envProp)
      echo "From Environment " $envProp " --> "$value 
      keyValues[$key]=$value
    fi
  done 


find "$filePathToAct" | while read -r resultFileName; do
  if [ ! -f "$resultFileName" ]; then
    continue
  else
    echo "Acting on the file $resultFileName"

    for key in ${!keyValues[@]}; do
      value=$(echo "${keyValues[${key}]}" | sed 's/\//\\//g')
      echo "sed -i 's/$key/$value/g' $resultFileName "
      eval "sed -i 's/$key/$value/g' $resultFileName "
    done 
  fi
done 

} 

回答by Alex Raj Kaliamoorthy

I know it is too late to add an answer to this question however, I thought to share my knowledge to you all. There is a very general approach which I have followed to solve a similar kind of problem. I have deleted the whole line which is matching the string and added the required values to that key. To your question here is the answer

我知道现在为这个问题添加答案为时已晚,但我想与大家分享我的知识。我遵循了一种非常通用的方法来解决类似的问题。我删除了与字符串匹配的整行,并将所需的值添加到该键。你的问题在这里是答案

replaceValue=SQLTEST
sed -i "/central.database =/d" /home/testing.txt
echo "central.database = $replaceValue"  >> /home/testing.txt

sed deletes the matching string line from the file and the immediate next line is inserting the required key and value to the file.

sed 从文件中删除匹配的字符串行,紧接着的下一行将所需的键和值插入到文件中。

回答by Theo Orphanos

I have a file called "config.php" and I wanted to change one of its definitions lines.

我有一个名为“config.php”的文件,我想更改其定义行之一。

For example, the line:

例如,该行:

define('SOME_CONSTANT', 'old_value');

had to be replaced with this one:

必须用这个替换:

define('SOME_CONSTANT', 'new_value');

So I did that:

所以我这样做了:

sed -i -e "/.*SOME_CONSTANT*./ s/.*/define('SOME_CONSTANT', 'new_value');/" path/to/config.php

In the first part, I am looking for a line that contains "SOME_CONSTANT" (thus the wildcards)

在第一部分中,我正在寻找包含“SOME_CONSTANT”的行(因此是通配符)

Then I replace that line with a new definition such as: define('SOME_CONSTANT', 'new_value');

然后我用一个新定义替换该行,例如:define('SOME_CONSTANT', 'new_value');

Tested and works fine in Centos 7

在 Centos 7 中测试并正常工作