Bash:将多行解析为单行命令

Bash:将多行解析为单行命令

我必须编写一个 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 解析器。

相关内容