巴什参数报价和eval(Bash parameter quotes and eval)

2019-07-29 11:49发布

我已经写了一个bash日志库与我公司目前正在使用一些复杂的脚本来实现。 我一直deadset上提供的脚本文件名($ {} 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。” 这个实现,上面的调用失败(只有一个引用的句子)的工作就好了。

在最基本的层面上,我真正想要的是一个脚本,能够调用[日志功能/宏]“字符串登录特殊characers”,并有导致日志消息包含调用脚本的文件名和行号,接着日志消息。 如果可能的话,我会认为我很接近,但如果有我俯瞰这将需要不同的方法什么的,我接受这一点。 我不能强迫用户逃避所有的消息,因为这可能会导致他们不使用这个库。 这是,如果我不能找到解决这个问题,我很可能会恢复到旧的能力,这样的问题(这需要$ {} LINENO作为一个函数参数传递 - 这是侵入少得多)。

TLDR:有没有什么办法让EVAL尊重一个报价参数中的特殊字符,而不必逃跑呢?

Answer 1:

我建议避免eval如果可能的话。 为了您的日志记录的使用情况,您可以看看在shell内置caller 。 如果您需要了解更多信息,你可以使用变量BASH_SOURCEBASH_LINENOFUNCNAME 。 请注意,所有这些变量是数组,并包含完整的调用堆栈。 请看下面的例子:

#! /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 "$@"


Answer 2:

(注:此引用解决的迫在眉睫的问题,但@ nosid的有关访问调用堆栈答案要好得多)

改变你的定义_log略有下降,从标准输入,而不是采取从位置参数日志消息如下:

_log () {
    # Set up _BASH_SOURCE and _LINENO the same way

    cat <(echo -n "$(_BASH_SOURCE:$_LINENO) ") -
}

然后用此文档或字符串,在这里通过标准输入传递你的日志信息:

${LOG} <<<"This is a log message"
${LOG} <<<"I'm thrilled this works, too!"
${LOG} <<HERE
Even this long
message works as
intended!
HERE


Answer 3:

为了您的日志记录的特定情况下,你可能想看看这个函数打印出呼叫者的上下文的文件功能和行号。

可重复使用的引用函数

这个函数会做正确的报价为您提供:

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
$

这表明,在令牌的空格都被保留。



文章来源: Bash parameter quotes and eval