bash中的动态变量名称

发布于 2025-01-25 15:16:09 字数 544 浏览 3 评论 0 原文

我对狂欢脚本感到困惑。

我有以下代码:

function grep_search() {
    magic_way_to_define_magic_variable_$1=`ls | tail -1`
    echo $magic_variable_$1
}

我希望能够创建一个包含命令的第一个参数的变量名称,并具有 ls 的最后一行的值。

因此,为了说明我想要的东西:

$ ls | tail -1
stack-overflow.txt

$ grep_search() open_box
stack-overflow.txt

那么,我应该如何定义/声明 $ magic_way_to_define_magic_variable_ $ 1 ,我应该如何在脚本中称呼它?

我尝试过 eval $ {...} \ $$ {...} ,但我仍然很困惑。

I am confused about a bash script.

I have the following code:

function grep_search() {
    magic_way_to_define_magic_variable_$1=`ls | tail -1`
    echo $magic_variable_$1
}

I want to be able to create a variable name containing the first argument of the command and bearing the value of e.g. the last line of ls.

So to illustrate what I want:

$ ls | tail -1
stack-overflow.txt

$ grep_search() open_box
stack-overflow.txt

So, how should I define/declare $magic_way_to_define_magic_variable_$1 and how should I call it within the script?

I have tried eval, ${...}, \$${...}, but I am still confused.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(20

つ可否回来 2025-02-01 15:16:09

我最近一直在寻找更好的方法。对我来说,联想阵列听起来像是过度杀伤。看看我发现的内容:

suffix=bzz
declare prefix_$suffix=mystr

...然后...

varname=prefix_$suffix
echo ${!varname}

docs

“ $”字符引入参数扩展,命令替代或算术扩展。 ...

参数扩展的基本形式为$ {参数}。参数的值被取代。 ...

如果参数的第一个字符是感叹点(!),而参数不是nameref,则会引入一个间接级别。 Bash使用通过将其余参数作为新参数形成的值;然后将其扩展,并在其余的扩展中使用该值,而不是原始参数的扩展。这被称为间接扩展。该值受Tilde扩展,参数扩展,命令替代和算术扩展的约束。 ...

I've been looking for better way of doing it recently. Associative array sounded like overkill for me. Look what I found:

suffix=bzz
declare prefix_$suffix=mystr

...and then...

varname=prefix_$suffix
echo ${!varname}

From the docs:

The ‘$’ character introduces parameter expansion, command substitution, or arithmetic expansion. ...

The basic form of parameter expansion is ${parameter}. The value of parameter is substituted. ...

If the first character of parameter is an exclamation point (!), and parameter is not a nameref, it introduces a level of indirection. Bash uses the value formed by expanding the rest of parameter as the new parameter; this is then expanded and that value is used in the rest of the expansion, rather than the expansion of the original parameter. This is known as indirect expansion. The value is subject to tilde expansion, parameter expansion, command substitution, and arithmetic expansion. ...

悍妇囚夫 2025-02-01 15:16:09

使用具有命令名称的关联数组。

# Requires bash 4, though
declare -A magic_variable=()

function grep_search() {
    magic_variable[$1]=$( ls | tail -1 )
    echo ${magic_variable[$1]}
}

如果您不能使用关联阵列(例如,必须支持 bash 3),则可以使用声明来创建动态变量名称:

declare "magic_variable_$1=$(ls | tail -1)"

并使用间接参数扩展来访问价值。

var="magic_variable_$1"
echo "${!var}"

请参阅Bashfaq:间接 - 评估间接/参考变量

Use an associative array, with command names as keys.

# Requires bash 4, though
declare -A magic_variable=()

function grep_search() {
    magic_variable[$1]=$( ls | tail -1 )
    echo ${magic_variable[$1]}
}

If you can't use associative arrays (e.g., you must support bash 3), you can use declare to create dynamic variable names:

declare "magic_variable_$1=$(ls | tail -1)"

and use indirect parameter expansion to access the value.

var="magic_variable_$1"
echo "${!var}"

See BashFAQ: Indirection - Evaluating indirect/reference variables.

半透明的墙 2025-02-01 15:16:09

除了关联阵列外,还有几种方法可以实现bash中的动态变量。请注意,所有这些技术都呈现出风险,在本答案结束时进行了讨论。

在以下示例中,我将假设 i = 37 ,并且您想别名名为 var_37 的变量,其初始值为 lolilol

方法1。使用“指针”变量,

您可以简单地将变量的名称存储在间接变量中,与C指针不同。然后,bash具有读取的语法 aleased变量: $ {!name} 扩展到该变量的值,其名称是变量 name name name name < /代码>。您可以将其视为两个阶段的扩展: $ {!name} 将扩展到 $ var_37 ,它扩展到 lolilol

name="var_$i"
echo "$name"         # outputs “var_37”
echo "${!name}"      # outputs “lolilol”
echo "${!name%lol}"  # outputs “loli”
# etc.

不幸的是,修改的同类变量没有对应语法。相反,您可以使用以下技巧之一来实现作业。

1a。用 eval eval 分配

是邪恶的,但也是实现我们目标的最简单,最便携的方法。您必须小心地逃脱分配的右侧,因为它将两次评估。这样做的一种简单而系统的方法是事先评估右侧(或使用 printf%q )。

您应该手动检查左侧是有效的变量名称,或带有索引的名称(如果是 Ever> Evil_code#?)。相比之下,下面的所有其他方法都会自动执行它。

# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit

value='babibab'
eval "$name"='$value'  # carefully escape the right-hand side!
echo "$var_37"  # outputs “babibab”

缺点:

  • 不检查变量名称的有效性。
  • 评估是邪恶的。
  • eval 是邪恶的。
  • eval 是邪恶的。

1b。用读取

读取内置的允许您将值分配给您提供名称的变量,这一事实可以与此处串触相结合:

IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37"  # outputs “babibab\n”

ifs part和选项 -r 确保将值分配给AS-IS,而选项 -d''允许分配多行值。由于最后一个选项,命令使用非零退出代码返回。

请注意,由于我们正在使用此处的弦,因此 newline字符附加到值。

despsides:

  • 有些晦涩;
  • 使用非零退出代码返回;
  • 将新线附加到该值。

1C。用 printf 分配

自Bash 3.1(发布2005年), printf 内置也可以将其结果分配给给出名称的变量。与以前的解决方案相反,它只是有效的,不需要额外的努力来逃脱事物,以防止分裂等。

printf -v "$name" '%s' 'babibab'
echo "$var_37"  # outputs “babibab”

弊端:

  • 便携式(但是,很好)。

方法2。自Bash 4.3(发布2014年发布)以来,使用“参考”变量

neclare indentin具有一个选项 -n 用于创建一个变量,该变量是“名称参考”到另一个变量,就像C ++引用一样。就像方法1中一样,参考将其名称存储在别名变量的名称中,但是每次访问参考(用于读取或分配)时,BASH都会自动解决间接。

此外,BASH具有一种特殊且非常令人困惑的语法,以获取参考本身的价值,请自己判断: $ {!ref}

declare -n ref="var_$i"
echo "${!ref}"  # outputs “var_37”
echo "$ref"     # outputs “lolilol”
ref='babibab'
echo "$var_37"  # outputs “babibab”

这并不能避免下面解释的陷阱,但至少它使语法直接。

缺点:

  • 不携带。

风险

所有这些混叠技术都带来了几种风险。第一个是每次解决间接时执行任意代码(用于阅读或分配)。实际上,您也可以像 var_37 那样而不是标量变量名称,也可以像 arr [42] 一样别名。但是Bash每次需要评估方括号的内容,因此Aliasing arr [$(do_evil)] 将产生意想不到的效果……因此,仅在控制时使用这些技术别名的出处

function guillemots {
  declare -n var="$1"
  var="«${var}»"
}

arr=( aaa bbb ccc )
guillemots 'arr[1]'  # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]'  # writes twice into date.out
            # (once when expanding var, once when assigning to it)

第二种风险是创建一个环状别名。由于狂欢变量是通过其名称而不是通过其范围来标识的,因此您可以无意间为自己创建一个别名(同时认为它会从封闭范围中吸用变量)。使用通用变量名称时,这可能会特别发生(例如 var )。结果,才能在控制别名变量的名称的名称时使用这些技术。

function guillemots {
  # var is intended to be local to the function,
  # aliasing a variable which comes from outside
  declare -n var="$1"
  var="«${var}»"
}

var='lolilol'
guillemots var  # Bash warnings: “var: circular name reference”
echo "$var"     # outputs anything!

Source:

Beyond associative arrays, there are several ways of achieving dynamic variables in Bash. Note that all these techniques present risks, which are discussed at the end of this answer.

In the following examples I will assume that i=37 and that you want to alias the variable named var_37 whose initial value is lolilol.

Method 1. Using a “pointer” variable

You can simply store the name of the variable in an indirection variable, not unlike a C pointer. Bash then has a syntax for reading the aliased variable: ${!name} expands to the value of the variable whose name is the value of the variable name. You can think of it as a two-stage expansion: ${!name} expands to $var_37, which expands to lolilol.

name="var_$i"
echo "$name"         # outputs “var_37”
echo "${!name}"      # outputs “lolilol”
echo "${!name%lol}"  # outputs “loli”
# etc.

Unfortunately, there is no counterpart syntax for modifying the aliased variable. Instead, you can achieve assignment with one of the following tricks.

1a. Assigning with eval

eval is evil, but is also the simplest and most portable way of achieving our goal. You have to carefully escape the right-hand side of the assignment, as it will be evaluated twice. An easy and systematic way of doing this is to evaluate the right-hand side beforehand (or to use printf %q).

And you should check manually that the left-hand side is a valid variable name, or a name with index (what if it was evil_code # ?). By contrast, all other methods below enforce it automatically.

# check that name is a valid variable name:
# note: this code does not support variable_name[index]
shopt -s globasciiranges
[[ "$name" == [a-zA-Z_]*([a-zA-Z_0-9]) ]] || exit

value='babibab'
eval "$name"='$value'  # carefully escape the right-hand side!
echo "$var_37"  # outputs “babibab”

Downsides:

  • does not check the validity of the variable name.
  • eval is evil.
  • eval is evil.
  • eval is evil.

1b. Assigning with read

The read builtin lets you assign values to a variable of which you give the name, a fact which can be exploited in conjunction with here-strings:

IFS= read -r -d '' "$name" <<< 'babibab'
echo "$var_37"  # outputs “babibab\n”

The IFS part and the option -r make sure that the value is assigned as-is, while the option -d '' allows to assign multi-line values. Because of this last option, the command returns with an non-zero exit code.

Note that, since we are using a here-string, a newline character is appended to the value.

Downsides:

  • somewhat obscure;
  • returns with a non-zero exit code;
  • appends a newline to the value.

1c. Assigning with printf

Since Bash 3.1 (released 2005), the printf builtin can also assign its result to a variable whose name is given. By contrast with the previous solutions, it just works, no extra effort is needed to escape things, to prevent splitting and so on.

printf -v "$name" '%s' 'babibab'
echo "$var_37"  # outputs “babibab”

Downsides:

  • Less portable (but, well).

Method 2. Using a “reference” variable

Since Bash 4.3 (released 2014), the declare builtin has an option -n for creating a variable which is a “name reference” to another variable, much like C++ references. Just as in Method 1, the reference stores the name of the aliased variable, but each time the reference is accessed (either for reading or assigning), Bash automatically resolves the indirection.

In addition, Bash has a special and very confusing syntax for getting the value of the reference itself, judge by yourself: ${!ref}.

declare -n ref="var_$i"
echo "${!ref}"  # outputs “var_37”
echo "$ref"     # outputs “lolilol”
ref='babibab'
echo "$var_37"  # outputs “babibab”

This does not avoid the pitfalls explained below, but at least it makes the syntax straightforward.

Downsides:

  • Not portable.

Risks

All these aliasing techniques present several risks. The first one is executing arbitrary code each time you resolve the indirection (either for reading or for assigning). Indeed, instead of a scalar variable name, like var_37, you may as well alias an array subscript, like arr[42]. But Bash evaluates the contents of the square brackets each time it is needed, so aliasing arr[$(do_evil)] will have unexpected effects… As a consequence, only use these techniques when you control the provenance of the alias.

function guillemots {
  declare -n var="$1"
  var="«${var}»"
}

arr=( aaa bbb ccc )
guillemots 'arr[1]'  # modifies the second cell of the array, as expected
guillemots 'arr[$(date>>date.out)1]'  # writes twice into date.out
            # (once when expanding var, once when assigning to it)

The second risk is creating a cyclic alias. As Bash variables are identified by their name and not by their scope, you may inadvertently create an alias to itself (while thinking it would alias a variable from an enclosing scope). This may happen in particular when using common variable names (like var). As a consequence, only use these techniques when you control the name of the aliased variable.

function guillemots {
  # var is intended to be local to the function,
  # aliasing a variable which comes from outside
  declare -n var="$1"
  var="«${var}»"
}

var='lolilol'
guillemots var  # Bash warnings: “var: circular name reference”
echo "$var"     # outputs anything!

Source:

美煞众生 2025-02-01 15:16:09

下面的示例返回$ name_of_var的值

var=name_of_var
echo $(eval echo "\$var")

Example below returns value of $name_of_var

var=name_of_var
echo $(eval echo "\$var")
情泪▽动烟 2025-02-01 15:16:09

使用声明

不需要在其他答案上使用前缀,也不需要。仅使用 声明 双引号参数扩展

我经常使用以下技巧解析参数列表conaning n to n 格式为 key = key = value eleseke equale = etherkeque et eett et eett et et et etceet = etc ,例如:

# brace expansion just to exemplify
for variable in {one=foo,two=bar,ninja=tip}
do
  declare "${variable%=*}=${variable#*=}"
done
echo $one $two $ninja 
# foo bar tip

一样扩展argv列表

for v in "$@"; do declare "${v%=*}=${v#*=}"; done

但是:但是像额外 尖端

# parse argv's leading key=value parameters
for v in "$@"; do
  case "$v" in ?*=?*) declare "${v%=*}=${v#*=}";; *) break;; esac
done
# consume argv's leading key=value parameters
while test $# -gt 0; do
  case "$1" in ?*=?*) declare "${1%=*}=${1#*=}";; *) break;; esac
  shift
