【问题标题】:How to expand shell variables in a text file?如何在文本文件中扩展 shell 变量?
【发布时间】:2013-01-04 06:23:28
【问题描述】:

考虑一个 ASCII 文本文件(假设它包含非 shell 脚本语言的代码):

Text_File.msh:

spool on to '$LOG_FILE_PATH/logfile.log';
login 'username' 'password';
....

现在,如果这是一个 shell 脚本,我可以将其作为 $ sh Text_File.msh 运行,并且 shell 会自动扩展变量。 我想做的是让 shell 扩展这些变量,然后创建一个新文件为Text_File_expanded.msh,如下所示:

Text_File_expanded.msh:

spool on to '/expanded/path/of/the/log/file/../logfile.log';
login 'username' 'password';
....

考虑:

$ a=123
$ echo "$a"
123

所以从技术上讲,这应该可以解决问题:

$ echo "`cat Text_File.msh`" > Text_File_expanded.msh

...但它没有按预期工作,并且输出文件与源文件相同。

所以我不确定如何实现这一点。我的目标是更容易维护嵌入在我的非 shell 脚本中的目录路径。 这些脚本不能包含任何 UNIX 代码,因为它不是由 UNIX shell 编译的。

【问题讨论】:

  • 如上所示,您的变量会在单引号内吗?
  • @Guru - 目前,它在文本文件中使用单引号或根本没有引号。

标签: bash shell unix solaris ksh


【解决方案1】:

这个问题已在另一个帖子中提出,这是 IMO 的最佳答案:

export LOG_FILE_PATH=/expanded/path/of/the/log/file/../logfile.log
cat Text_File.msh | envsubst > Text_File_expanded.msh

如果在 Mac 上,请先安装 gettextbrew install gettext

见: Forcing bash to expand variables in a string loaded from a file

