开发者

Indirect parameter substitution in shell script

开发者 https://www.devze.com 2023-01-29 02:59 出处:网络
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 th

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_开发者_StackOverflowarg "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.


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


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


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!


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.


A safer solution with eval:

v=1

valid_var_name='[[:alpha:]_][[:alnum:]_]*$'

print_arg() {
    local arg=$1
    if ! expr "$arg" : "$valid_var_name" >/dev/null; then
        echo "$0: invalid variable name ($arg)" >&2
        exit 1
    fi
    local argval
    eval argval=\$$arg
    echo "$argval"
}

print_arg v
print_arg 'v; echo test'

Inspired by the following answer.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号