done

Use declare

There is no need on using prefixes like on other answers, neither arrays. Use just declare, double quotes, and parameter expansion.

I often use the following trick to parse argument lists contanining one to n arguments formatted as key=value otherkey=othervalue etc=etc, Like:

# brace expansion just to exemplify
for variable in {one=foo,two=bar,ninja=tip}
do
  declare "${variable%=*}=${variable#*=}"
done
echo $one $two $ninja 
# foo bar tip

But expanding the argv list like

for v in "$@"; do declare "${v%=*}=${v#*=}"; done

Extra tips

# parse argv's leading key=value parameters
for v in "$@"; do
  case "$v" in ?*=?*) declare "${v%=*}=${v#*=}";; *) break;; esac
done
# consume argv's leading key=value parameters
while test $# -gt 0; do
  case "$1" in ?*=?*) declare "${1%=*}=${1#*=}";; *) break;; esac
  shift
done
紫瑟鸿黎 2025-02-01 15:16:09

在这里将两个高度评价的答案结合到一个完整的示例中,希望有用且不言而喻:

#!/bin/bash

intro="You know what,"
pet1="cat"
pet2="chicken"
pet3="cow"
pet4="dog"
pet5="pig"

# Setting and reading dynamic variables
for i in {1..5}; do
        pet="pet$i"
        declare "sentence$i=$intro I have a pet ${!pet} at home"
