14

I would like to inspect group permissions of a file from a bash script. Specifically, I need to check whether a file has the group writeable bit on.

That's it. Simple as that. However:

  1. I also need this to be portable.
  2. test -w <file won't tell me whether it's group writeable.
  3. The output of ls -ld is nice for humans, but not so sure about scripts. Technically I could parse output such as drwxrwxr-x to extract the group bits, but this seems brittle.
  4. The interface for stat is completely incompatible between OS X and other systems.
  5. find <file> -perm ... can't possibly be the answer?
mislav
  • 381

4 Answers4

10

Method #1 - stat

You could use the stat command to get the permissions bits. stat is available on most Unixes (OSX, BSD, not AIX from what I can find). This should work on most Unixes, except OSX & BSD from what I can find.

$ stat -c "%a" <file>

Example

$ ls -l a
-rw-rw-r-- 1 saml saml 155 Oct  6 14:16 afile.txt

Use this command:

$ stat -c "%a" afile.txt
664

And use a simple grep to see if the groups permissions mode is a 6 or 7.

$ stat -c "%a" afile.txt | grep ".[67]."

For OSX and BSD you'd need to use this form of stat, stat -f (or perhaps stat -x), and parse accordingly. Since the options to stat are different you could wrap this command in an lsb_release -a command and call the appropriate version based on the OS. Not ideal but workable. Realize that lsb_release is only available for Linux distros so another alternative would need to be devised for testing other Unix OSes.

Method #2 - find

I think this command might serve you better though. I makes use of find and the printf switch.

Example

$ find a -prune -printf '%m\n'
664

Method #3 - Perl

Perl might be a more portable way to do this depending on the OSes you're trying to cover.

$ perl -le '@pv=stat("afile.txt"); printf "%04o", $pv[2] & 07777;'
0664

NOTE: The above makes use of Perl's stat() function to query the filesystem bits.

You can make this more compact by forgoing using an array, @pv, and dealing with the output of stat() directly:

$ perl -le 'printf "%04o", (stat("a"))[2] & 07777;'
0664
slm
  • 369,824
  • I think you missed his point 4. The stat command varies widely among different systems and is not portable. – jordanm Oct 26 '13 at 01:34
  • @jordanm - thanks I saw it, was looking for an alternative. I think find . -printf can do it but can't determine if it's available on OSX' find. – slm Oct 26 '13 at 01:40
  • I am not sure either, I know it's not POSIX. – jordanm Oct 26 '13 at 01:44
  • 1
    I couldn't find it referenced in the POSIX2008 guide either, so I'm inclined to agree. I'm not sure of any other methods. I think you have to bite the bullet here and detect which OS and call the approp. command, would seem to be the best approach. – slm Oct 26 '13 at 01:45
  • 1
    I couldn't think of a method using a standard shell utility either. That's why my answer uses perl. – jordanm Oct 26 '13 at 01:49
  • 1
    @jordanm - great minds. I was working on a perl solution too 8-) – slm Oct 26 '13 at 01:58
  • lsb_release -a is a Linux-only command. Not that surprising since LSB stands for Linux Standard Base. If all the platforms he is working on have stat, it would be possible to detect which version it is. On OpenBSD at least, while stat -x could work, stat -f seems to be a lot closer to the stat -c you were using. – kurtm Oct 26 '13 at 02:19
  • @kurtm - Yup know all about lsb_release. I've added links to another U&L Q&A about that. You could run multiple stat commands and based on the results, determine if you're on a platform that supports one switch vs. another. Added info on diff. vers. of stat, thanks. – slm Oct 26 '13 at 02:41
  • @slm: Thanks for the plethora of approaches. I ended up writing and picking my own answer since I wanted to go with the least amount of code, and no extra language interpreter if I can avoid it. – mislav Oct 29 '13 at 02:25
  • This is old, but shouldn't the pattern in Method #1 include '2'? So it would be a grep for ".[267].". – Noah Spurrier Feb 10 '15 at 23:28
  • @NoahSpurrier - No, I don't think so. He was looking to find out if the write bit was enabled or not. The grep will detect if it's on when there's either a 6 or a 7 in the middle of the 3 digits that is returned via stat. A 2 would denote that it was read only. If the grep for my pattern returns nothing, that would mean that it was potentially a 2 or 0. – slm Feb 11 '15 at 00:05
2

I ended up ls-parsing:

is_group_writable() {
  local ls="$(ls -ld "$1")"
  [ "${ls:5:1}" = "w" ]
}

I'm grateful for @slm's extensive answer, but none of the other methods are as straightforward. The stat method would require me to write at least two different invocations to cover OS X, and even after that it returns the octal representation which I still have to apply arithmetic to. The Perl method is OK but requires me to start an interpreter for such a simple task, and I would like to keep it simple and using only posix utils.

mislav
  • 381
1

One option is to use perl, which will be portable everywhere perl is available. It's tough to find a unix system without it. Here is an example of how it can be integrated into a shell script:

if perl -e 'use Fcntl ":mode"; exit( ((stat("file.txt"))[2] & S_IWGRP) >> 3)'; then
    echo "file is group writable"
else
    echo "file is not group witeable"
fi

Similar perl examples can be found in the stat perldoc.

jordanm
  • 42,678
0
FILE="filename";G=$(stat -c '%a' ${FILE} | cut -c2);
if [ $G -gt 5 ];then   echo "Group write is set on ${FILE}";fi

All standard Unix/Linux tools.

I suppose it's POSSIBLE to have group write, but not group read. In that possibility, it would be either a case statement 2|3|6|7) or a grep '[2|3|6|7]'

Doc
  • 1