[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Help-bash] storing a command in a variable
From: |
Bob Proulx |
Subject: |
Re: [Help-bash] storing a command in a variable |
Date: |
Fri, 12 Apr 2013 14:22:47 -0600 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
address@hidden wrote:
> Often I do something like
>
> cmd="command arg"
> echo "${cmd}" >>my_log
> eval "${cmd}" 2>&1 >>my_log
>
> and many times I have read on this list that storing a command in a
> variable is a bad thing to do
> What is a better/safer way of doing this
<gratuitous-editorial-remark>
In Perl there are many times when using eval on a block is the normal
and right thing to do such as to trap errors. I don't know but I
think the propagation of use of eval in the shell often stems from
crossover knowledge coming from other places such as Perl's use of
it. In the shell using eval is something that must be done much more
carefully. Approach it with caution.
</gratuitous-editorial-remark>
If I am trying to do something simple then I often will store the
arguments in a variable. I will sometimes store the program name in a
variable. I will contrive something. (Other people will pick this
apart because it is so contrived. But it would work on any system and
on even very old shells. So *I* would do it this way because I would.)
if ...something...; then
rsync=xrsyncspecial
else
rsync=rsync
fi
opts="-a"
if ...something... ; then
opts="$opts -v"
fi
if ! $rsync $opts $srcfile $dstdir/; then
As long as it is *simple* then the above works just fine. Simple is
the word. There isn't anything surprising in the above.
But if you are trying to do something with more complications then
create a function. Put all of the complexity into the function. Here
is another contrived example cut down from something else but I put
here to show that functions may be used to simplify calling complex
operations.
copy() {
src=$1
dst=$2
test -f "$src" || { echo "Error: copy without file source." 1>&2; exit 1 ;}
case $dst in
*/)
# The destination is a directory. Rewrite.
if ! copy "$src" "$dst$(basename $src)"; then
return 1
fi
;;
*)
rsync -t -p "$src" "$dst" || return 1
;;
esac
return 0
}
if ! copy $source $destination; then
Don't pick that above cut down contrived example apart too far. The
takeaway is that functions can contain complex operations. Perhaps
you should alway think about using functions instead of eval. I
almost never use eval.
> cmd="command arg"
> echo "${cmd}" >>my_log
> eval "${cmd}" 2>&1 >>my_log
In another location you mentioned using -x to trace through the
script and needing to cya and doing something like the above to be
able to debug it. Why is this difficult for you? Just use -x and
trace through the statements.
Something I do when there aren't other markers is that I will insert a
noop true statement with hints for me as a marker. Remember that ":"
is a synonym for true. It is a command. Therefore it and any
arguments will be printed. It is *NOT* a comment. Anything you put
on the line will be executed. But that is why it will show up in
tracing. Don't put any backticks `...` or $(...) statements in there
or they will be executed. Definitely not a comment. But useful markers.
case $something in
foo) ...do foo stuff... ;;
bar) ...do bar stuff... ;;
esac
That will disappear entirely if something isn't foo or bar. If I want
to trace that I will insert a debugging noop so that it will clue me
in during -x tracing.
: "debug: before case $something in"
case $something in
foo) ...do foo stuff... ;;
bar) ...do bar stuff... ;;
esac
: "debug: after case $something in"
Then the markers will show up in the -x tracing out.
Bob