我对bash脚本感到困惑。
我有以下代码:
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, ${…}, \$${...},但我还是很困惑。
使用声明
没有必要像其他答案一样使用前缀,也没有数组。只使用声明、双引号和参数展开。
我经常使用下面的技巧来解析包含1到n个参数的参数列表,格式为key=value otherkey=othervalue etc=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
尽管这是一个老问题,但我在获取动态变量名时仍然遇到了一些困难,同时避免使用eval (evil)命令。
用declare -n解决了这个问题,它创建了一个动态值的引用,这在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
POSIX兼容的答案
对于这个解决方案,您需要拥有/tmp文件夹的r/w权限。
我们创建一个临时文件保存变量,并利用set内置的-a标志:
$ man套装
...
-a每个创建或修改的变量或函数都被赋予export属性,并标记为可导出到后续命令的环境。
因此,如果我们创建一个包含动态变量的文件,我们可以使用set在脚本中赋予它们生命。
实现
#!/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