如何在Bash中进行字符串变量的扩展?

3

我正在编写一个简单的bash调试工具,log_next_line。其预期行为是当定义了变量log时,它会读取脚本的下一行,扩展其中的变量,将其写入由$log指定的文件中,执行命令,并将其输出捕获到同一日志中。

我的问题在于变量扩展。假设我有一行bash代码存储在变量$line中。如何扩展其中的字符串,而不执行任何外部程序,也不执行管道操作(这很危险)?

简单的line=$("echo $line")根本不起作用。当我尝试line=$(bash -c "echo $line")时,我运气比较好,但是这时我需要将所有bash变量导出到派生的bash中(这一点我不知道该如何实现)。而且,如果line="echo $(rm -r /)",Bash将执行管道和外部程序,调用外部程序似乎有些过度;

有没有一种方法可以进行变量扩展,而无需从头开始编写Bash解析器? 我只需要在Linux(Ubuntu >= 14.04)下使用它。

完整函数原型如下:

function log_next_line()
{
    if [ -n "$log" ]; then
        file=${BASH_SOURCE[1]##*/}  #Takes path to the file, from where the function is called
        linenr=$((${BASH_LINENO[0]} + 1 )) #Line number
        line=`sed "${linenr}q;d" $file` #Reads this line number from the source file
        line=$("echo $line") #DOESN'T WORK. I want it to do a variable expansion
        echo "\$ $line" >>$log #Writes the variable-expanded line to the log
        $line >>$log 2>>$log #Executes the line and 
        exitstatus=$?
        if [ "$exitstatus" -ne "0" ]; then
            echo "## Exit status: $exitstatus" >>$log
        fi
        echo >>$log
        BASH_LINENO[0]=$((BASH_LINENO[0] + 1)) #Skips executing of the next line, since we have already executed it (and captured its output)
        if [ "$exitstatus" -ne "0" ]; then
            exit $exitstatus
        fi
    fi
}

以下是一个可用作测试案例的简单脚本:

#!/bin/bash

log=mylog.log

. ./log_next_line.sh

rm mylog.log

a=4
b=example
x=a

log_next_line
echo $x
log_next_line
echo ${!b}
log_next_line
touch ${!b}.txt > /dev/null
log_next_line
touch ${!x}.txt
log_next_line
if [ (( ${#a} - 6  )) -gt 10 ]; then echo "Too long string";fi
log_next_line
echo "\$a and \$x"|tee file.txt

单元测试:

如果 x=aa="example",那么我希望以下扩展:

  1. echo $x 应该是 echo a
  2. echo ${!b} 应该是 echo example
  3. touch ${!b}.txt>/dev/null 应该是 touch example.txt>/dev/null
  4. if [ (( ${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi 应该是 if [ 1 -gt 10 ]; then echo "Too long string";fi
  5. echo "\$a and \$x"|tee file.txt 应该是 echo "\$a and \$x"|tee file.txt"

这个问题是对https://unix.stackexchange.com/questions/131150/bash-is-it-safe-to-eval-bash-command的概括。那里给出的答案并不能通过我的所有测试用例。


您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - user3159253
1
你为什么要使用 line=sed "${linenr}q;d" $file 呢?这看起来像是一个扩展的噩梦。为什么不使用 line=$( sed -n ${linenr}p "$file" ) 呢?这将从 $file 中获取 $linenr,而不会在双引号中嵌入行尾符号,即使它能工作,也是一场灾难。 - David C. Rankin
1个回答

1
该函数可以通过简单的set -x在脚本的特定点打开跟踪,set +x关闭跟踪。
如果您一定希望将其实现为一行标志,可以查看this question并通过DEBUG陷阱配置-x标志。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接