9

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?
  • Do you need to check the group permission bit, or do you need to check if a specific group has write permissions? The group (or the user) could have write permission through an ACL. – Gilles 'SO- stop being evil' Oct 27 '13 at 23:59
  • Just the group permission bit. No specific group is relevant. – mislav Oct 29 '13 at 1:51
8

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
  • I think you missed his point 4. The stat command varies widely among different systems and is not portable. – jordanm Oct 26 '13 at 1: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 1:40
  • 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 1: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 1:49
  • 1
    @jordanm - great minds. I was working on a perl solution too 8-) – slm Oct 26 '13 at 1:58
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.

1

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.

  • 2
    You're using bash, not just POSIX. In POSIX: perms=$(ls -ld -- "$1"); perms=${perms%% *}; case $perms in ?????w????) … – Gilles 'SO- stop being evil' Oct 29 '13 at 23:14
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]'

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.