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? Commented 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. Commented 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.