【问题标题】:Using a shell script to replace tokens in a file使用 shell 脚本替换文件中的标记
【发布时间】:2018-06-08 22:00:36
【问题描述】:

我有一个包含令牌变量的文件。当我们切换环境时,我们希望为我们所处的环境正确替换这些标记。

如何在 Linux shell 中执行此操作?我在考虑 awk 或 sed。

例子:

文件有这个数据:

DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__

我想读取这个文件,识别__HOST__并将其替换为环境变量$(HOST)。我们将从两个下划线之间的匹配字符串中获取环境变量名称。

有没有人知道我们如何做到这一点?

【问题讨论】:

  • 请避免 "Give me the codez: 已经被问过和回答过很多次的问题,你必须努力避免找到答案。另见How much research effort is expected of Stack Overflow users?我们不应该提供 LMGTFY 答案,但这里是:bash replace token in a file site:stackoverflow.com
  • @jww 我已经研究了好几个小时了。我并不是想说给我代码本身,但我只是没有列出我尝试过的每一步。我尝试使用 sed 来查找模式,我能够从两个下划线之间获取字符串,我该如何使用它?我尝试了 awk,但无法弄清楚如何使用下划线并将 DB_HOST 替换为 $(DB_HOST)。我很少使用 bash。我有点希望只有一个我可以使用的命令行工具可以开箱即用地做到这一点。 Stack Overflow 是我最后的选择。我讨厌问问题。如果你愿意,请投反对票
  • @Decius:有一个常用的命令行工具几乎可以做到这一点;我将它添加到我的答案中,以及一些使用说明。对该问题的补偿性投票。
  • 就我个人而言,我认为我会选择原生格式的 envsubst,除非你真的需要使用下划线。
  • 你为什么要这样做而不是让你的基本文件是DB_HOST=$HOST而不是DB_HOST=__HOST__,然后在使用它之前必须用$HOST的值替换__HOST__?跨度>

标签: linux bash shell awk sed


【解决方案1】:

程序envsubst 大致就是为了这个目的而设计的,但是它想使用标准的shell 格式来替换环境中的字符串:

# Here-doc delimiter quoted to avoid having the shell do the substitutions
$ HOST=myhost PASSWORD=secret envsubst <<"EOF"
DB_HOST=$HOST
DB_PASSWORD=${PASSWORD}
EOF

DB_HOST=myhost
DB_PASSWORD=secret

envsubst 是 Gnu gettext 的一部分,并且可以广泛使用。如果您确实需要使用__...__ 语法,可以通过 sed 管道将其更改为envsubst 语法:

$ cat myfile.txt
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__

$ sed -E 's/__(([^_]|_[^_])*)__/${\1}/g' myfile.txt |envsubst

或者,如果你有 Python,有一个非常简单的解决方案:

from sys import stdin
from os import getenv
import re

pattern = re.compile(r"__(\w*)__")

def replacer(m):
    val = getenv(m[1])
    if val is None:
        # No such environment
        # Perhaps produce a warning, or an error
        # Here we just pass through the string unaltered
        return m[0]
    return val

print(pattern.sub(replacer, stdin.read()))

例子:

$ export HOST=myhost PASSWORD=secret 
$ python3 subst.py < myfile.txt
DB_HOST=myhost
DB_PASSWORD=secret

此解决方案的关键是 Python(不幸的是,只有少数其他语言)允许模式替换的替换参数是一个函数,然后在每个连续匹配时调用该函数以生成替换文本。这使得在 Python 中编写这样的函数比在 awk 中更容易,例如,替换文本是固定的。

【讨论】:

  • 对不起,我对 Python 不是很熟悉。我该如何称呼这个脚本?我需要每行调用一次还是可以传递整个文件?
  • @Decius:你把整个文件传进去,我贴一个例子
  • @decius:我的错,我打字很匆忙,我漏掉了一对括号。现在修好了,我希望。
  • @decius: @decius: (([^_]|_[^_])*) 是“任意次数的非下划线字符或下划线后跟非下划线字符的重复次数”。这个想法是匹配一个不包含__ 的字符串,这是您需要为否定编写正则表达式的笨拙方式。许多正则表达式库包含更简单的扩展(如非贪婪重复),但sed 仅实现 Posix 正则表达式。
  • @decius: 和它周围的括号,我在原文中省略了,使\1 捕获整个重复(即__s 之间的任何内容)。 \2(第二个括号的内容)只是最后一个重复,用处不大。
