我想将“模板”文件的输出通过管道传递到MySQL,该文件具有${dbName}
散布的变量。替换这些实例并将输出转储到标准输出的命令行实用程序是什么?
我想将“模板”文件的输出通过管道传递到MySQL,该文件具有${dbName}
散布的变量。替换这些实例并将输出转储到标准输出的命令行实用程序是什么?
Answers:
塞德!
给定template.txt:
数字是$ {i} 这个词是$ {word}
我们只需要说:
sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.txt
感谢Jonathan Leffler的技巧,它可以将多个-e
参数传递给同一sed
调用。
cat
。您所需要的就是sed -e "s/\${i}/1/" -e "s/\${word}/dog/" template.text
。
sed
将期望转义的文本,这很麻烦。
这是yottatsa的一个类似问题的解决方案,它仅替代$ VAR或$ {VAR}之类的变量,并且是一个简短的单行代码
i=32 word=foo envsubst < template.txt
当然,如果i和word在您的环境中,那只是
envsubst < template.txt
在我的Mac上,它看起来像是作为gettext的一部分并从MacGPG2安装的
这是对mogsie的解决方案的一个类似问题的改进,我的解决方案不需要escale双引号,mogsie的则是双引号,但他只是一个衬板!
eval "cat <<EOF
$(<template.txt)
EOF
" 2> /dev/null
这两种解决方案的强大之处在于,尽管反斜杠是一个在这里转义字符,但是您不必担心解析存在错误,并且多行就可以了。
envsubst
如果不导出您的envar,则裸机不起作用。
envsubst
顾名思义,它只能识别环境变量,而不能识别shell变量。还值得注意的是,它envsubst
是一个GNU实用程序,因此并非预安装或在所有平台上都可用。
使用/bin/sh
。创建一个小的shell脚本来设置变量,然后使用shell本身来解析模板。这样(编辑以正确处理换行符):
the number is ${i}
the word is ${word}
#!/bin/sh
#Set variables
i=1
word="dog"
#Read in template one line at the time, and replace variables (more
#natural (and efficient) way, thanks to Jonathan Leffler).
while read line
do
eval echo "$line"
done < "./template.txt"
#sh script.sh
the number is 1
the word is dog
bash
输入中的所有命令将被执行。如果模板是:“单词是; rm -rf $ HOME”,则将丢失文件。
read
按照编写的命令,修剪每行的前导和尾随空格,并'吃掉' \
字符。(c)仅在完全信任或控制输入,因为由于使用,嵌入在输入中的命令替换(`…`
或$(…)
)允许执行任意命令eval
。最后,极少有机echo
会将其命令行选项之一误认为行首。
考虑到最近的兴趣,我又在考虑这个问题,我认为我最初想到的工具是m4
用于自动工具的宏处理器。因此,您可以使用以下代码代替我最初指定的变量:
$echo 'I am a DBNAME' | m4 -DDBNAME="database name"
envsubst
此简单的变量替换/模板化用法,如其他答案中所述。m4
虽然它是一个很好的工具,但是它是功能齐全的预处理器,具有更多的功能,因此如果您只想替换一些变量,则可能不需要复杂性。
template.txt
Variable 1 value: ${var1}
Variable 2 value: ${var2}
数据文件
#!/usr/bin/env bash
declare var1="value 1"
declare var2="value 2"
解析器
#!/usr/bin/env bash
# args
declare file_data=$1
declare file_input=$2
declare file_output=$3
source $file_data
eval "echo \"$(< $file_input)\"" > $file_output
./parser.sh data.sh template.txt parsed_file.txt
parsed_file.txt
Variable 1 value: value 1
Variable 2 value: value 2
`…`
或$(…)
)由于使用允许执行任意命令,而由于使用eval
则可以直接执行shell代码source
。同样,输入中的双引号会被悄悄地丢弃,并且echo
可能会将其命令行选项之一误认为行首。
这是一个健壮的Bash函数,尽管使用了它,但eval
应该可以安全使用。
${varName}
输入文本中的所有变量引用都基于调用Shell的变量进行扩展。
没有其他扩展:名称未包含在中的变量引用{...}
(例如$varName
),命令替代($(...)
和传统语法`...`
),算术替代($((...))
和传统语法$[...]
)。
要将a $
视为文字,请\
-e;例如:\${HOME}
请注意,仅通过stdin接受输入。
例:
$ expandVarsStrict <<<'$HOME is "${HOME}"; `date` and \$(ls)' # only ${HOME} is expanded
$HOME is "/Users/jdoe"; `date` and $(ls)
函数源代码:
expandVarsStrict(){
local line lineEscaped
while IFS= read -r line || [[ -n $line ]]; do # the `||` clause ensures that the last line is read even if it doesn't end with \n
# Escape ALL chars. that could trigger an expansion..
IFS= read -r -d '' lineEscaped < <(printf %s "$line" | tr '`([$' '\1\2\3\4')
# ... then selectively reenable ${ references
lineEscaped=${lineEscaped//$'\4'{/\${}
# Finally, escape embedded double quotes to preserve them.
lineEscaped=${lineEscaped//\"/\\\"}
eval "printf '%s\n' \"$lineEscaped\"" | tr '\1\2\3\4' '`([$'
done
}
该函数假定没有0x1
,0x2
,0x3
,和0x4
控制字符存在于输入,因为那些字符。在内部使用-由于该函数处理text,因此应该是一个安全的假设。
eval
它也是非常安全的。
"
正确转义!)
${FOO:-bar}
或者仅在设置为-时输出某些内容${HOME+Home is ${HOME}}
。我怀疑有一点扩展,它也可能返回缺少变量的退出代码,${FOO?Foo is missing}
但目前没有 tldp.org/LDP/abs/html/parameter-substitution.html列出的列表是否有帮助
创建rendertemplate.sh
:
#!/usr/bin/env bash
eval "echo \"$(cat $1)\""
和template.tmpl
:
Hello, ${WORLD}
Goodbye, ${CHEESE}
渲染模板:
$ export WORLD=Foo
$ CHEESE=Bar ./rendertemplate.sh template.tmpl
Hello, Foo
Goodbye, Bar
$(rm -rf ~)
,则您将其作为代码运行。
eval "echo \"$(cat $1)\""
很棒!
如果您愿意使用Perl,那将是我的建议。尽管可能有一些sed和/或AWK专家可能知道如何轻松得多。如果您有一个更复杂的映射,而不仅仅是dbName来进行替换,则可以轻松扩展此映射,但此时您也可以将其放入标准Perl脚本中。
perl -p -e 's/\$\{dbName\}/testdb/s' yourfile | mysql
一个简短的Perl脚本,用于执行稍微复杂一些的操作(处理多个键):
#!/usr/bin/env perl
my %replace = ( 'dbName' => 'testdb', 'somethingElse' => 'fooBar' );
undef $/;
my $buf = <STDIN>;
$buf =~ s/\$\{$_\}/$replace{$_}/g for keys %replace;
print $buf;
如果将上述脚本命名为replace-script,则可以按以下方式使用它:
replace-script < yourfile | mysql
这是一种使外壳程序为您进行替换的方法,就像在双引号之间键入文件内容一样。
使用带有内容的template.txt示例:
The number is ${i}
The word is ${word}
下一行将使Shell插值template.txt的内容,并将结果写入标准输出。
i='1' word='dog' sh -c 'echo "'"$(cat template.txt)"'"'
说明:
i
并word
作为环境变量传递给执行sh
。sh
执行它传递的字符串的内容。echo "
'+' $(cat template.txt)
“ +' "
'"
,因此“ $(cat template.txt)
”成为的输出cat template.txt
。sh -c
变为:
echo "The number is ${i}\nThe word is ${word}"
,i
和word
是指定的环境变量。'$(rm -rf ~)'$(rm -rf ~)
则模板文件中的文字引号将与您在扩展之前添加的引号相匹配。
'$(echo a)'$(echo a)
。它产生'a'a
。发生的主要事情是对echo a
里面的第一个'
进行求值,这可能不是您期望的,因为它位于中'
,但是其行为与包含'
在带"
引号的字符串中相同。
"
重点在于扩展任何用引号引起来的字符串(包括$(...)
)。
${varname}
不是其他)更高安全风险的扩展。
echo "
,然后是带有字面含义的双引号字符串template.txt
,然后是另一个文字字符串"
,所有这些都串联成一个传递给的参数sh -c
。您是对的,'
不能匹配(因为它是由外壳消耗的而不是传递给内部的),但是可以匹配"
,因此Gotcha"; rm -rf ~; echo "
可以执行包含模板。
file.tpl:
The following bash function should only replace ${var1} syntax and ignore
other shell special chars such as `backticks` or $var2 or "double quotes".
If I have missed anything - let me know.
script.sh:
template(){
# usage: template file.tpl
while read -r line ; do
line=${line//\"/\\\"}
line=${line//\`/\\\`}
line=${line//\$/\\\$}
line=${line//\\\${/\${}
eval "echo \"$line\"";
done < ${1}
}
var1="*replaced*"
var2="*not replaced*"
template file.tpl > result.txt
\$(date)
while IFS= read -r line; do
用作read
命令,否则您将从每条输入行中去除前导和尾随空格。另外,echo
可能会将行的开头误认为其命令行选项之一,因此最好使用printf '%s\n'
。最后,双引号更安全${1}
。
我建议使用类似Sigil的东西:https : //github.com/gliderlabs/sigil
它被编译为单个二进制文件,因此在系统上安装非常容易。
然后,您可以像下面这样做一个简单的单线:
cat my-file.conf.template | sigil -p $(env) > my-file.conf
这比eval
使用正则表达式或sed
cat
使用它,<my-file.conf.template
而是使用sigil
真实的文件句柄而不是FIFO。
我在想同一件事的同时发现了这个线程。这启发了我这一点(注意反引号)
$ echo $MYTEST
pass!
$ cat FILE
hello $MYTEST world
$ eval echo `cat FILE`
hello pass! world
$(cat file)
是$(< file)
eval echo "\"$(cat FILE)\""
它,但是由于输入中的双引号被舍弃了,所以可能仍然不够。
`…`
或$(…)
)允许执行任意命令eval
。
这里有很多选择,但我想我会把我扔在堆上。它基于perl,仅针对$ {...}格式的变量,将要处理的文件作为参数,并在stdout上输出转换后的文件:
use Env;
Env::import();
while(<>) { $_ =~ s/(\${\w+})/$1/eeg; $text .= $_; }
print "$text";
当然,我并不是一个真正的perl人,因此很容易出现致命的缺陷(尽管对我有用)。
Env::import();
行,这意味着use
。另外,我建议不要先在内存中建立整个输出:只需在循环内部使用print;
而不是$text .= $_;
,然后删除循环后print
命令即可。
如果您可以控制配置文件格式,则可以使用bash本身来完成。您只需要来源(“。”)配置文件,而不是对其进行子外壳处理。这样可以确保变量是在当前外壳程序的上下文中创建的(并继续存在),而不是子外壳程序(在子外壳程序退出时变量消失)中。
$ cat config.data
export parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA
export parm_user=pax
export parm_pwd=never_you_mind
$ cat go.bash
. config.data
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
如果您的配置文件不能是Shell脚本,则可以在执行之前对其进行“编译”(编译取决于您的输入格式)。
$ cat config.data
parm_jdbc=jdbc:db2://box7.co.uk:5000/INSTA # JDBC URL
parm_user=pax # user name
parm_pwd=never_you_mind # password
$ cat go.bash
cat config.data
| sed 's/#.*$//'
| sed 's/[ \t]*$//'
| sed 's/^[ \t]*//'
| grep -v '^$'
| sed 's/^/export '
>config.data-compiled
. config.data-compiled
echo "JDBC string is " $parm_jdbc
echo "Username is " $parm_user
echo "Password is " $parm_pwd
$ bash go.bash
JDBC string is jdbc:db2://box7.co.uk:5000/INSTA
Username is pax
Password is never_you_mind
在您的特定情况下,您可以使用类似以下内容的方法:
$ cat config.data
export p_p1=val1
export p_p2=val2
$ cat go.bash
. ./config.data
echo "select * from dbtable where p1 = '$p_p1' and p2 like '$p_p2%' order by p1"
$ bash go.bash
select * from dbtable where p1 = 'val1' and p2 like 'val2%' order by p1
然后将go.bash的输出通过管道传输到MySQL和voila中,希望您不会破坏数据库:-)。
go.bash
)中谈论echo语句,那您将有错的根源-它们不是解决方案的一部分,它们只是表明变量正在被使用的一种方式正确设置。