done

# Just reading dynamic variables
for i in {1..5}; do
        sentence="sentence$i"
        echo "${!sentence}"
done

echo
echo "Again, but reading regular variables:"
echo $sentence1
echo $sentence2
echo $sentence3
echo $sentence4
echo $sentence5

输出:

您知道,我在家中有一只宠物猫
你知道吗,我在家里有一只宠物鸡
你知道吗,我在家有宠物母牛
你知道吗,我在家有宠物狗
你知道吗,我又在家有一只宠物猪

,但是阅读常规变量:
你知道吗,我在家里有一只宠物猫
你知道吗,我在家里有一只宠物鸡
你知道吗,我在家有宠物母牛
你知道吗,我在家有宠物狗
你知道吗,我在家有宠物猪

Combining two highly rated answers here into a complete example that is hopefully useful and self-explanatory:

#!/bin/bash

intro="You know what,"
pet1="cat"
pet2="chicken"
pet3="cow"
pet4="dog"
pet5="pig"

# Setting and reading dynamic variables
for i in {1..5}; do
        pet="pet$i"
        declare "sentence$i=$intro I have a pet ${!pet} at home"
done

# Just reading dynamic variables
for i in {1..5}; do
        sentence="sentence$i"
        echo "${!sentence}"
done

echo
echo "Again, but reading regular variables:"
echo $sentence1
echo $sentence2
echo $sentence3
echo $sentence4
echo $sentence5

