我编写了一个bash日志库,用我公司正在使用的一些复杂脚本来实现。在进行日志调用时,我一直在提供脚本文件名($ {BASH_SOURCE})和调用脚本的行号($ {LINENO})。但是,我不想依赖用户或实现脚本将这两个变量作为参数传递。如果这是C / C ++,我只想创建一个宏,它将“__FILE__”和“__LINE__”添加到参数列表中。
我终于能够让这部分工作了。以下是一些非常简化的摘要作为概念证明:
这是日志库:
# log.sh
LOG="eval _log \${BASH_SOURCE} \${LINENO}"
_log () {
_BASH_SOURCE=`basename "${1}"` && shift
_LINENO=${1} && shift
echo "(${_BASH_SOURCE}:${_LINENO}) $@"
}
实施测试脚本:
# MyTest.sh
. ./log.sh
${LOG} "This is a log message"
# (test.sh:5) This is a log message
这非常有效(而且,我很高兴让它首先工作)。然而,这有一个明显的问题:引号和eval之间的相互作用。如果我打电话:
${LOG} "I'm thrilled that I got this working"
# ./test.sh: eval: line 5: unexected EOF while looking for matching `''
# ./test.sh: eval: line 6: syntax error: unexpected end of file
现在,我相信我理解为什么会这样。引用的参数在传递给eval时保持不变,但此时,内容按原样放入生成的命令字符串中。我知道我可以通过做一些逃避来解决这个问题。但是,我 真的 不想强制执行脚本必须这样做。在我实现这个“eval宏”功能之前,我让用户直接调用“_log”并允许他们选择传入“$ {LINENO}”。通过这个实现,上面的失败调用(只有引用的句子)工作得很好。
在最基本的层面上,我真正想要的是一个脚本能够调用 [log function / macro]“String with log characers”并生成日志消息调用脚本的文件名和行号,后跟日志消息。如果可能的话,我会认为我非常接近,但是如果有一些我忽略的东西需要采用不同的方法,我也会对此持开放态度。我无法强制用户转义所有邮件,因为这可能会导致他们不使用此库。这是一个问题,如果我找不到解决方案,我可能会恢复到旧的功能(需要将$ {LINENO}作为函数参数传递 - 这样就不那么干扰了。)
TLDR :有没有办法让eval尊重引用参数中的特殊字符,而不必转义它们?
答案 0 :(得分:14)
我建议尽可能避免使用eval
。对于您的日志记录用例,您可以查看内置caller
的shell。如果您需要更多信息,可以使用变量BASH_SOURCE
,BASH_LINENO
和FUNCNAME
。请注意,所有这些变量都是数组并包含完整的调用堆栈。请参阅以下示例:
#! /bin/bash
function log() {
echo "[$( caller )] $*" >&2
echo "BASH_SOURCE: ${BASH_SOURCE[*]}"
echo "BASH_LINENO: ${BASH_LINENO[*]}"
echo "FUNCNAME: ${FUNCNAME[*]}"
}
function foobar() {
log "failed:" "$@"
}
foobar "$@"
答案 1 :(得分:2)
(注意:这解决了引用的直接问题,但是@nosid的答案 访问调用堆栈要好得多)
稍微更改_log
的定义,而不是标准输入
从位置参数中获取日志消息:
_log () {
# Set up _BASH_SOURCE and _LINENO the same way
cat <(echo -n "$(_BASH_SOURCE:$_LINENO) ") -
}
然后使用here doc或here string:
通过标准输入传递日志消息${LOG} <<<"This is a log message"
${LOG} <<<"I'm thrilled this works, too!"
${LOG} <<HERE
Even this long
message works as
intended!
HERE
答案 2 :(得分:0)
对于特定的日志记录情况,您可能需要查看this function,它会打印调用者上下文的文件功能和行号。
此功能将为您正确报价:
function token_quote {
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
}
用法示例:
用法示例:
$ token_quote token 'single token' token
token single\ token token
上面,请注意single token
的空格引用为\
。
$ set $(token_quote token 'single token' token)
$ eval printf '%s\\n' "$@"
token
single token
token
$
这表明令牌中的空格得以保留。