![Bash:将多行解析为单行命令](https://linux22.com/image/72655/Bash%EF%BC%9A%E5%B0%86%E5%A4%9A%E8%A1%8C%E8%A7%A3%E6%9E%90%E4%B8%BA%E5%8D%95%E8%A1%8C%E5%91%BD%E4%BB%A4.png)
我必须编写一个 Bash 脚本来检查另一个 Bash 脚本是否包含某个命令行。由于 Bash 允许您将命令行拆分为多行,因此我的脚本必须能够在进行实际模式匹配之前合并相应的行。
如何将 Bash 脚本中的所有多行命令解析为单行命令?
例子
我想检查某个脚本是否包含LS命令 - 如果它包含LS命令,我想知道传递给哪些参数LS命令。为了回答这个问题,我可以使用sed。但因此我必须首先合并所有多行命令。
输入:
# My comment \
ls \
-a \
-l
输出:
# My comment \
ls -a -l
无效输出的示例:
# My comment ls -a -l
答案1
就在炮弹休克之前,我在 StackOverflow 上回答了一个问题关于消除 bash 脚本中的注释。我的答案使用了一个简单的技巧,即通过将脚本文件的内容包含在 中来创建一个函数tmp_() { ... }
,然后使用declare -f tmp_
它来漂亮地打印该函数。在漂亮的打印输出中,没有注释,并且以反斜杠换行符继续的行已解析为单行。 (除了内部反引号命令替换。)
还完成了一些其他重新格式化。例如,复合命令被分成几行。并且某些形式的续行不会重新格式化;例如,以管道符号结尾的行不会改变。但它应该满足这个问题中的用例。 (请参阅下面的示例输出。)
当然,需要评估函数定义,这意味着打印精美的脚本可能包含注入攻击。在我建议的代码中,函数定义是通过 bash 功能进行评估的,该功能允许导出函数并与子进程共享。当我写这个小黑客时,我相信该机制比调用更安全eval
,但事实证明我错了。
自 shellshock 以来,bash 用于导入函数定义的代码已经有了许多改进,至少阻止了一些注入攻击,但显然不能保证该过程是完全安全的。
如果您要运行正在分析的脚本,那么使用此过程来漂亮地打印它可能不会增加您的漏洞;攻击者可以简单地将危险代码直接插入到脚本中,而无需通过可能绕过函数导入代码中的安全检查的方式来隐藏攻击。
尽管如此,您应该仔细考虑安全问题,无论是这个小程序还是您可能必须执行任意脚本的任何计划。
这是漂亮打印机的版本,它可以与经过 shellshock 修补的 bash 一起使用(并且不能与以前的 bash 版本一起使用):
env "BASH_FUNC_tmp_%%=() {
$(<script_name)
}" bash -c 'declare -f tmp_' | tail -n+2
script_name
将第二行中的 ,替换为包含脚本的文件名。您可能想要调整tail
命令;它会删除包装函数名称,但不会删除脚本主体周围的大括号。
原始版本适用于 bash 的 pre-shellshock 版本,可以在引用的 SO 答案中找到。
样本。
根据提供的输入进行测试斯蒂芬·查泽拉斯:
{
echo \\;
echo a#b;
echo 'foo\
bar';
cat <<EOF
thisis joined
this 'aswell'
$(ls -l)
EOF
cat <<'EOF'
this is\
not joined
EOF
echo "$(ls -l)";
echo `ls \\
-l`
}
这与 Stéphane 的建议输出不同:
- 行已缩进,并且许多行已以分号终止。许多行中添加和/或删除了空格。
cat << E\OF
已更改为cat <<'EOF'
,其语义相同。- 末尾的反引号命令替换中的嵌套延续行尚未被修改。 (命令替换中的连续行
$(...)
被删除。)
答案2
现在,这在更多情况下有效;看看它是否符合您的预期:
sed ':loop /^[^#].*[^\\]\\$/N; s/\\\n//; t loop' input
默认情况下打印每一行;如果它在行 ($) 末尾发现反斜杠(因为它是特殊字符而转义),并且行开头没有哈希标记,则将其与修改后的下一行 (N) 连接搜索并替换反斜杠(再次转义)和换行符没有什么。如果搜索和替换执行了某些操作,则返回到“循环”标签并重新运行搜索。
输入:
# My comment \
ls \
-al
# leading comment
echo some \
long \
text
# trailing comment
ls -al
输出:
# My comment \
ls -al
# leading comment
echo some long text
# trailing comment
ls -al
答案3
这并不是一个真正的答案,只是一个关于在一般情况下工作的解决方案需要考虑的事项的注释。
#! /bin/sh -
echo \\
echo a#\
b
echo 'foo\
bar'
cat << EOF
this\
is joined
this 'as\
well'
$(ls \
-l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls \
-l)"
echo `ls \\
-l`
我对问题意图的理解是应该将其转换为:
#! /bin/sh -
echo \\
echo a#b
echo 'foo\
bar'
cat << EOF
thisis joined
this 'aswell'
$(ls -l)
EOF
cat << E\OF
this is\
not joined
EOF
echo "$(ls -l)"
echo `ls -l`
答案4
要将换行符替换为空格,您可以
tr '\n' ' '
这可能会在代码中留下一些反斜杠,因此您可能应该将反斜杠前面的换行符替换为空格
perl -pe 's/\\\n/ /'
这仍然可能会出错(这里是文档、注释等),但要获得 100% 正确,您必须编写一个完整的 shell 解析器。