【讨论】:

    【解决方案2】:

    这个解决方案并不优雅,但它确实有效。创建脚本调用shell_expansion.sh:

    echo 'cat <<END_OF_TEXT' >  temp.sh
    cat "$1"                 >> temp.sh
    echo 'END_OF_TEXT'       >> temp.sh
    bash temp.sh >> "$2"
    rm temp.sh
    

    然后您可以按如下方式调用此脚本:

    bash shell_expansion.sh Text_File.msh Text_File_expanded.msh
    

    【讨论】:

    • 谢谢@Hai Vu。我首先设置变量$ LOG_FILE_PATH=/some/path,然后运行$ ./shell_expansion.sh Text_File.msh Text_File_expanded.msh(还有$ bash shell_expansion.sh Text_File.msh Text_File_expanded.msh)——但在Text_File_expanded.msh 中,$LOG_FILE_PATH 条目被替换为空格。我可能做错了..
    • 我没有exported 变量.. 在我尝试$ export LOG_FILE_PATH=/sone/path/ 之后它工作正常。谢谢! :)
    • 不需要使用临时文件,只需使用eval - 请参阅@ThirteenThirtySeven 的答案。此外,使用临时文件的方法有些问题(shell_expansion.sh 的多个实例将全部写入同一个文件、权限问题、错误检查等 - 临时文件会产生许多潜在的并发症,您需要准备好处理或知道要避免肯定)。
    • eval 比使用临时文件更危险。
    • 我发现echo -e "cat &lt;&lt;END_OF_TEXT\n$(cat [filename])\nEND_OF_TEXT" | bash 在没有临时文件的情况下工作得很好。
    【解决方案3】:

    如果你想要它在一行中(我不是 bash 专家,所以可能会有一些警告,但它在我尝试过的任何地方都可以使用):

    当 test.txt 包含时

    ${line1}
    ${line2}
    

    然后:

    >line1=fark
    >line2=fork
    >value=$(eval "echo \"$(cat test.txt)\"")
    >echo "$value"
    line1 says fark
    line2 says fork
    

    显然,如果您只想打印它,您可以取出多余的value=$()echo "$value"

    【讨论】:

    • 如果test.txt 包含,例如"; touch /etc/your_system_is_pwned; echo ",您将看到坏事发生。
    • 谢谢你,这太棒了
    【解决方案4】:

    如果 Perl 解决方案适合您:

    示例文件:

    $ cat file.sh
    spool on to '$HOME/logfile.log';
    login 'username' 'password';
    

    解决方案:

    $ perl -pe 's/\$(\w+)/$ENV{$1}/g' file.sh
    spool on to '/home/user/logfile.log';
    login 'username' 'password';
    

    【讨论】:

    • 感谢@Guru - 我试过了,效果很好。因为我需要避免对 Perl 的依赖,所以我不能应用这个解决方案。我不是 Perl 专家,但我们确定 \w 将始终正确捕获 shell 变量名吗?
    • 好吧,我看了一下,得到了答案 - 1. PERL reference: \w 将匹配“word”字符(字母数字加“”)。 2.UNIX shell reference:变量名只能包含字母数字字符和“”。
    • 大家好 - 我上面评论中的错别字:"_" 被视为未标记为代码,因此被用来使文本斜体..对不起。
    • 我试过你的脚本。它几乎可以正常工作。它将确实替换所有 env 变量,无论它们是否已定义。我认为避免替换未定义的变量会很有用。
    【解决方案5】:

    上述答案的一个限制是它们都需要将变量导出到环境中。以下是我想出的允许变量在当前 shell 脚本本地的方法:

    #!/bin/sh
    FOO=bar;
    FILE=`mktemp`; # Let the shell create a temporary file
    trap 'rm -f $FILE' 0 1 2 3 15;   # Clean up the temporary file 
    
    (
      echo 'cat <<END_OF_TEXT'
      cat "$@"
      echo 'END_OF_TEXT'
    ) > $FILE
    . $FILE
    

    上面的例子允许在命令行命名的文件中替换变量$FOO。我确信它可以改进,但到目前为止这对我有用。

    感谢之前两个答案的想法!

    【讨论】:

    • 感谢@PaulGear!欢迎来到 StackOverflow。 +1 用于使用trap 清理临时文件。我建议进行一些修改,以使像我这样的新手更容易理解代码;请检查编辑并在需要时恢复它们。谢谢
    • 我没有得到0 指的是哪个信号。
    • 0 表示正常退出而不是信号。
    • 我不知道你为什么认为 semis 在变量赋值和陷阱上是必要的,但在 echo 和 cat 上却不是,但 cmets 很有用,所以我不打算恢复。
    • 谢谢保罗。关于半决赛——在添加 cmets 时添加它们;不是它的必要,只是一种习惯的力量:)
    【解决方案6】:

    如果您要翻译的变量已知且数量有限,您可以随时自行翻译:

    sed "s/\$LOG_FILE_PATH/$LOG_FILE_PATH/g" input > output
    

    并且还假设变量本身是已知的

    【讨论】:

    • 假设 $LOG_FILE_PATH 包含斜杠将不起作用。它将在模式内简单地展开,从而导致“替代命令中的错误标志”之类的错误。刚刚用 $PATH 试了一下。
    【解决方案7】:

    此解决方案允许您在输出文件中保持相同的格式

    将以下行复制并粘贴到您的脚本中

    cat $1 | while read line
    do
      eval $line
      echo $line
      eval echo $line
    done | uniq | grep -v '\$'
    

    这将逐行读取作为参数传递的文件,然后尝试将每一行打印两次: - 一次没有替换 - 一次替换变量。 然后删除重复的行 然后删除包含可见变量($)的行

    【讨论】:

      【解决方案8】:

      创建一个 ascii 文件 test.txt,内容如下:

      Try to replace this ${myTestVariable1} 
      bla bla
      ....
      

      现在创建一个包含变量名的文件“sub.sed”,例如

      's,${myTestVariable1},'"${myTestVariable1}"',g;
      s,${myTestVariable2},'"${myTestVariable2}"',g;
      s,${myTestVariable3},'"${myTestVariable3}"',g;
      s,${myTestVariable4},'"${myTestVariable4}"',g'
      

      打开终端移动到包含 test.txt 和 sub.sed 的文件夹。
      定义要替换的变量的值

      myTestVariable1=SomeNewText
      

      现在调用 sed 来替换那个变量

      sed "$(eval echo $(cat sub.sed))" test.txt > test2.txt
      

      输出将是

      $cat test2.txt
      
      Try to replace this SomeNewText 
      bla bla
      ....
      

      【讨论】:

        【解决方案9】:

        是的 eval 应该小心使用,但它为我的问题提供了这个简单的 oneliner。以下是使用您的文件名的示例:

        eval "echo \"$(<Text_File.msh)\""
        

        出于我自己的目的,我使用 printf 而不是 echo,但这应该可以解决问题。谢谢 abyss.7 提供解决我问题的链接。希望对您有所帮助。

        【讨论】:

          【解决方案10】:

          #logfiles.list:

          $EAMSROOT/var/log/LinuxOSAgent.log
          $EAMSROOT/var/log/PanacesServer.log
          $EAMSROOT/var/log/PanacesStrutsGUI.log
          

          #我的程序:

          cat logfiles.list | while read line
              do
                  eval Eline=$line
                  echo $Eline
              done
          

          【讨论】:

            猜你喜欢
            • 2019-03-31
            • 1970-01-01
            • 2012-08-22
            • 1970-01-01
            • 1970-01-01
            • 2021-06-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多