Indirect parameter substitution in shell script

2019-07-21 17:21发布

I'm having a problem with a shell script (POSIX shell under HP-UX, FWIW). I have a function called print_arg into which I'm passing the name of a parameter as $1. Given the name of the parameter, I then want to print the name and the value of that parameter. However, I keep getting an error. Here's an example of what I'm trying to do:

#!/usr/bin/sh

function print_arg
  {
  # $1 holds the name of the argument to be shown

  arg=$1

  # The following line errors off with
  #   ./test_print.sh[9]: argval=${"$arg"}: The specified substitution is not valid for this command.

  argval=${"$arg"}

  if [[ $argval != '' ]] ; then
    printf "ftp_func: $arg='$argval'\n"
  fi
  }

COMMAND="XYZ"

print_arg "COMMAND"

I've tried re-writing the offending line every way I can think of. I've consulted the local oracles. I've checked the online "BASH Scripting Guide". And I sharpened up the ol' wavy-bladed knife and scrubbed the altar until it gleamed, but then I discovered that our local supply of virgins has been cut down to, like, nothin'. Drat!

Any advice regarding how to get the value of a parameter whose name is passed into a function as a parameter will be received appreciatively.

4条回答
狗以群分
2楼-- · 2019-07-21 17:56

You could use eval, though using direct indirection as suggested by SiegeX is probably nicer if you can use bash.

#!/bin/sh

foo=bar
print_arg () {
    arg=$1
    eval argval=\"\$$arg\"
    echo "$argval"
}
print_arg foo
查看更多
对你真心纯属浪费
3楼-- · 2019-07-21 18:08

This worked surprisingly well:

#!/bin/sh
foo=bar

print_arg () {
    local line name value
    set | \
    while read line; do
        name=${line%=*} value=${line#*=\'}
        if [ "$name" = "$1" ]; then
            echo ${value%\'}
        fi
    done
}

print_arg foo

It has all the POSIX clunkiness, in Bash would be much sorter, but then again, you won't need it because you have ${!}. This -in case it proves solid- would have the advantage of using only builtins and no eval. If I were to construct this function using an external command, it would have to be sed. Would obviate the need for the read loop and the substitutions. Mind that asking for indirections in POSIX without eval, has to be paid with clunkiness! So don't beat me!

查看更多
够拽才男人
4楼-- · 2019-07-21 18:16

In bash (but not in other sh implementations), indirection is done by: ${!arg}

Input

foo=bar
bar=baz

echo $foo
echo ${!foo}

Output

bar
baz
查看更多
Deceive 欺骗
5楼-- · 2019-07-21 18:21

Even though the answer's already accepted, here's another method for those who need to preserve newlines and special characters like Escape ( \033 ): Storing the variable in base64.

You need: bc, wc, echo, tail, tr, uuencode, uudecode

Example

#!/bin/sh


#====== Definition =======#
varA="a
b
c"
# uuencode the variable
varB="`echo "$varA" | uuencode -m -`"
# Skip the first line of the uuencode output.
varB="`NUM=\`(echo "$varB"|wc -l|tr -d "\n"; echo -1)|bc \`; echo "$varB" | tail -n $NUM)`"


#====== Access =======#

namevar1=varB
namevar2=varA

echo simple eval:
eval "echo \$$namevar2"
echo simple echo: 
echo $varB
echo precise echo: 
echo "$varB"
echo echo of base64
eval "echo \$$namevar1"
echo echo of base64 - with updated newlines
eval "echo \$$namevar1 | tr ' ' '\n'"
echo echo of un-based, using sh instead of eval (but could be made with eval, too)
export $namevar1
sh -c "(echo 'begin-base64 644 -'; echo \$$namevar1 | tr ' ' '\n' )|uudecode"

Result

simple eval:
a b c
simple echo:
YQpiCmMK ====
precise echo:
YQpiCmMK
====
echo of base64
YQpiCmMK ====
echo of base64 - with updated newlines
YQpiCmMK
====
echo of un-based, using sh instead of eval (but could be made with eval, too)
a
b
c

Alternative

You also could use the set command and parse it's output; with that, you don't need to treat the variable in a special way before it's accessed.

查看更多
登录 后发表回答