18

In a shell script, how do I easily and non-invasively test for write access to a file without actually attempting to modify the file?

I could parse the output of stat, but that seems really complex, and perhaps brittle, though I'm not sure how much stat output differs across implementations and time.

I could append to the end of the file and see if that succeeds, but that's potentially dangerous, for two reasons I can think of:

  1. I now have to remove the addition, and in case some other process writes to the file, this immediately becomes non-trivial as my line is no longer the last one.
  2. Any process reading the file may have arbitrary requirements on the contents of that file, and I may just have broken that application.
26

Just use the -w flag of the test utillity:

[ -w /path/to/file ] && echo "writeable" || echo "write permission denied"

Note that if you're going to write to the file later, it's still possible that you won't be able to write to it. The file may have moved, the permissions may have changed, etc. It can also happen that -w detects write permissions but some other factor intervenes to make the file not writable.

  • 1
    Of course! I should have thought to check test's man page. Thank you. – user50849 Oct 6 '14 at 9:32
  • 1
    @qweilun it is a shell builtin, but you can display the man page via man test or man [ – chaos Oct 6 '14 at 12:57
  • 5
    @chaos It is both a shell builtin and an external executable - try type -a – Volker Siegel Oct 6 '14 at 13:40
  • 1
    Per the source test uses euidaccess which simply checks permission bits. Aren't there other factors (e.g. SELinux) that could prohibit write access? – zamnuts Oct 6 '14 at 18:39
  • 2
    @BroSlow, && and || have equal precedence. They're evaluated from left to right. – Wildcard Mar 6 '16 at 10:01
9

Another approach:

if >> /path/to/file
then
    echo "writeable"
else
    echo "write permission denied"
fi

This will attempt to open the file for appending, and, if that succeeds, run no command (i.e., run a null command) with output to the file. 

Beware that this creates an empty file if it didn't exist.

The -w operator of the test command might just do a stat and then try to figure out whether it looks like you should have access.  My alternative (above) is more reliable than the test approach in some special conditions, because it forces the access check to be done by the kernel rather than the shell.  For example,

  • if the file is on a non-Unix file system – especially if it is remotely mounted from a non-Unix file server – because stat might return a mode value that is misleading.
  • if the file is on a file system that is mounted read-only.
  • if the file has an ACL, and the mode makes it look like you should have access, but the ACL denies it, or vice versa.
  • if some security framework (AppArmor, SELinux, …) denies access to the file.
  • 2
    I just tested this (on Debian). Neither time was altered, and that is the way it should work on any Unix. Access time should be updated only if you read from the file, modify time should be updated only if you write to the file. This code does neither. @Schwern: do you have a reference for your statement? Have any of you tried it? – G-Man Oct 6 '14 at 18:21
  • 2
    P.S. @muru: I just tried to touch a file that I owned but didn't have write access to, and it succeeded. I guess it chmods the file and chmods it back. So touch appears to be absolutely useless as an answer to the question. – G-Man Oct 6 '14 at 18:24
  • 2
    @G-Man ah, that's interesting. IIRC vim has that behaviour of quickly changing the permissions when forced to write on read-only files. I checked with strace, touch's open fails with EACCES, but the subsequent call to utimensat succeeds, which is why I think touch on the whole exits successfully. – muru Oct 6 '14 at 18:31
  • 2
    @muru: Thanks for checking that. utimensat(2) says, “Permissions requirements: 1. write access (or) 2. the caller’s effective user ID must match the owner of the file, ….” – G-Man Oct 6 '14 at 18:38
  • 2
    Other problems like for Champignac's: >> file is not portable (for instance, runs the NULLCMD in zsh), use true >> file instead. And if the file is a named pipes, it has nasty side effects. – Stéphane Chazelas Dec 1 '17 at 19:04
3

G-man is right: [ -w ] won't always tell the truth. Here to cope with non-existing file and Permission denied message from shell:

( [ -e /path/to/file ] && >> /path/to/file ) 2> /dev/null && 
  echo writable || 
  echo not writable

Update: Looks frightening, isn't? Well, it is. Hmm... how to phrase it... DON'T USE THIS, unless you perfectly know you are in the conditions it requests to work as expected. See Stephane's comment.

What to conclude, then? Even if [ -w ] does not tell the truth, it is the one command that is intended to do the job. If it does not, well, we'll blame it, write bug reports, and it will work in the future. Better check the conditions under which it works and use [ -w ]; write special code for special cases. Workarounds have their own conditions.

[ -w /path/to/file ]

is the best a priori.

  • 2
    If the file is a named pipe, that will hang if there's no read, and even if there's a reader will have nasty side effects. test -w in most implementations uses access(2) so should be enough to test permissions. – Stéphane Chazelas Dec 1 '17 at 18:34

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.