我写了一个使用多个参数的bash脚本。 我已按以下格式分配变量。
x=$1
y=$2
z=$3
k=$4
参数是可选的,它也可以在没有它们的情况下运行 例如:
./myscript.sh
./myscript.sh x y ...
两种情况都正常。
我正在寻找一种更好的方法,从设计的角度来看,因为我不喜欢变量获得价值的方式。
如果将来我的论据增加到9
,那将不会很好看感谢。
答案 0 :(得分:2)
您可以read
结合使用herestring和printf
的引用重建功能:
read x y z k <<<$(printf " %q" "$@")
以示例:
$ cat example.bash
#!/bin/bash
read x y z k <<<$(printf " %q" "$@")
echo "x=[$x]"
echo "y=[$y]"
echo "z=[$z]"
echo "k=[$k]"
$ ./example.bash a b "c d"
x=[a]
y=[b]
z=[c d]
k=[]
那么,这里发生了什么?让我们从内到外工作。
printf " %q" "$@"
quotes以与原始命令行参数等效的方式给出的参数。如果没有这种引用重构,带空格的命令行参数将被视为单独的参数,即使最初引用。要查看我的意思,请尝试read x y z w <<<"$@"
:z指定为“c”,k指定为“d”。
read
接收重构的命令行,然后从左到右将每个非转义空格分隔的字符串分配给给定的变量。
回到我们的例子:
"$@"
基本上是a b "c d"
printf " %q" "$@"
是a b c\ d
read x y z k <<<"a b c\ d"
是您想要的硬编码表示。虽然这是紧凑和可扩展的,但它也很棘手。如果您的脚本使用代表选项的参数(脚本行为根据缺少所述参数而发生变化),那么我建议使用getopts
。但是,如果您的脚本采用了许多表示值的参数(比如输入到矩阵计算中),那么读入数组(read -a
)可能更容易理解。
您可能还希望处理不提供命令行参数的情况。这需要略微阐述:
read x y z k <<<$([ 0 -eq $# ] && echo '' || printf " %q" "$@")
在此变体中,将检查参数的数量,如果存在一些参数,则执行printf requoting业务。
答案 1 :(得分:1)
你可以使用这个结构。
for i in x y z k
do
eval $i='$1'
shift
done
答案 2 :(得分:1)
如果参数数量开放式,您最好使用数组 - 甚至可能"$@"
,所有参数的数组,直接(将所有参数复制到自定义数组,使用类似args=( "$@" )
的内容)。
您可以了解有关Bash数组here的更多信息 此外,下面的解决方案通过使用辅助数组来创建单个变量来演示阵列技术。
那就是说,如果你做需要不同的变量名 ,这是一个强有力的方法:
为了参数化和错误处理,这种方法可能比它需要的更间接 有关此方法的要点,请参阅rené's helpful answer。
#!/usr/bin/env bash
# Declare the up to 9 variable names to assign to, using a helper array.
varNames=( a b c d e f g h i )
# Exit, if more arguments than available variables were specified.
(( $# > ${#varNames[@]} )) && { echo "Too many arguments." >&2; exit 2; }
# Assign to the variables in sequence, looping over the variable-names array.
for varName in "${varNames[@]}"; do
(( $# )) || break # Break, if there are no more arguments.
declare "$varName"="$1"
shift
done
然后使用variable indirection打印生成的变量:
# Print all variable values, using indirection.
for varName in "${varNames[@]}"; do
echo "Value of \$$varName: '${!varName}'"
done
答案 3 :(得分:1)
通过在命令行上命名参数来完全跳过位置参数:
$ cat example.bash
#!/bin/bash
declare x=1013 y=242 z k
declare "$@" >/dev/null
echo "x=[$x]"
echo "y=[$y]"
echo "z=[$z]"
echo "k=[$k]"
$ ./example.bash x="b" z="c d" k=$'e\nf'
x=[b]
y=[242]
z=[c d]
k=[e
f]
第一个declare
充当安全网,并使用默认值初始化所有预期变量。第二个declare
在命令行中提取命名变量,将它们视为变量赋值。命令行中未提供的变量保留其初始化的默认值(如示例中所示,y
未传递,因此默认值为242
)。声明之外的变量将在您的脚本中提供。这可能是也可能不是。
答案 4 :(得分:0)
使用“shift”可以将命令行参数作为命名选项。 见Handling positional parameters
while :
do
case "$1" in
-f | --file)
file="$2"
shift 2
;;
-h | --help)
display_help # Call your function
# no shifting needed here, we're done.
exit 0
;;
-u | --user)
username="$2" # You may want to check validity of $2
shift 2
;;
-v | --verbose)
# It's better to assign a string, than a number like "verbose=1"
# because if you're debugging the script with "bash -x" code like this:
#
# if [ "$verbose" ] ...
#
# You will see:
#
# if [ "verbose" ] ...
#
# Instead of cryptic
#
# if [ "1" ] ...
#
verbose="verbose"
shift
;;
--) # End of all options
shift
break;
;;
-*) # Wrong option
echo "Error: Unknown option: $1" >&2
exit 1
;;
*) # No more options
break
;;
esac
done
或者您可以使用getopts。
答案 5 :(得分:0)
在任何情况下都不可能为变量分配零字节$'\0'
c
中的字符串以零字节结尾,不能包含零字节
下面不再讨论这个问题。
如果您不需要允许空格或换行符,则可以有效地运行:
if (IFS=''; reg=$'[ \t\n]'; [[ "$*" =~ $reg ]] ); then
echo "$0: error: input contains spaces, tabs or newlines" >&2
exit 1
fi
if (( $# != 4 )); then
echo "$0: error: we need 4 arguments"
exit 2
fi
read x y z k <<<"$@"
printf 'x="%s" y="%s" z="%s" k="%s"\n' "$x" "$y" "$z" "$k"
如果输入有空格,制表符或换行符,则脚本将禁止。
将其用作:
$ script one two t33 f44
x="one" y="two" z="t33" k="f44"
要使其接受空格和制表符,我们需要将读取扩展为:
IFS=$'\n' read -d '' x y z k < <(printf '%s\n' "$@")
$ ./script $'o\tne' 't wo' t33 f44
x="o ne" y="t wo" z="t33" k="f44"
一个合理的选择是使用关联数组(无新行):
if (IFS=''; reg=$'[\n]'; [[ "$*" =~ $reg ]] ); then
echo "$0: error: input contains newlines" >&2
exit 1
fi
varlist=(x y z k)
n=${#varlist[@]}
if (( $# != $n )); then
echo "$0: error: we need $n arguments"
exit 2
fi
declare -A arr
for i in ${varlist[@]}; do
IFS='' read "arr[$i]"
done < <(printf '%s\n' "$@")
for i in ${!arr[@]}; do
printf '%s="%s" ' "$i" "${arr[$i]}"
done
echo
某些更改允许直接使用变量名称:
if (IFS=''; reg=$'[\n]'; [[ "$*" =~ $reg ]] ); then
echo "$0: error: input contains newlines" >&2
exit 1
fi
varlist=(x y z k)
n=${#varlist[@]}
if (( $# != $n )); then
echo "$0: error: we need $n arguments"
exit 2
fi
for i in ${varlist[@]}; do
IFS='' read "$i"
done < <(printf '%s\n' "$@")
for i in ${varlist[@]}; do
printf '%s="%s" ' "$i" "${!i}"
done
echo
最后的改编允许最终接受换行:
varlist=(x y z k)
n=${#varlist[@]}
if (( $# != $n )); then
echo "$0: error: we need $n arguments"
exit 2
fi
for i in ${varlist[@]}; do
IFS='' read -d '' "$i"
done < <(printf '%s\0' "$@")
for i in ${varlist[@]}; do
printf '%s="%s" ' "$i" "${!i}"
done
echo