有没有一种方法可以在bash中实现一个计数器,而不是针对字母而不是数字?
我正在使用一个写得有点混乱的现有脚本。设置一个包含所有意大利面条代码的循环可能会让我在短期内更头疼。也许当我有更多的时间,我可以清理它,但现在,我只是在寻找一个简单的解决办法 该脚本处理xen服务器上的虚拟磁盘。它读取有没有一种方法可以在bash中实现一个计数器,而不是针对字母而不是数字?,bash,Bash,我正在使用一个写得有点混乱的现有脚本。设置一个包含所有意大利面条代码的循环可能会让我在短期内更头疼。也许当我有更多的时间,我可以清理它,但现在,我只是在寻找一个简单的解决办法 该脚本处理xen服务器上的虚拟磁盘。它读取多路径输出,并询问是否应根据特定条件以任何方式格式化特定LUN。但是,它并没有将已格式化的磁盘路径插入到配置文件中,而是以该格式显示每一行 'phy:/dev/mapper/UUID,xvd?,w', 当然,UUID是一个实际的UUID 该脚本实际上以这种格式显示找到的每个LUN
多路径
输出,并询问是否应根据特定条件以任何方式格式化特定LUN。但是,它并没有将已格式化的磁盘路径插入到配置文件中,而是以该格式显示每一行
'phy:/dev/mapper/UUID,xvd?,w',
当然,UUID是一个实际的UUID
该脚本实际上以这种格式显示找到的每个LUN,希望用户将它们复制并粘贴到配置文件中,将每个?
依次替换为一个字母。这充其量是乏味的
有几种方法可以在bash中增加一个数字。除其他外:
var=$((var+1))
((var+=1))
((var++))
有没有一种方法可以对不涉及在整个字母表上循环的字符执行相同的操作,这样我就可以轻松地将磁盘分配从xvda
增加到xvdb
,等等?使用Bash 4范围
您可以使用Bash 4功能,该功能允许您在一个范围内指定一个范围。例如:
for letter in {a..z}; do
echo "phy:/dev/mapper/UUID,xvd${letter},w"
done
letters=( "" $(jot -w %c 26 a) )
for idx in 1 26; do
echo ${letters[$idx]}
done
另请参见Bash Wiki中的。要对字母进行“增量”,请定义函数:
incr() { LC_CTYPE=C printf "\\$(printf '%03o' "$(($(printf '%d' "'$1")+1))")"; }
现在,请注意:
$ echo $(incr a)
b
$ echo $(incr b)
c
$ echo $(incr c)
d
因为,它通过ASCII递增,incr z
变成{
工作原理
第一步是将字母转换为ASCII数值。例如,a
is 97:
$ printf '%d' "'a"
97
下一步是增加:
$ echo "$((97+1))"
98
或:
最后一步是将新的递增数字转换回字母:
$ LC_CTYPE=C printf "\\$(printf '%03o' "98")"
b
或:
可供替代的
使用bash,我们可以定义关联数组来容纳下一个字符:
$ declare -A Incr; last=a; for next in {b..z}; do Incr[$last]=$next; last=$next; done; Incr[z]=a
或者,如果您喜欢将代码分散在多行上:
declare -A Incr
last=a
for next in {b..z}
do
Incr[$last]=$next
last=$next
done
Incr[z]=a
使用此数组,可以通过以下方式递增字符:
$ echo "${Incr[a]}"
b
$ echo "${Incr[b]}"
c
$ echo "${Incr[c]}"
d
在此版本中,z
的增量循环回到a
:
$ echo "${Incr[z]}"
a
如果一个数组中的条目A-Z被分配给索引1-26,情况如何
IFS=':' read -r -a alpharray <<< ":A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z"
唯一的问题是,如果你最后运行了26个字母,你将开始从数组中返回空格。使用Jot
虽然Bash range选项使用内置工具,但您也可以使用类似BSD jot实用程序的实用程序。默认情况下,这在macOS上可用,但在Linux系统上您的里程可能会有所不同。例如,您需要在Debian上安装
更多循环
这里的一个技巧是预先填充Bash数组,然后使用索引变量从数组中获取所需的输出。例如:
for letter in {a..z}; do
echo "phy:/dev/mapper/UUID,xvd${letter},w"
done
letters=( "" $(jot -w %c 26 a) )
for idx in 1 26; do
echo ${letters[$idx]}
done
无循环的替代方案
注意,你不必在一个循环中增加计数器。你也可以用其他方法来做。考虑下面的内容,它将增加传递给函数的任何字母,而不必预先填充数组:
increment_var () {
local new_var=$(jot -nw %c 2 "$1" | tail -1)
if [[ "$new_var" == "{" ]]; then
echo "Error: You can't increment past 'z'" >&2
exit 1
fi
echo -n "$new_var"
}
var="c"
var=$(increment_var "$var")
echo "$var"
这可能更接近OP想要的,但它显然看起来比原始循环更复杂,也不那么优雅。但是,您的里程可能会有所不同,最好有选项!这里有一个函数,它将返回a-z范围内的下一个字母。输入“z”将返回“a”
nextl(){
((num=(36#$(printf '%c' $1)-9) % 26+97));
printf '%b\n' '\x'$(printf "%x" $num);
}
它将输入的第一个字母视为以36为基数的整数,减去9,然后返回序号为“a”加上mod 26的字符。将
用于xvda xvdb中的c;do echo$c;done
do?或用于{a..c}中的c;do…
?循环是我特别想要避免的,因为它需要包装一堆乱七八糟的代码。从长远来看,我可以担心修复整个脚本,使循环变得更优雅。现在我需要能够说var=a,var++
。这在早期版本中肯定可用。我按照这些思路思考可能是最简单的解决方案。我怀疑我们是否需要担心>26个磁盘,所以这不应该是一个问题。我喜欢这种关联数组方法
increment_var () {
local new_var=$(jot -nw %c 2 "$1" | tail -1)
if [[ "$new_var" == "{" ]]; then
echo "Error: You can't increment past 'z'" >&2
exit 1
fi
echo -n "$new_var"
}
var="c"
var=$(increment_var "$var")
echo "$var"
nextl(){
((num=(36#$(printf '%c' $1)-9) % 26+97));
printf '%b\n' '\x'$(printf "%x" $num);
}