简体   繁体   English

shell脚本中的间接参数替换

[英]Indirect parameter substitution in shell script

I'm having a problem with a shell script (POSIX shell under HP-UX, FWIW).我在使用 shell 脚本(HP-UX 下的 POSIX shell,FWIW)时遇到问题。 I have a function called print_arg into which I'm passing the name of a parameter as $1.我有一个名为 print_arg 的函数,我将参数名称作为 $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".我查看了在线“BASH 脚本指南”。 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.任何关于如何获取其名称作为参数传递给函数的参数值的建议都将受到赞赏。

In bash (but not in other sh implementations), indirection is done by: ${!arg}在 bash 中(但不是在其他 sh 实现中),间接通过以下方式完成: ${!arg}

Input输入

foo=bar
bar=baz

echo $foo
echo ${!foo}

Output输出

bar
baz

You could use eval , though using direct indirection as suggested by SiegeX is probably nicer if you can use bash .您可以使用eval ,但如果您可以使用bash ,则使用SiegeX 建议的直接间接可能会更好。

#!/bin/sh

foo=bar
print_arg () {
    arg=$1
    eval argval=\"\$$arg\"
    echo "$argval"
}
print_arg foo

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 .即使答案已经被接受,对于那些需要保留换行符和特殊字符(如 Escape ( \033 ))的人来说,这是另一种方法:将变量存储在base64中。

You need: bc, wc, echo, tail, tr, uuencode, uudecode你需要: 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;您也可以使用set命令并解析它的输出; with that, you don't need to treat the variable in a special way before it's accessed.这样,您无需在访问变量之前以特殊方式处理它。

A safer solution with eval :使用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 .受到以下答案的启发。

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 ${!} .它具有 POSIX 的所有笨拙,在 Bash 中会更加分类,但话又说回来,你不需要它,因为你有${!} This - in case it proves solid - would have the advantage of using only builtins and no eval .这 -如果它被证明是可靠的 - 将具有仅使用内置函数而不使用eval的优势。 If I were to construct this function using an external command, it would have to be sed .如果我要使用外部命令构造这个函数,它必须是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!请注意,在没有 eval 的情况下在 POSIX 中请求间接,必须付出笨拙的代价! So don't beat me!所以不要打我!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM