在Bash中使用getopts检索单个选项的多个参数
我需要有关在Bash中使用getopts检索单个选项的多个参数,bash,command-line-arguments,getopts,Bash,Command Line Arguments,Getopts,我需要有关getopts的帮助 while getopts ":i:d:s:f:" opt do case $opt in i ) initial=$OPTARG;; d ) dir=$OPTARG;; s ) sub=$OPTARG;; f ) files=$OPTARG;; esac done 我创建了一个Bash脚本,运行时如下所示: $foo.sh-i env-d目录-s子目录-f文件 当处理来
getopts
的帮助
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
esac
done
我创建了一个Bash脚本,运行时如下所示:
$foo.sh-i env-d目录-s子目录-f文件
当处理来自每个标志的一个参数时,它可以正常工作。但是当我从每个标志调用几个参数时,我不知道如何从getopts
中的变量中提取多变量信息
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
esac
done
在获取这些选项之后,我想从变量中构建目录结构
foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
那么目录结构将是
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3
有什么想法吗?因为你没有展示你希望如何构建你的列表
/test/directory/subdirectory/file1
. . .
test/directory/subdirectory2/file3
有点不清楚如何继续,但基本上您需要不断向适当的变量添加任何新值,即
case $opt in
d ) dirList="${dirList} $OPTARG" ;;
esac
请注意,在第一次传递时,dir将为空,您将在${dirList}
的最终值的from处得到一个空格。(如果您真的需要不包含任何额外空格的代码,前面或后面,我可以向您展示一个命令,但它很难理解,而且您似乎不需要它,但请告诉我)
然后,您可以将列表变量包装在for循环中以发出所有值,即
for dir in ${dirList} do
for f in ${fileList} ; do
echo $dir/$f
done
done
最后,将任何未知输入“捕获”到您的案例陈述中被认为是良好的做法,即
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
* )
printf "unknown flag supplied "${OPTARG}\nUsageMessageGoesHere\n"
exit 1
;;
esac
我希望这能有所帮助。getopts选项只能接受零个或一个参数。您可能希望更改接口以删除-f选项,只需迭代其余的非选项参数
usage: foo.sh -i end -d dir -s subdir file [...]
所以
我修复了与您相同的问题,如下所示: 而不是:
foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
这样做:
foo.sh -i test -d directory -s "subdirectory subdirectory2" -f "file1 file2 file3"
使用空格分隔符,您可以使用一个基本循环来运行它。
代码如下:
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
esac
done
for subdir in $sub;do
for file in $files;do
echo $subdir/$file
done
done
以下是一个示例输出:
$ ./getopts.sh -s "testdir1 testdir2" -f "file1 file2 file3"
testdir1/file1
testdir1/file2
testdir1/file3
testdir2/file1
testdir2/file2
testdir2/file3
我知道这个问题很老了,但我想把这个答案放在这里,以防有人来找答案 像BASH这样的shell已经支持像这样递归地创建目录,所以实际上不需要脚本。例如,最初的海报需要如下内容:
$ foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3
使用以下命令行可以轻松完成此操作:
pong:~/tmp
[10] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/{file1,file2,file3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
或者再短一点:
pong:~/tmp
[12] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
或更短,更符合要求:
pong:~/tmp
[14] rmclean$ mkdir -pv test/directory/subdirectory{1,2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
最后,使用序列:
pong:~/tmp
[16] rmclean$ mkdir -pv test/directory/subdirectory{1..2}/file{1..3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
您可以多次使用同一选项,并将所有值添加到数组中 对于这里非常具体的原始问题,Ryan的
mkdir-p
解决方案显然是最好的
但是,对于使用getopts从同一个选项中获取多个值这一更一般的问题,这里是:
#!/bin/bash
while getopts "m:" opt; do
case $opt in
m) multi+=("$OPTARG");;
#...
esac
done
shift $((OPTIND -1))
echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[@]}'"
echo "Or:"
for val in "${multi[@]}"; do
echo " - $val"
done
产出将是:
$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:
$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
- one arg with spaces
$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
- one
- second argument
- three
subdirectory1 subdirectory2
subdirectory2
file1
实际上,有一种方法可以使用
getopts
检索多个参数,但它需要使用getopts
“OPTIND
变量进行一些手动操作
请参阅以下脚本(复制如下):。也许有更简单的方法,但这是我能找到的最快的方法
#!/bin/sh
usage() {
cat << EOF
$0 -a <a1> <a2> <a3> [-b] <b1> [-c]
-a First flag; takes in 3 arguments
-b Second flag; takes in 1 argument
-c Third flag; takes in no arguments
EOF
}
is_flag() {
# Check if $1 is a flag; e.g. "-b"
[[ "$1" =~ -.* ]] && return 0 || return 1
}
# Note:
# For a, we fool getopts into thinking a doesn't take in an argument
# For b, we can just use getopts normal behavior to take in an argument
while getopts "ab:c" opt ; do
case "${opt}" in
a)
# This is the tricky part.
# $OPTIND has the index of the _next_ parameter; so "\${$((OPTIND))}"
# will give us, e.g., ${2}. Use eval to get the value in ${2}.
# The {} are needed in general for the possible case of multiple digits.
eval "a1=\${$((OPTIND))}"
eval "a2=\${$((OPTIND+1))}"
eval "a3=\${$((OPTIND+2))}"
# Note: We need to check that we're still in bounds, and that
# a1,a2,a3 aren't flags. e.g.
# ./getopts-multiple.sh -a 1 2 -b
# should error, and not set a3 to be -b.
if [ $((OPTIND+2)) -gt $# ] || is_flag "$a1" || is_flag "$a2" || is_flag "$a3"
then
usage
echo
echo "-a requires 3 arguments!"
exit
fi
echo "-a has arguments $a1, $a2, $a3"
# "shift" getopts' index
OPTIND=$((OPTIND+3))
;;
b)
# Can get the argument from getopts directly
echo "-b has argument $OPTARG"
;;
c)
# No arguments, life goes on
echo "-c"
;;
esac
done
#/垃圾箱/垃圾箱
用法(){
cat最初的问题涉及getopts,但还有另一个解决方案,它在没有getopts的情况下提供了更灵活的功能(这可能有点冗长,但提供了更灵活的命令行界面)。下面是一个示例:
while [[ $# > 0 ]]
do
key="$1"
case $key in
-f|--foo)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
case $nextArg in
bar)
echo "--foo bar found!"
;;
baz)
echo "--foo baz found!"
;;
*)
echo "$key $nextArg found!"
;;
esac
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
-b|--bar)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
case $nextArg in
foo)
echo "--bar foo found!"
;;
baz)
echo "--bar baz found!"
;;
*)
echo "$key $nextArg found!"
;;
esac
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
-z|--baz)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
echo "Doing some random task with $key $nextArg"
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
*)
echo "Unknown flag $key"
;;
esac
shift
done
在本例中,我们将遍历所有命令行选项,查找与接受的命令行标志(如-f或--foo)匹配的参数。一旦我们找到一个标志,我们将循环遍历每个参数,直到参数用完或遇到另一个标志。这将使我们重新进入只处理标志的外部循环
使用此设置,以下命令等效:
script -f foo bar baz
script -f foo -f bar -f baz
script --foo foo -f bar baz
script --foo foo bar -f baz
您还可以分析非常混乱的参数集,例如:
script -f baz derp --baz herp -z derp -b foo --foo bar -q llama --bar fight
要获得输出,请执行以下操作:
--foo baz found!
-f derp found!
Doing some random task with --baz herp
Doing some random task with -z derp
--bar foo found!
--foo bar found!
Unknown flag -q
Unknown flag llama
--bar fight found!
如果要为某个选项指定任意数量的值,可以使用简单的循环查找它们并将它们填充到数组中。例如,让我们修改OP的示例以允许任意数量的-s参数:
unset -v sub
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=("$OPTARG")
until [[ $(eval "echo \${$OPTIND}") =~ ^-.* ]] || [ -z $(eval "echo \${$OPTIND}") ]; do
sub+=($(eval "echo \${$OPTIND}"))
OPTIND=$((OPTIND + 1))
done
;;
f ) files=$OPTARG;;
esac
done
这采用第一个参数($OPTARG)并将其放入数组$sub中。然后它将继续搜索其余参数,直到找到另一个虚线参数或没有更多参数可计算。如果它找到更多非虚线参数的参数,则将其添加到$sub数组中,并放大$OPTIND变量
因此,在OP的示例中,可以运行以下命令:
foo.sh -i test -d directory -s subdirectory1 subdirectory2 -f file1
如果我们在脚本中添加以下行以演示:
echo ${sub[@]}
echo ${sub[1]}
echo $files
产出将是:
$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:
$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
- one arg with spaces
$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
- one
- second argument
- three
subdirectory1 subdirectory2
subdirectory2
file1
!/bin/bash
myname=$(基本名称“$0”)
#帮助功能
help(){cat以下链接应该是此需求的通用解决方案
它易于注入,足够清晰易懂,同时也最大限度地减少了对原始代码的影响
这更有意义,但还不是100%。我的文件列表将从标志中提取值,以构建文件的目录路径。如果构建了第二个目录路径,则我需要重建第二个目录的新路径/filegetopt是更好的选择,还是您会使用其他方法?我将使用上面所写的方法。如果您愿意如果能够为-f选项提供多个参数,或者能够多次为-f提供一个参数,我知道您可以在Perl中使用Getopt::Long模块来实现这一点。我同意Glenn的观点,这通常是我使用的。但是,另一个选项是只使用另一个分隔符,例如逗号来分隔多个参数,而不是空格然后在逗号上拆分$OPTARG。例如-f file1、file2、file3。我倾向于只在我计划保留的命令上执行此操作,因为我不相信其他人会意识到他们不能在逗号后加空格。请注意,“$@”中的
可以在不改变语义的情况下删除。@cole,是的,在你将分支附加到一个数组的情况下:subdirs+=(“$OPTARG”)
Yah,除了它将目录而不是文件作为叶节点。谢谢-我一直想知道是否有一种巧妙的方法可以避免getopts,而你的答案是