Output:

You know what, I have a pet cat at home
You know what, I have a pet chicken at home
You know what, I have a pet cow at home
You know what, I have a pet dog at home
You know what, I have a pet pig at home

Again, but reading regular variables:
You know what, I have a pet cat at home
You know what, I have a pet chicken at home
You know what, I have a pet cow at home
You know what, I have a pet dog at home
You know what, I have a pet pig at home

じ违心 2025-02-01 15:16:09

这也会起作用

my_country_code="green"
x="country"

eval z='

在您的情况下,

eval final_val='
my_"$x"_code
echo $z                 ## o/p: green

在您的情况下,


magic_way_to_define_magic_variable_"$1"
echo $final_val
my_"$x"_code echo $z ## o/p: green

在您的情况下,


This will work too

my_country_code="green"
x="country"

eval z='

In your case

eval final_val='
my_"$x"_code
echo $z                 ## o/p: green

In your case


magic_way_to_define_magic_variable_"$1"
echo $final_val
my_"$x"_code echo $z ## o/p: green

In your case



    
找个人就嫁了吧 2025-02-01 15:16:09

对于ZSH(Newers Mac OS版本),您应该使用

real_var="holaaaa"
aux_var="real_var"
echo ${(P)aux_var}
holaaaa

代替“!”

