1

我有一个文件,其中包含文本和对环境变量的引用。例子:

#PRINTME It is always fun to start your week on a sunny ${DAY_OF_WEEK}
#PRINTME My name is ${USERNAME}, you killed my father - prepare to die!
Unrelated gibberish - not to be printed
...

将其视为元数据。

我想定义一个命令来greps这个文件并打印标记的所有内容#PRINTME并评估环境变量。

我这样做了:grep #HELP myfile | sed "s/#PRINTME //g" | awk '{print $1}'但我的输出是

It is always fun to start your week on a sunny ${DAY_OF_WEEK}
My name is ${USERNAME}, you killed my father - prepare to die!

代替

It is always fun to start your week on a sunny Monday
My name is Inigo Montoya, you killed my father - prepare to die!

有没有一种 SHELL 方式来做我想做的事?我正在使用TCSH- 无法更改。grep替换,sed和没有问题awk

4

4 回答 4

3

我会使用 Perl,但它可以用任何脚本语言编写——Python、Ruby、Tcl/Tk、...

这只是一个单行:

perl -n -e 'next unless m/^#PRINTME /;s/#PRINTME //;s/\$\{(\w+\)\}/$ENV{$1}/eg;print;'

-n意味着读取行但不自动打印它们(想想sed -n)。next跳过不打印的行。第一个替换删除打印标记。烟火在第二个替代品中:

  • 查找${WORD},然后将其替换为$ENV{WORD},使用e选项将替换评估为表达式(可能是不规则的,因为此时它不是正则表达式;它只是一个普通表达式),并在全局范围内这样做g

然后打印剩下的。

(代码现已测试。)

$ cat xx.sh
DAY_OF_WEEK=Tuesday USERNAME="Inigo Montoya" \
perl -n -e 'next unless m/^#PRINTME /; s/#PRINTME //; s/\$\{(\w+)\}/$ENV{$1}/eg; print;' <<EOF

#PRINTME It is always fun to start your week on a sunny ${DAY_OF_WEEK}
#PRINTME My name is ${USERNAME}, you killed my father - prepare to die!
Unrelated gibberish - not to be printed
...

EOF
$ sh xx.sh
It is always fun to start your week on a sunny Tuesday
My name is Inigo Montoya, you killed my father - prepare to die!
$
于 2012-07-17T21:14:07.743 回答
2

你可以用 Gawk 做到这一点:

gawk '

  /^#PRINTME/ {

    # Remove prefix
    sub( /^#PRINTME /, "" )

    # Loop while the line contains variables
    while( /\$\{[^}]+\}/ ) {

      # Extract the first variable name
      VAR = gensub( /^[^$]*\$\{([^}]+)\}.*$/, "\\1", 1 )

      # Replace it with its value
      gsub( "\\$\\{" VAR "\\}", ENVIRON[ VAR ] )
    }

    print

 }
'

该脚本一一查找变量并用它们的值替换它们。

如果您希望操作系统为您评估变量,则更容易(并且在标准 Awk 中可行):

awk '/^#PRINTME/ { sub( /^#PRINTME /, "" ) ; system( "echo " $0 ) }'

这一次,我们构建了一个 echo 命令并将其传递给 shell。

于 2012-07-17T21:57:14.653 回答
2

您可以ENVIRONawk. 试试这个,例如:

awk 'BEGIN {print ENVIRON["HOME"]}' </dev/null
于 2012-07-17T21:10:57.917 回答
1

你想处理像这样的表达式${foo:-bar}吗?您在评论中提到要评估该行是否包含命令,因此您可能只想使用eval. 请注意,如果您有不受控制的输入,则会涉及大量安全风险。在sh中,您可以执行以下操作:

sh$ < myfile sed -e '/#HELP/!d' -e 's/#PRINTME //g' |
 while read line; do eval echo "\"$line\""; done 

如果您真的被迫使用 tcsh,请尝试:

tcsh$ sh -c '< myfile sed -e "/#HELP/!d" -e "s/#PRINTME //g" |\
            while read line; do eval echo "\"$line\""; done'

(您的代码过滤掉了 #HELP 行,尽管您没有提及它,并且您发布的输出表明您awk '{print $1}'是真的awk '{print $0}'。我已将所有这些组合到单个 sed 调用中。您也可以这样做sed -n '/#HELP/s/#PRINTME //gp',而且您应该几乎当然考虑用^)锚定模式

于 2012-07-18T11:24:19.340 回答