How do I perform variable expansion of a string variable in bash?
I'm writing a simple tool bash debugging log_next_line
. Intended behavior: when a variable is defined log
, it reads the next script line, expands the variables on it, writes it to the file specified $log
, executes the command, and dumps its output to the same log.
I have a problem with variable expansion. Suppose I have a line of bash code in a variable $line
. How can I expand the lines in it without executing any external programs and without executing protocols (which is dangerous)?
Simple line=$("echo $line")
doesn't work at all. When I try line=$(bash -c "echo $line")
, I'm in luck, but then I need to export ALL bash variables to a spawned bash (which I have no idea how to do). And calling an external program seems overkill for her; in addition, bash will execute pipes and external programs if for example line="echo $(rm -r /)"
.
Is there a way to do variable expansion that doesn't involve writing a bash parser from scratch ;-)? I need it to work under Linux (Ubuntu> = 14.04 to be precise).
For the complete picture, I include the function prototype:
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
}
And a simple script that can be used as a test case
#!/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
unit tests:
if x=a
, a="example"
then I want the following extensions:
-
echo $x
should beecho a
. -
echo ${!b}
it should beecho example
-
touch ${!b}.txt>/dev/null
it should betouch example.txt>/dev/null
-
if [ (( ${#a} - 6 )) -gt 10 ]; then echo "Too long string";fi
it should beif [ 1 -gt 10 ]; then echo "Too long string";fi
-
echo "\$a and \$x"|tee file.txt
it should beecho "\$a and \$x"|tee file.txt"
This question is a generalization of https://unix.stackexchange.com/questions/131150/bash-is-it-safe-to-eval-bash-command . The answers given here don't pass all of my test cases.
source to share
the function can be implemented as simple set -x
to enable tracing at a given point in the script and set +x
to disable it.
If you definitely want to implement it as a single line flag, I would go over this question and set up the flag -x
through the DEBUG hook.
source to share