For zsh (newers mac os versions), you should use

real_var="holaaaa"
aux_var="real_var"
echo ${(P)aux_var}
holaaaa

Instead of "!"

〃温暖了心ぐ 2025-02-01 15:16:09

这应该有效:

function grep_search() {
    declare magic_variable_$1="$(ls | tail -1)"
    echo "$(tmpvar=magic_variable_$1 && echo ${!tmpvar})"
}
grep_search var  # calling grep_search with argument "var"

This should work:

function grep_search() {
    declare magic_variable_$1="$(ls | tail -1)"
    echo "$(tmpvar=magic_variable_$1 && echo ${!tmpvar})"
}
grep_search var  # calling grep_search with argument "var"
北方的巷 2025-02-01 15:16:09

bashfaq/bashfaq/006 > with 在这里字符串语法用于分配间接变量:

function grep_search() {
  read "$1" <<<$(ls | tail -1);
}

用法:usage:usage:usage:usage: usage:usage:

$ grep_search open_box
$ echo $open_box
stack-overflow.txt

As per BashFAQ/006, you can use read with here string syntax for assigning indirect variables:

function grep_search() {
  read "$1" <<<$(ls | tail -1);
}

Usage:

$ grep_search open_box
$ echo $open_box
stack-overflow.txt
梦忆晨望 2025-02-01 15:16:09

