假设我有两个包含二进制值的 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
.
据我所知,这就是简单的序列生成器,这给您留下了几种可能性。
愚蠢的方法是过滤掉非二进制值。如果a
和b
不是很小的话,这很简单,但效率非常低:
for x in $(seq -w $a $b); do
if [[ ! ($x =~ [2-9]) ]]; then
echo $x
fi
done
这是一个更好的解决方案。假设a
和b
的长度相同(如果不是,您可以使用 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]'