5

I'm wondering if there's a way to replace the if statement with something that checks whether $2 has the 7th bit set to 1?

cat $file | awk '{if ($2 == 87) print $1; else {}}' > out.txt"

For instance, 93 should print something whereas 128 should not.

Inian
  • 80,270
  • 14
  • 142
  • 161
jyu429
  • 71
  • 2
  • 5
  • i m a bit confuse with yoiur code and explain.`$2 == 87` doesn t check 7th bit value, it compare a string that could be evaluate as a number in second field (default separate by space). There is a missing piped instruction or your file is plain text with decimal value separate by space. COuld you provide a sample of *file* – NeronLeVelu Jan 24 '17 at 07:55

4 Answers4

9

bash has bitwise operators

Test 7th bit:

$ echo $(((93 & 0x40) != 0))
1

$ echo $(((128 & 0x40) != 0))
0

See also the bash documentation

Though if you're parsing the values out of a file, you're probably better off continuing to use awk, as the answer of @RakholiyaJenish

chepner
  • 497,756
  • 71
  • 530
  • 681
nos
  • 223,662
  • 58
  • 417
  • 506
3

You can use bitwise operation to check if 7th bit is 1 in gawk:

and($2,0x40)

Note: Standard awk does not have bitwise operation. So for that you can use bash bitwise operation or perl bitwise operation (for string processing).

Using gawk:

gawk '(and($2,0x40)){print $1}' filename

Using perl:

perl -ane 'print "$F[0]\n" if ($F[1]&0x40)' filename
Rakholiya Jenish
  • 3,165
  • 18
  • 28
2

You can use a bash wrapper function to check for the bit set, defining a local IFS operator either in a script or .bashrc

# Returns the bit positions set in the number for the given bit mask
# Use the return value of the function directly in a script

bitWiseAnd() {
    local IFS='&'
    printf "%s\n" "$(( $* ))"
}

and use it in a function as below in your script

# The arguments to this function should be used as number and bit-mask, i.e.
# bitWiseAnd <number> <bit-mask>
if [ $(bitWiseAnd "93" "0x40") -ne 0 ]
then
    # other script actions go here
    echo "Bit set"
fi

The idea is to use the Input-Field-Separator(IFS), a special variable in bash used for word splitting after expansion and to split lines into words. The function changes the value locally to use word-splitting character as the bit-wise AND operator &.

Remember the IFS is changed locally and does NOT take effect on the default IFS behaviour outside the function scope. An excerpt from the man bash page,

The shell treats each character of IFS as a delimiter, and splits the results of the other expansions into words on these characters. If IFS is unset, or its value is exactly , the default, then sequences of , , and at the beginning and end of the results of the previous expansions are ignored, and any sequence of IFS characters not at the beginning or end serves to delimit words.

The "$(( $* ))" represents the list of arguments passed to be split by & and later the calculated value is output using the printf function. The function can be extended to add scope for other arithmetic operations also.

Inian
  • 80,270
  • 14
  • 142
  • 161
0

This function returns an exit status of zero if the number given as its first argument has the bit given as its second argument set:

hasbitset () {
    local num=$1
    local bit=$2

    if (( num & 2**(bit-1) )); then
        return 0
    else
        return 1
    fi
}

or short and less readable:

hasbitset () { (( $1 & 2**($2-1) )) && return 0 || return 1; }

For the examples from the question:

$ hasbitset 93 7 && echo "Yes" || echo "No"
Yes
$ hasbitset 128 7 && echo "Yes" || echo "No"
No

Notice that it's often customary to count bits in offsets instead of positions, i.e., starting from bit 0 – unlike the usage in this question.

Benjamin W.
  • 46,058
  • 19
  • 106
  • 116