【解决方案2】:

这就是m4 的用途:

$ export HOST=myhost PASSWORD=mypassword

$ cat myfile
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__

$ m4 -D __HOST__="$HOST" -D __PASSWORD__="$PASSWORD" myfile
DB_HOST=myhost
DB_PASSWORD=mypassword

如果您想为所有环境变量自动定义宏,那当然是可能的,如果有点不确定:

$ cat mymacros.m4
esyscmd(`env | sed -ne "s/^\([A-Z_]*\)=\(.*\)/define(__\1__, \`\2')/p" | tr -d "\n"')

$ cat myfile2
include(`mymacros.m4')
DB_HOST=__HOST__
DB_PASSWORD=__PASSWORD__
And other arbitrary text containing things like "My editor is __EDITOR__"

$ m4 myfile2
DB_HOST=myhost
DB_PASSWORD=mypassword
And other arbitrary text containing things like "My editor is vim"

这不一定是最简单的方法,但它具有高度的灵活性和可扩展性,支持条件语句、宏函数等。

【讨论】:

  • 想自己“m4”?那是什么? 这正是我喜欢这个网站的地方,因为它是真的——你真的可以每天学习新的东西。 +1
  • 这不是标准文件。基本上我有一个詹金斯服务器,我们希望能够将我们想要的任何东西传递给它。是否可以遍历每个变量并根据两个下划线之间的内容调用 m4 ?顺便说一句,M4 真的很酷,我从来没有听说过它
【解决方案3】:

这就是你想要的:

 while IFS= read -r line  || [[ -n "$line" ]]; do
    from=$(echo $line | cut -d '=' -f 1 )
    to=$(echo $line | cut -d '=' -f 2 )
    sedString=${sedString}" s/__${from}__/${to}/g;"
  done < tokenFile.txt

sed "${sedString}" old.txt >> new.txt

它使用 tokenFile.txt 中的所有替换内容构建一个字符串,然后在您指定的任何文件中用一个 sed 命令一次性替换它们。 tokenFile.txt 有你的 'old=new' 对。 old.txt 是带有 tokens 的文件。 new.txt 是你输出的地方。

例如,运行脚本后你会得到:

$ cat tokenFile.txt
DB_HOST=127.0.0.1

$ cat old.txt
dbconnect __DB_HOST__

$ cat new.txt
dbconnect 127.0.0.1

【讨论】:

    【解决方案4】:

    我写了一个 bash 脚本,因为我没有可用的 envsubst。我使用grep 找到我的令牌模式,然后使用sed 进行替换。注意使用 bash indirect expansion

    给定文件replace-vars.txt

    Craig likes ${ENV_1}
    Anuj likes ${ENV_2}
    Dave likes ${ENV_3}
    

    运行以下脚本:

    #!/usr/bin/env bash
    
    # env variables to replace
    export ENV_1=milk
    export ENV_2=cheese
    export ENV_3=candy
    
    # File to modify
    declare FILE_TO_REPLACE=replace-vars.txt
    
    # Pattern for your tokens -- e.g. ${token}
    declare TOKEN_PATTERN='(?<=\$\{)\w+(?=\})'
    
    # Find all tokens to replace
    declare TOKENS=$(grep -oP ${TOKEN_PATTERN} ${FILE_TO_REPLACE} | sort -u)
    
    # Loop over tokens and use sed to replace
    for token in $TOKENS
    do
      echo "Replacing \${${token}} with ${!token}"
      sed -i "s/\${${token}}/${!token}/" ${FILE_TO_REPLACE}
    done
    

    输出:

    Replacing ${ENV_1} with milk
    Replacing ${ENV_2} with cheese
    Replacing ${ENV_3} with candy
    

    修改replace-vars.txt

    Craig likes milk
    Anuj likes cheese
    Dave likes candy
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多