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

하지만 $blah10진수가 되고 2진수로 유지하고 싶습니다. (언제든지 10진수를 다시 2진수로 변환할 수 있지만 이미극심한바이너리)

코드는 제한된 Linux(오픈WRT) 사용할 수 없습니다 obase. 대답이 이렇다면이진 값을 보존하는 것은 불가능합니다, 이것은 또한 유용한 답변입니다 ( 를 사용하지 않고 10진수를 2진수로 변환하는 함수를 만들 수 있습니다 obase) 또한 일반 bash.

답변1

당신은 이것에 꽤 가깝습니다 :

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

for비슷한 방법을 사용하여 루프에서 10진수 값을 다시 2진수로 변환하면 됩니다 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앞에 0이 채워지고 특정 너비로 ​​형식이 지정되도록 출력을 제어하는 ​​데 사용됩니다 . 매개변수는 %04d출력 내용을 지정합니다.

이 명령의 또 다른 핵심은 bc명령줄 계산기를 사용하는 것입니다. 이 명령의 예는 다음과 같습니다.

echo "obase=2;$blah" | bc

를 사용하여 값을 가져와서 $blah2진수(이진수라고도 함)로 변환합니다 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 표준의 일부도 아닙니다. 그러나 일반적인 구현에는 seq10이 아닌 기준으로 정렬하는 기능이 없습니다.

Bash 에서는 범위 를 {start..finish}.{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]'

관련 정보