不依赖于您拥有的shell/bash版本的额外方法是使用 envsubst 。例如:

newvar=$(echo '$magic_variable_'"${dynamic_part}" | envsubst)

An extra method that doesn't rely on which shell/bash version you have is by using envsubst. For example:

newvar=$(echo '$magic_variable_'"${dynamic_part}" | envsubst)
舂唻埖巳落 2025-02-01 15:16:09

即使这是一个古老的问题,我仍然很难找到动态变量名称,同时避免了 eval (evil)命令。

nectrare -n 求解它,该创建了对动态值的引用,这在CI/CD进程中特别有用,在CI/CD过程中,CI/CD服务的所需秘密名称直到运行时才知道。以下是:

# Bash v4.3+
# -----------------------------------------------------------
# Secerts in CI/CD service, injected as environment variables
# AWS_ACCESS_KEY_ID_DEV, AWS_SECRET_ACCESS_KEY_DEV
# AWS_ACCESS_KEY_ID_STG, AWS_SECRET_ACCESS_KEY_STG
# -----------------------------------------------------------
# Environment variables injected by CI/CD service
# BRANCH_NAME="DEV"
# -----------------------------------------------------------
declare -n _AWS_ACCESS_KEY_ID_REF=AWS_ACCESS_KEY_ID_${BRANCH_NAME}
declare -n _AWS_SECRET_ACCESS_KEY_REF=AWS_SECRET_ACCESS_KEY_${BRANCH_NAME}

export AWS_ACCESS_KEY_ID=${_AWS_ACCESS_KEY_ID_REF}
export AWS_SECRET_ACCESS_KEY=${_AWS_SECRET_ACCESS_KEY_REF}

echo $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY
aws s3 ls

Even though it's an old question, I still had some hard time with fetching dynamic variables names, while avoiding the eval (evil) command.

Solved it with declare -n which creates a reference to a dynamic value, this is especially useful in CI/CD processes, where the required secret names of the CI/CD service are not known until runtime. Here's how:

# Bash v4.3+
# -----------------------------------------------------------
# Secerts in CI/CD service, injected as environment variables
# AWS_ACCESS_KEY_ID_DEV, AWS_SECRET_ACCESS_KEY_DEV
# AWS_ACCESS_KEY_ID_STG, AWS_SECRET_ACCESS_KEY_STG
# -----------------------------------------------------------
# Environment variables injected by CI/CD service
# BRANCH_NAME="DEV"
# -----------------------------------------------------------
declare -n _AWS_ACCESS_KEY_ID_REF=AWS_ACCESS_KEY_ID_${BRANCH_NAME}
declare -n _AWS_SECRET_ACCESS_KEY_REF=AWS_SECRET_ACCESS_KEY_${BRANCH_NAME}

export AWS_ACCESS_KEY_ID=${_AWS_ACCESS_KEY_ID_REF}
export AWS_SECRET_ACCESS_KEY=${_AWS_SECRET_ACCESS_KEY_REF}

echo $AWS_ACCESS_KEY_ID $AWS_SECRET_ACCESS_KEY
aws s3 ls
廻憶裏菂餘溫 2025-02-01 15:16:09

