Bash 使用 getopts 将字符串存储为数组的脚本
Bash script using getopts to store strings as an array
我正在编写一个 Bash 脚本,它需要将零到多个字符串作为输入,但我不确定如何执行此操作,因为列表前缺少标志。
脚本用法:
script [ list ] [ -t <secs> ] [ -n <count> ]
该列表将零个、一个或多个字符串作为输入。当遇到 space 时,在两个或更多的情况下,它充当字符串之间的分隔符。这些字符串最终将作为 grep
命令的输入,所以我的想法是将它们保存在某种数组中。我目前的 -t
和 -n
工作正常。我曾尝试查找示例,但无法找到与我想做的类似的任何事情。我的另一个问题是如何在设置标志后忽略字符串输入,以便不接受其他字符串。
我当前的脚本:
while getopts :t:n: arg; do
case ${arg} in
t)
seconds=${OPTARG}
if ! [[ $seconds =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
;;
n)
count=${OPTARG}
if ! [[ $count =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
;;
:)
echo "[=12=]: Must supply an argument to -$OPTARG" >&2
exit
;;
?)
echo "Invalid option: -${OPTARG}"
exit
;;
esac
done
编辑: 这是家庭作业,我不确定参数的顺序是否可以更改
编辑2:选项可以任意顺序
请您尝试以下操作:
#!/bin/bash
# parse the arguments before getopts
for i in "$@"; do
if [[ $i = "-"* ]]; then
break
else # append the arguments to "list" as long as it does not start with "-"
list+=("")
shift
fi
done
while getopts :t:n: arg; do
: your "case" code here
done
# see if the variables are properly assigned
echo "seconds=$seconds" "count=$count"
echo "list=${list[@]}"
尝试:
#! /bin/bash -p
# Set defaults
count=10
seconds=20
args=( "$@" )
end_idx=$(($#-1))
# Check for '-n' option at the end
if [[ end_idx -gt 0 && ${args[end_idx-1]} == -n ]]; then
count=${args[end_idx]}
end_idx=$((end_idx-2))
fi
# Check for '-t' option at the (possibly new) end
if [[ end_idx -gt 0 && ${args[end_idx-1]} == -t ]]; then
seconds=${args[end_idx]}
end_idx=$((end_idx-2))
fi
# Take remaining arguments up to the (possibly new) end as the list of strings
strings=( "${args[@]:0:end_idx+1}" )
declare -p strings seconds count
- 基本思想是处理参数 right-to-left 而不是 left-to-right。
- 代码假定唯一可接受的参数顺序是问题中给出的顺序。特别是,它假定
-t
和 -n
选项如果存在则必须在末尾,并且如果两者都存在则它们必须按该顺序。
- 它不会尝试处理与选项结合的选项参数(例如
-t5
而不是 -t 5
)。如果需要,这可以很容易地完成。
- 列表中的字符串可以以
-
开头。
我的较短版本
一些备注:
- 而不是遍历所有参数**,然后
break
如果参数以 -
开头,我只是使用 while
循环。
- 来自How do I test if a variable is a number in Bash?,添加了高效的
is_int
测试函数
- 由于在
while getopts ...
循环中完成的任何输出 (echo
) 都是错误的,因此重定向会执行 STDERR
(>&2
) 可以寻址到整个循环,而不是在每个 echo
行上重复。
- ** 注意对所有参数进行循环可以写成
for varname ;do
。因为 $@
代表 默认参数 ,in "$@"
隐含在 for
循环中。
#!/bin/bash
is_int() { case ${1#[-+]} in
'' | *[!0-9]* ) echo "Argument '' is not a number"; exit 3;;
esac ;}
while [[ ${1%%-*} ]];do
args+=("")
shift
done
while getopts :t:n: arg; do
case ${arg} in
t ) is_int "${OPTARG}" ; seconds=${OPTARG} ;;
n ) is_int "${OPTARG}" ; count=${OPTARG} ;;
: ) echo "[=10=]: Must supply an argument to -$OPTARG" ; exit 2;;
? ) echo "Invalid option: -${OPTARG}" ; exit 1;;
esac
done >&2
declare -p seconds count args
标准做法是将选项参数放在任何 non-option 参数或变量参数之前。
getopts
本身将 --
识别为选项开关定界符的结尾。
如果您需要传递以破折号 -
开头的参数,请使用 --
分隔符,因此 getopts
停止尝试拦截选项参数。
这是一个实现:
#!/usr/bin/env bash
# SYNOPSIS
# script [-t<secs>] [-n<count>] [string]...
# Counter of option arguments
declare -i opt_arg_count=0
while getopts :t:n: arg; do
case ${arg} in
t)
seconds=${OPTARG}
if ! [[ $seconds =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
opt_arg_count+=1
;;
n)
count=${OPTARG}
if ! [[ $count =~ ^[1-9][0-9]*$ ]] ; then
exit 1
fi
opt_arg_count+=1
;;
?)
printf 'Invalid option: -%s\n' "${OPTARG}" >&2
exit 1
;;
esac
done
shift "$opt_arg_count" # Skip all option arguments
[[ "" == -- ]] && shift # Skip option argument delimiter if any
# Variable arguments strings are all remaining arguments
strings=("$@")
declare -p count seconds strings
用法示例
字符串不以破折号开头:
$ ./script -t45 -n10 foo bar baz qux
declare -- count="10"
declare -- seconds="45"
declare -a strings=([0]="foo" [1]="bar" [2]="baz" [3]="qux")
对于以短划线开头的字符串,需要 --
个分隔符:
$ ./script -t45 -n10 -- '-dashed string' foo bar baz qux
declare -- count="10"
declare -- seconds="45"
declare -a strings=([0]="-dashed string" [1]="foo" [2]="bar" [3]="baz" [4]="qux")
我正在编写一个 Bash 脚本,它需要将零到多个字符串作为输入,但我不确定如何执行此操作,因为列表前缺少标志。
脚本用法:
script [ list ] [ -t <secs> ] [ -n <count> ]
该列表将零个、一个或多个字符串作为输入。当遇到 space 时,在两个或更多的情况下,它充当字符串之间的分隔符。这些字符串最终将作为 grep
命令的输入,所以我的想法是将它们保存在某种数组中。我目前的 -t
和 -n
工作正常。我曾尝试查找示例,但无法找到与我想做的类似的任何事情。我的另一个问题是如何在设置标志后忽略字符串输入,以便不接受其他字符串。
我当前的脚本:
while getopts :t:n: arg; do
case ${arg} in
t)
seconds=${OPTARG}
if ! [[ $seconds =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
;;
n)
count=${OPTARG}
if ! [[ $count =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
;;
:)
echo "[=12=]: Must supply an argument to -$OPTARG" >&2
exit
;;
?)
echo "Invalid option: -${OPTARG}"
exit
;;
esac
done
编辑: 这是家庭作业,我不确定参数的顺序是否可以更改
编辑2:选项可以任意顺序
请您尝试以下操作:
#!/bin/bash
# parse the arguments before getopts
for i in "$@"; do
if [[ $i = "-"* ]]; then
break
else # append the arguments to "list" as long as it does not start with "-"
list+=("")
shift
fi
done
while getopts :t:n: arg; do
: your "case" code here
done
# see if the variables are properly assigned
echo "seconds=$seconds" "count=$count"
echo "list=${list[@]}"
尝试:
#! /bin/bash -p
# Set defaults
count=10
seconds=20
args=( "$@" )
end_idx=$(($#-1))
# Check for '-n' option at the end
if [[ end_idx -gt 0 && ${args[end_idx-1]} == -n ]]; then
count=${args[end_idx]}
end_idx=$((end_idx-2))
fi
# Check for '-t' option at the (possibly new) end
if [[ end_idx -gt 0 && ${args[end_idx-1]} == -t ]]; then
seconds=${args[end_idx]}
end_idx=$((end_idx-2))
fi
# Take remaining arguments up to the (possibly new) end as the list of strings
strings=( "${args[@]:0:end_idx+1}" )
declare -p strings seconds count
- 基本思想是处理参数 right-to-left 而不是 left-to-right。
- 代码假定唯一可接受的参数顺序是问题中给出的顺序。特别是,它假定
-t
和-n
选项如果存在则必须在末尾,并且如果两者都存在则它们必须按该顺序。 - 它不会尝试处理与选项结合的选项参数(例如
-t5
而不是-t 5
)。如果需要,这可以很容易地完成。 - 列表中的字符串可以以
-
开头。
我的较短版本
一些备注:
- 而不是遍历所有参数**,然后
break
如果参数以-
开头,我只是使用while
循环。 - 来自How do I test if a variable is a number in Bash?,添加了高效的
is_int
测试函数 - 由于在
while getopts ...
循环中完成的任何输出 (echo
) 都是错误的,因此重定向会执行STDERR
(>&2
) 可以寻址到整个循环,而不是在每个echo
行上重复。 - ** 注意对所有参数进行循环可以写成
for varname ;do
。因为$@
代表 默认参数 ,in "$@"
隐含在for
循环中。
#!/bin/bash
is_int() { case ${1#[-+]} in
'' | *[!0-9]* ) echo "Argument '' is not a number"; exit 3;;
esac ;}
while [[ ${1%%-*} ]];do
args+=("")
shift
done
while getopts :t:n: arg; do
case ${arg} in
t ) is_int "${OPTARG}" ; seconds=${OPTARG} ;;
n ) is_int "${OPTARG}" ; count=${OPTARG} ;;
: ) echo "[=10=]: Must supply an argument to -$OPTARG" ; exit 2;;
? ) echo "Invalid option: -${OPTARG}" ; exit 1;;
esac
done >&2
declare -p seconds count args
标准做法是将选项参数放在任何 non-option 参数或变量参数之前。
getopts
本身将 --
识别为选项开关定界符的结尾。
如果您需要传递以破折号 -
开头的参数,请使用 --
分隔符,因此 getopts
停止尝试拦截选项参数。
这是一个实现:
#!/usr/bin/env bash
# SYNOPSIS
# script [-t<secs>] [-n<count>] [string]...
# Counter of option arguments
declare -i opt_arg_count=0
while getopts :t:n: arg; do
case ${arg} in
t)
seconds=${OPTARG}
if ! [[ $seconds =~ ^[1-9][0-9]*$ ]] ; then
exit
fi
opt_arg_count+=1
;;
n)
count=${OPTARG}
if ! [[ $count =~ ^[1-9][0-9]*$ ]] ; then
exit 1
fi
opt_arg_count+=1
;;
?)
printf 'Invalid option: -%s\n' "${OPTARG}" >&2
exit 1
;;
esac
done
shift "$opt_arg_count" # Skip all option arguments
[[ "" == -- ]] && shift # Skip option argument delimiter if any
# Variable arguments strings are all remaining arguments
strings=("$@")
declare -p count seconds strings
用法示例
字符串不以破折号开头:
$ ./script -t45 -n10 foo bar baz qux
declare -- count="10"
declare -- seconds="45"
declare -a strings=([0]="foo" [1]="bar" [2]="baz" [3]="qux")
对于以短划线开头的字符串,需要 --
个分隔符:
$ ./script -t45 -n10 -- '-dashed string' foo bar baz qux
declare -- count="10"
declare -- seconds="45"
declare -a strings=([0]="-dashed string" [1]="foo" [2]="bar" [3]="baz" [4]="qux")