如何在文本文件中扩展 shell 变量?

Posted

技术标签:

【中文标题】如何在文本文件中扩展 shell 变量?【英文标题】:How to expand shell variables in a text file? 【发布时间】: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 - 目前,它在文本文件中使用单引号或根本没有引号。 相关问题:superuser.com/q/235738/126693 【参考方案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!欢迎来到 ***。 +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

【讨论】:

以上是关于如何在文本文件中扩展 shell 变量?的主要内容,如果未能解决你的问题,请参考以下文章

bat中如何筛选文本文件中某行某段文字,提取并给变量赋值

shell脚本命令如何 读取文本指定位置内容 写入另一文本指定位置并替换原内容

Shell脚本用于修改同一目录中多个文件的文件名的变量

如何使用 UNIX shell 计算一个字母在文本文件中出现的次数?

shell 脚本里面从一个文本里面读出一个数字,如何转换成整数?我需要用这个数字进行加减乘除

如何使用shell提取多个文本中相同的内容