#!/bin/bash
shopt -s extdebug
handle_@ () {
local re='[[:space:]]*@ ([^[:space:]]+)[[:space:]]+(.*)'
if [[ $BASH_COMMAND =~ $re ]]; then
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
return 1
fi
}
m() {
printf "<%s>\n" "$1"
}
trap handle_@ DEBUG
@ m 'foo bar' doo "$var" > /dev/null
@ m what about this?; date
@ m or some (parentheses)?
<'foo bar' doo "$var" > /dev/null>
<what about this?>
Sun Sep 18 18:39:38 EEST 2022
debugprint.sh: line 20: syntax error near unexpected token `('
debugprint.sh: line 20: `@ m or some (parentheses)?'
这里要注意的是,当你运行例如
该程序
m
没有得到整个命令行,但shell首先解析命令行,处理引号、变量扩展和类似的东西。该命令的最终结果更像是一个单独的字符串数组,在本例中为 [let
,it
,be
,foo bar
]。同样,在
m
只会看到 [Bond, James Matthew Bond
],并且不知道所涉及的变量和引号。这意味着常规程序无法访问原始输入行。
在Any shell 中,在没有转义或引用的情况下关闭扩展的情况下,使用 pre-exec 处理程序的 zsh 有解决方法?和zsh: alias or shell function 只回显它的命令行,包括 shell control characters,但我想我没见过 Bash 的。
最接近你的是
DEBUG
陷阱,它看到在扩展、报价处理和重定向之前要执行的命令。但它并不完整,因为它一次只能看到一个简单的命令。评论会在看到它们之前被删除,并且未引用的;
,&
,|
,&&
,||
,(
或)
(至少)仍将被解析。无论如何,我们可以尝试:
该脚本使用
DEBUG
陷阱检测以 开头的命令@
,将下一个以空格分隔的“单词”作为要运行的命令,并将其余的作为单个参数传递给它。从陷阱处理程序返回1
告诉外壳程序实际上不以正常方式运行命令,防止可能的错误消息并阻止任何嵌入式扩展运行(这需要extdebug
设置。)输出是这样的,显示
date
分号之后的运行,并且括号破坏了语法:老实说,这有点恶心,特殊字符的限制比较随意,我不保证脚本也没有其他问题。
一般来说,如果您计划将此用于某种生产用途,我建议重新考虑程序的结构并通过文件或例如 here-doc 获取字符串,具体取决于您在做什么。
一个 here-doc 使得输入原始字符串变得相对容易,尽管它通过命令的
stdin
:那打印
使用带引号的 here-doc 分隔符字符串 (in
<<'EOF'
),您可以在其中放置任意文本和字符,而无需担心引号。(当然,除了分隔符字符串本身,但您可以使用随机生成的字符串来代替EOF
.)您必须说服 shell 单引号是变量值的一部分,如下所示:
shell
'
在函数看到它们之前解释引号,并将对 eg"
和执行相同的操作\
。您可以使用 来逃避它们\
,例如这里的问题是您正在传递未引用的输入。因此,在调用您的函数之前
'
,shell 会使用。如果你使用了 a 也会发生同样的情况,如果你使用了类似or的通配符,它也会失败:"
*
?
至少对我来说,最干净的解决方案是始终传递您的输入引用。这将保护任何具有特殊含义的字符不受 shell 的影响,因此确保它们不会被原封不动地传递给您的函数: