如何在文本文件中扩展 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 上,请先安装 gettext
:brew 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 条目被替换为空格。我可能做错了..
我没有export
ed 变量.. 在我尝试$ export LOG_FILE_PATH=/sone/path/
之后它工作正常。谢谢! :)
不需要使用临时文件,只需使用eval
- 请参阅@ThirteenThirtySeven 的答案。此外,使用临时文件的方法有些问题(shell_expansion.sh
的多个实例将全部写入同一个文件、权限问题、错误检查等 - 临时文件会产生许多潜在的并发症,您需要准备好处理或知道要避免肯定)。
eval
比使用临时文件更危险。
我发现echo -e "cat <<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 变量?的主要内容,如果未能解决你的问题,请参考以下文章
shell脚本命令如何 读取文本指定位置内容 写入另一文本指定位置并替换原内容
如何使用 UNIX shell 计算一个字母在文本文件中出现的次数?