亲吻方法:

a=1
c="bam"
let "$c$a"=4
echo $bam1

结果4

KISS approach:

a=1
c="bam"
let "$c$a"=4
echo $bam1

results in 4

九八野马 2025-02-01 15:16:09

哇,大多数语法都是可怕的!这是一个具有一些简单语法的解决方案,如果您需要间接参考数组:

#!/bin/bash

foo_1=(fff ddd) ;
foo_2=(ggg ccc) ;

for i in 1 2 ;
do
    eval mine=( \${foo_$i[@]} ) ;
    echo ${mine[@]}" " ;
done ;

对于更简单的用例,我建议您建议语法在高级bash-scripting指南中描述的语法

Wow, most of the syntax is horrible! Here is one solution with some simpler syntax if you need to indirectly reference arrays:

#!/bin/bash

foo_1=(fff ddd) ;
foo_2=(ggg ccc) ;

for i in 1 2 ;
do
    eval mine=( \${foo_$i[@]} ) ;
    echo ${mine[@]}" " ;
done ;

For simpler use cases I recommend the syntax described in the Advanced Bash-Scripting Guide.

情话墙 2025-02-01 15:16:09

我希望能够创建一个包含命令的第一个参数

的变量名称

script.sh.sh 文件的第一个参数的变量名称:

#!/usr/bin/env bash
function grep_search() {
  eval $1=$(ls | tail -1)
}

test:

$ source script.sh
$ grep_search open_box
$ echo $open_box
script.sh

per help> help> help eval eval

执行参数为shell命令。


您也可以使用Bash $ {!var} 间接扩展,但是它不支持检索数组索引。


有关进一步的阅读或示例,请检查 bashfaq/006关于indirection

我们不知道没有任何技巧可以在没有 eval eval 的情况下复制Posix或Bourne shell中的该功能,这可能很难安全。因此,将其视为您自己的风险hack

但是,您应该根据以下说明重新考虑使用间接方向。

通常,在bash脚本中,您根本不需要间接参考。通常,当人们不了解或不了解狂欢数组或不完全考虑其他bash功能(例如函数)时,人们就将其视为解决方案。

将变量名称或任何其他bash语法在内部参数中经常错误地进行,并且在不适当的情况下以解决具有更好解决方案的问题。它违反了代码和数据之间的分离,因此使您陷入了针对错误和安全问题的滑坡。间接方向可以使您的代码不那么透明,更难遵循。

I want to be able to create a variable name containing the first argument of the command

script.sh file:

#!/usr/bin/env bash
function grep_search() {
  eval $1=$(ls | tail -1)
}

Test:

$ source script.sh
$ grep_search open_box
$ echo $open_box
script.sh

As per help eval:

Execute arguments as a shell command.


You may also use Bash ${!var} indirect expansion, as already mentioned, however it doesn't support retrieving of array indices.


For further read or examples, check BashFAQ/006 about Indirection.

We are not aware of any trick that can duplicate that functionality in POSIX or Bourne shells without eval, which can be difficult to do securely. So, consider this a use at your own risk hack.

However, you should re-consider using indirection as per the following notes.

Normally, in bash scripting, you won't need indirect references at all. Generally, people look at this for a solution when they don't understand or know about Bash Arrays or haven't fully considered other Bash features such as functions.

Putting variable names or any other bash syntax inside parameters is frequently done incorrectly and in inappropriate situations to solve problems that have better solutions. It violates the separation between code and data, and as such puts you on a slippery slope toward bugs and security issues. Indirection can make your code less transparent and harder to follow.

九八野马 2025-02-01 15:16:09

虽然我认为声明-n 仍然是最好的方法,但没有人提到的另一种方式,在CI/CD中非常有用,

function dynamic(){
  export a_$1="bla"
}

dynamic 2
echo $a_2

此功能将不支持空格,因此 Dynamic“ 2 3” 将返回错误。

