Bash:带有二进制范围的 For 循环保持控制值二进制

Bash:带有二进制范围的 For 循环保持控制值二进制

假设我有两个包含二进制值的 bash 变量:

a=0011 # decimal 3
b=1000 # decimal 8

有没有办法可以循环遍历所有可能的值$a$b保持二进制?就像是:

for blah in $(seq $a $b) ; do
    print "Blah is: $blah"
done

所以它会输出:

Blah is: 0011
Blah is: 0100
Blah is: 0101
Blah is: 0110
Blah is: 0111
Blah is: 1000

我努力了:

for blah in $(seq "$((2#$a))" "$((2#$b))") ; do

但随后$blah变成十进制,我想将其保留为二进制(我总是可以将十进制转换回二进制,但这似乎很浪费,因为我已经有了极端二进制)

该代码必须在有限的linux中运行(开放WRT) 没有obase可用的。如果答案是这样的话不可能保留二进制值,这也是一个有用的答案(我可以创建一个将十进制转换为二进制的函数,而不使用obase)此外,对于使用常规bash.

答案1

你很接近这个:

for blah in $(seq "$((2#$a))" "$((2#$b))") ; do

您只需for使用类似的方法将循环中的十进制值转换回二进制值dc

例子

$ for blah in $(seq "$((2#0011))" "$((2#1000))"); do \
     printf "%04d\n" $(echo "obase=2;$blah" | bc);done
0011
0100
0101
0110
0111
1000

用于printf控制输出,以便用前导零填充,并格式化为一定的宽度。参数%04d是指定输出的内容。

该命令的另一个关键是使用bc命令行计算器。该命令例如:

echo "obase=2;$blah" | bc

正在获取该值,$blah并通过使用将其转换为基数 2(也称为二进制)bc

无 bc 或 dc

如果您使用的系统受到限制,因此这些工具都不存在,您可以直接使用手册awk中的此功能进行转换。awk

例子

创建一个包含以下内容的文件,将其命名为dec2bin.awk.

# bits2str --- turn a byte into readable 1's and 0's

function bits2str(bits, data, mask)
{
    if (bits == 0)
        return "0"

    mask = 1
    for (; bits != 0; bits = rshift(bits, 1))
        data = (and(bits, mask) ? "1" : "0") data

    while ((length(data) % 8) != 0)
        data = "0" data

    return data
}

{
  printf("%s\n", bits2str($1))
}

现在使用上面的函数:

$ for blah in $(seq "$((2#0011))" "$((2#1000))"); do echo $blah \
     | awk -f dec2bin.awk; done
00000011
00000100
00000101
00000110
00000111
00001000

答案2

seq不是内置的。它也不是 Posix 标准的一部分。但通常的实现seq不具备以 10 以外的碱基进行排序的能力。

在 bash 中,您可以将范围指定为{start..finish}.然而,这也不适用于 10 以外的基数(尽管它确实适用于字母:{a..f}扩展为a b c d e f.

据我所知,这就是简单的序列生成器,这给您留下了几种可能性。

愚蠢的方法是过滤掉非二进制值。如果ab不是很小的话,这很简单,但效率非常低:

for x in $(seq -w $a $b); do
  if [[ ! ($x =~ [2-9]) ]]; then
    echo $x
  fi
done

这是一个更好的解决方案。假设ab的长度相同(如果不是,您可以使用 printf 来解决这个问题),以下将循环遍历从 a 到 b(包括)的所有二进制数:

# We need a string of 0s at least as long as a:
z=${a//1/0}
while [[ ! ($a > $b) ]]; do
  # do something with $a
  # The following "increments" a by removing the last 0 (and trailing 1s)
  # and replacing that with a 1 and the same number of 0s.
  a=$(printf "%.*s" ${#a} ${a%0*}1$z)
done

答案3

使用以下方法更容易zsh

for ((i=2#$a; i<=2#$b; i++)) echo $(([##2]i))

或者使用 0 填充:

for ((i=2#$a; i<=2#$b; i++)) printf '%04d\n' $(([##2]i))

否则你可以使用bc

echo "ibase=obase=2; for (i=$a; i<=$b; i++) i" | bc

或者dc

echo "2doi $a [p1+d$b!<a]dsax" | dc

对于 0-pad,您始终可以通过管道将输出传输到:

sed 's/^/000/;s/^0*\(.\{4\}\)/\1/'

答案4

此方法将进行大量处理,因此不适用于大范围。

seq -f '%04g' 0011 1000 | grep -v '[2-9]'

相关内容