1

As clearly spelled out in the Bash documentation on shell arithmetic, if you prefix a number with a 0 within an arithmetic expansion expression (e.g., $(( expr ))), it's treated as octal. Similarly, you can use b#n for a number n in base b.

However, irrespective of the base you use for the arithmetic inside the expression, the value is always returned as base 10. How do I return octal from the result of an arithmetic expansion?

My original use case for this was to warn a user that the permissions on their ~/.my.cnf were too lax (group- or world-readable) by ANDing together the permission bits of the file with a bitmask, but I later decided that testing the output of find ~/.my.cnf -perm +0044 is probably cleaner, and more portable, since stat format strings are not standard between macOS/BSD and Linux. Yet, the question remains.

2
  • 1
    why do you want to return an octal representation of a number from an arithmetic operation?
    – jsotola
    Jun 8, 2019 at 1:27
  • As stated, to be able to view and manipulate file permissions in numeric (octal) rather than symbolic mode. This was sort of an X-Y problem, but no one else on SE seemed to have asked "X," and the solution I found for "X" was still interesting and useful to me, so I recorded it here for posterity. Jun 12, 2019 at 19:10

1 Answer 1

1

Here's a tidbit that I happened upon in a comment in another SE thread: you can just use printf with the %o (octal) format string.

A working example:

# figure out stat(1) flags based on OS
if [[ $(uname -s) == Linux ]]; then PERMS='-c %a' else PERMS='-f %Lp'; fi

stat $PERMS ~/.my.cnf
# result: 600 (not group/world readable)

# permission bits if group/world readable
bitmask=044
perm=$(stat $PERMS ~/.my.cnf)
printf "%o\n" $(( 0$perm & 0$bitmask ))
# result: 0

# now make the file group-readable and try again
chmod 640 ~/.my.cnf
perm=$(stat $PERMS ~/.my.cnf)
printf "%o\n" $(( 0$perm & 0$bitmask ))
# result: 40

Bonus: other solutions

If I don't want to see the permissions bits in octal, I could achieve my original aim without using printf at all. If all I care about is whether any of the bits in the bitmask were set, just testing if the result is non-zero is sufficient:

if (( 0$perm & 0$bitmask )); then
    echo "Permissions $perm are too lax." >&2
fi

If you wanted to know whether all the bits in bitmask were set, then compare the result of ANDing them together to the bitmask, something like this:

(( (0777 & 0066) == 0066 )) && echo "Oh noes, group AND world writable! "

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .