有没有办法在bash中实现一个计数器,但是用于字母而不是数字?

时间:2016-11-01 06:51:17

标签: bash

我正在处理一个写得有点混乱的现有脚本。设置一个包含所有意大利面条代码的循环可能会比我想要在短期内处理更令人头痛。也许当我有更多的时间我可以清理它但是现在,我只是在寻找一个简单的解决方案。

该脚本处理xen服务器上的虚拟磁盘。它会读取multipath输出,并询问是否应根据特定条件以任何方式格式化特定LUN。但是,不是将该磁盘路径和已经格式化的磁盘路径插入到配置文件中,而是简单地以格式显示每一行

'phy:/dev/mapper/UUID,xvd?,w',

UUID当然是一个真正的UUID。

该脚本实际上以这种格式显示每个找到的LUN,期望用户将它们复制并粘贴到配置文件中,用序列中的字母替换每个?。这充其量是乏味的。

有几种方法可以在bash中增加一个数字。其中包括:

var=$((var+1))
((var+=1))
((var++))

是否有办法对不涉及整个字母表循环的字符执行相同的操作,以便我可以轻松地将磁盘分配从xvda“递增”到xvdb等?< / p>

5 个答案:

答案 0 :(得分:4)

要对字母执行“增量”,请定义函数:

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是97:

$ printf '%d' "'a"
97

下一步是增加:

$ echo "$((97+1))"
98

或者:

$ echo "$(($(printf '%d' "'a")+1))"
98

最后一步是将新增加的数字转换回字母:

$ LC_CTYPE=C printf "\\$(printf '%03o' "98")"
b

或者:

$ LC_CTYPE=C printf "\\$(printf '%03o' "$(($(printf '%d' "'a")+1))")"
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

答案 1 :(得分:2)

将条目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"

这有1 = A,2 = B等。如果你想要0 = A,1 = B,依此类推,删除第一个冒号。

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"

然后,你真正需要这封信的地方;

var=$((var+1))
'phy:/dev/mapper/UUID,xvd${alpharray[$var]},w',

唯一的问题是如果你最终跑过26个字母,你将开始从阵列中返回空白。

答案 2 :(得分:1)

使用Bash 4范围

您可以使用Bash 4功能,让您指定sequence expression范围内的范围。例如:

for letter in {a..z}; do
    echo "phy:/dev/mapper/UUID,xvd${letter},w"
done

另请参阅Bash Wiki中的Ranges

答案 3 :(得分:0)

使用Jot

虽然Bash范围选项使用内置函数,但您也可以使用BSD jot 实用程序之类的实用程序。默认情况下,这在macOS上可用,但在Linux系统上,您的里程可能会有所不同。例如,您需要在Debian上安装athena-jot

更多循环

这里的一个技巧是预先填充Bash数组,然后使用索引变量从数组中获取所需的输出。例如:

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想要的东西,但它肯定比原始循环recommended elsewhere更复杂,更不优雅。但是,您的里程可能会有所不同,最好有选择!

答案 4 :(得分:0)

这是一个函数,它将返回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的字符。