我有一个用于部署服务的 Bash 脚本。它需要以附加命令行参数的形式进行更新,以指定用于启动服务的多个实例的端口。该参数接受逗号分隔的端口以及冒号分隔的端口范围,即一些有效值是
5001,5002,5003,5004
5001:5004
2003,4001:4008
1337,2300:2340,5000,5008
这些值需要读入数组,扩展范围(即,在2003,4001:4008
值数组的情况下应该是[2003,4001,4002,4003,4004,4005,4006,4007,4008]
)。
我似乎无法找到一种仅使用 Bash 来处理所有这些情况的有效方法(Perl 是不允许的,awk 可能是)。 TIA。
答案1
使用现代awk
实现:
awk -v RS='[,\n]' -F: 'NF == 1; NF == 2 { for (i = $1; i <= $2; ++i) print i }'
上面的awk
命令按照您描述的格式读取一行或一组行,并将每个逗号或换行符分隔的值视为“记录”,将每个冒号分隔的字符串视为“字段”。
如果记录只有一个字段 ( NF == 1
),则按原样打印。这是输入中非范围的值之一。
如果记录有两个字段 ( NF == 2
),则打印从第一个字段到第二个字段范围内的数字。
测试:
$ echo '2003,4001:4008' | awk -v RS='[,\n]' -F: 'NF == 1; NF == 2 { for (i = $1; i <= $2; ++i) print i }'
2003
4001
4002
4003
4004
4005
4006
4007
4008
然后,您可以按照您想要的任何方式使用此生成的列表。例如,
#!/bin/sh
printf '%s\n' "$1" |
awk -v RS='[,\n]' -F: 'NF == 1; NF == 2 { for (i = $1; i <= $2; ++i) print i }' |
xargs -I {} some_executable ...options... --port={}
这将为代码some_executable ...options... --port={}
中每个输出的数字执行一次awk
,并{}
替换为数字。该awk
代码获取作为脚本第一个参数给出的字符串。
不对所提供的参数进行验证。
答案2
由于您可以控制数据,因此我们可以使用参数和大括号扩展。添加保护子句是为了防止恶意输入,因为我使用的是不带引号的变量并eval
达到了想要的效果。
expand() {
if [[ $1 == *[^,:[:digit:]]* ]]; then
echo "unexpected characters detected in argument" >&2
return 1
fi
local IFS=,
for val in $1; do
if [[ $val == *:* ]]; then
eval "printf '%s\n' {${val/:/..}}"
else
echo "$val"
fi
done
}
然后,用您的数据进行测试:
while read line; do
readarray -t values < <(expand "$line")
declare -p values
done << 'END'
5001,5002,5003,5004
5001:5004
2003,4001:4008
1337,2300:2340,5000,5008
1,2,3,$(some malicious command),4
END
结果是
declare -a values=([0]="5001" [1]="5002" [2]="5003" [3]="5004")
declare -a values=([0]="5001" [1]="5002" [2]="5003" [3]="5004")
declare -a values=([0]="2003" [1]="4001" [2]="4002" [3]="4003" [4]="4004" [5]="4005" [6]="4006" [7]="4007" [8]="4008")
declare -a values=([0]="1337" [1]="2300" [2]="2301" [3]="2302" [4]="2303" [5]="2304" [6]="2305" [7]="2306" [8]="2307" [9]="2308" [10]="2309" [11]="2310" [12]="2311" [13]="2312" [14]="2313" [15]="2314" [16]="2315" [17]="2316" [18]="2317" [19]="2318" [20]="2319" [21]="2320" [22]="2321" [23]="2322" [24]="2323" [25]="2324" [26]="2325" [27]="2326" [28]="2327" [29]="2328" [30]="2329" [31]="2330" [32]="2331" [33]="2332" [34]="2333" [35]="2334" [36]="2335" [37]="2336" [38]="2337" [39]="2338" [40]="2339" [41]="2340" [42]="5000" [43]="5008")
unexpected characters detected in argument
declare -a values=()
答案3
for j in `cat file`; do echo $j | perl -pne "s/,/\n/g"| awk -F ":" '/:/{for(i=$1;i<=$2;i++)print i}1'|sed '/:/d'| awk 'ORS=" "'; echo -e "\n";done
output
5001 5002 5003 5004
5001 5002 5003 5004
2003 4001 4002 4003 4004 4005 4006 4007 4008
1337 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323