While I think declare -n is still the best way to do it there is another way nobody mentioned it, very useful in CI/CD

function dynamic(){
  export a_$1="bla"
}

dynamic 2
echo $a_2

This function will not support spaces so dynamic "2 3" will return an error.

自找没趣 2025-02-01 15:16:09

对于索引数组,您可以像这样引用它们:

foo=(a b c)
bar=(d e f)

for arr_var in 'foo' 'bar'; do
    declare -a 'arr=("${'"$arr_var"'[@]}")'
    # do something with $arr
    echo "\$arr_var contains:"
    for char in "${arr[@]}"; do
        echo "$char"
    done
done

可以类似地引用关联数组,但需要 -A switch On neclare 而不是 -A

For indexed arrays, you can reference them like so:

foo=(a b c)
bar=(d e f)

for arr_var in 'foo' 'bar'; do
    declare -a 'arr=("${'"$arr_var"'[@]}")'
    # do something with $arr
    echo "\$arr_var contains:"
    for char in "${arr[@]}"; do
        echo "$char"
    done
done

Associative arrays can be referenced similarly but need the -A switch on declare instead of -a.

玩套路吗 2025-02-01 15:16:09

此解决方案的POSIX合规答案

您需要将R/W权限具有/tmp 文件夹。
我们创建一个保存我们的变量的临时文件,并利用 -A set> set 内置的标志:

$男人集
...
-a 给出了所创建或修改的每个变量或函数的导出属性,并标记为导出到后续命令的环境。

因此,如果我们创建一个保留动态变量的文件,我们可以使用设置将它们在脚本中栩栩如生。

实施

#!/bin/sh
# Give the temp file a unique name so you don't mess with any other files in there
ENV_FILE="/tmp/$(date +%s)"

MY_KEY=foo
MY_VALUE=bar

echo "$MY_KEY=$MY_VALUE" >> "$ENV_FILE"

# Now that our env file is created and populated, we can use "set"
set -a; . "$ENV_FILE"; set +a
rm "$ENV_FILE"
echo "$foo"

# Output is "bar" (without quotes)

解释了上述步骤:

# Enables the -a behavior
set -a

# Sources the env file
. "$ENV_FILE"

# Disables the -a behavior
set +a

POSIX compliant answer

For this solution you'll need to have r/w permissions to the /tmp folder.
We create a temporary file holding our variables and leverage the -a flag of the set built-in:

$ man set
...
-a Each variable or function that is created or modified is given the export attribute and marked for export to the environment of subsequent commands.

Therefore, if we create a file holding our dynamic variables, we can use set to bring them to life inside our script.

The implementation

#!/bin/sh
# Give the temp file a unique name so you don't mess with any other files in there
ENV_FILE="/tmp/$(date +%s)"

MY_KEY=foo
MY_VALUE=bar

echo "$MY_KEY=$MY_VALUE" >> "$ENV_FILE"

# Now that our env file is created and populated, we can use "set"
set -a; . "$ENV_FILE"; set +a
rm "$ENV_FILE"
echo "$foo"

# Output is "bar" (without quotes)

Explaining the steps above:

# Enables the -a behavior
set -a

# Sources the env file
. "$ENV_FILE"

# Disables the -a behavior
set +a
罗罗贝儿 2025-02-01 15:16:09

小心:我刚刚发现这项工作非常不同 - zsh 中不起作用。因此,请确保您在外壳中的意思是您要参加;或将出于脚本的最终执行上下文。

Careful: I just found this works very differently - doesn't work - in zsh. So make sure you're in the shell you mean to be in; or will be in for your script's eventual execution context.

究竟谁懂我的在乎 2025-02-01 15:16:09

对于 varname = $ prefix_suffix 格式,只需使用:

varname=${prefix}_suffix

for varname=$prefix_suffix format, just use:

varname=${prefix}_suffix
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文