Velvet Star Monitor

Standout celebrity highlights with iconic style.

news

Command to check whether the current user has write permission

Writer Sophia Terry

I have to create a script to check if the current user has write permission on a file. If not, an error should appear.

I wrote this:

if [namei -m /path/to/textfile.txt | grep w]; then echo "message"

But I seem to get a syntax error.

4

1 Answer

Let's work through the issues with your code. As dessert mentioned in a comment, the first problem is the syntax of the [ command. We need to put spaces between [ and its arguments, because the shell uses spaces to separate fields, and thinks that [namei is the command you want to run.

I'm going to use a file in my home directory called file, which I know I have write permission on, in my examples. I have put your code on one line, to make it easier to test interactively. It will work in the same way as if there were a newline after then.

$ if [namei -m playground | grep w]; then echo "writeable"
No command '[namei' found, did you mean: Command 'namei' from package 'util-linux' (main)
[namei: command not found

Let's fix the spaces around [ and ]:

$ if [ namei -m playground | grep w ]; then echo "writeable"
>

Now I get a prompt to type something, a >. The prompt tells me that the shell is waiting for me to enter something. The simplest form of the syntax of if is

if condition; then command; fi

so the shell is waiting for the fi. If I enter it at the prompt, I get this:

$ if [ namei -m file | grep w ]; then echo "writeable"
> fi
bash: [: missing `]'
grep: ]: No such file or directory

Looks like the pipe | is causing problems here, since [ can't find its last argument, and grep thinks its last argument is ]. Since [ (or the one we're using here anyway) is a shell builtin, we should be able to get some information about it using help

$ help [
[: [ arg... ] Evaluate conditional expression. This is a synonym for the "test" builtin, but the last argument must be a literal `]', to match the opening `['.

We can't use a pipe in the middle of a command's arguments. In other situations, we might be able to put the commands in a subshell by enclosing them in ( ), but that is a syntax error in [ (and test).

The real problem here is that what we are trying to pass to the [ command is not something it can evaluate as true or false. If the argument you try to pass to test is a command, test does not use the exit status of that command as I think you are expecting. You can pass the output of a command to [ using command substitution, but that is not what you or your code want(s) to do.

I think what your code is trying to do is check that grep found a w in the output of namei -m somefile. We don't need the test command for that at all.

$ help if
if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi Execute commands based on conditional. The `if COMMANDS' list is executed. If its exit status is zero, then the `then COMMANDS' list is executed. Otherwise, each `elif COMMANDS' list is executed in turn, and if its exit status is zero, the corresponding `then COMMANDS' list is executed and the if command completes. Otherwise, the `else COMMANDS' list is executed, if present. The exit status of the entire construct is the exit status of the last command executed, or zero if no condition tested true.

So if we are only interested in whether a command succeeds, we can put the command as a condition for if:

$ if namei -m file | grep w; then echo "writeable"; fi -rw-rw-r-- file
writeable

You can even use -q to suppress output from grep and collect only its exit status (success if a match is found):

$ if namei -m file | grep -q w; then echo "writeable"; fi
writeable

But that's not a good way to test whether the file is writeable by you. Let's try another test file:

$ touch file_with_w
$ chmod 444 file_with_w
$ stat -c %A file_with_w
-r--r--r--
$ if namei -m file_with_w | grep -q w; then echo "writeable"; fi
writeable

If we parse the output of namei -m for a file with a w in its name, this method will make it appear to be writeable even if it is read only for everyone.

Parsing the output of commands is sometimes the right way to get the information you want. But there is usually a better way, in the form of an option to that command, or a different command with a similar purpose.

Let's go back to test aka [ command and see if it has any useful options. You can enter help test, or for more detail read the Conditional Expressions section of the Bash manual

File operators:
... -w FILE True if the file is writable by you.

Aha! the test command has exactly what we want, as mentioned in answer to Checking for a file and whether it is readable and writable

$ if [ -w file ]; then echo "writeable"; fi
writeable
$ if [ -w file_with_w ]; then echo "writeable"; fi
$ 

That's right :)

But you mentioned that you wanted the command to output an error message if the file is not writeable, rather than a statement that it is writeable. For that we need an else statement

$ if [ -w file_with_w ]; then echo "writeable"; else echo "not writeable"; fi
not writeable

As a script, you could write that command like this:

#!/bin/bash
if [ -w "$1" ]; then echo "writeable"
else echo "not writeable"
fi

Then you could give it execute permission (replace script with the actual filename)

chmod u+x script

and run it with the file you want to test as its argument (referenced in the script with $1):

$ ./script file
writeable
$ ./script file_with_w
not writeable

If you only want to get an error message when the file is non-writeable, and no output when it is, you can negate the test condition instead of writing an else:

$ if [ ! -w file_with_w ]; then echo "not writeable"; fi
not writeable

As Panther mentioned in a comment, you might also want to make use of the variable $USER, which should expand to the name of the user running the shell. You can thus make a more impressive error message:

$ if [ ! -w file_with_w ]; then echo "file is not writeable by $USER"; fi
file is not writeable by zanna

For simple test commands, you can also use [ and test and [[ without if, and use shell operators for the logic:

$ [ -w file_with_w ] || echo "not writeable"
not writeable

|| is the shell's logical OR. It means if the previous command failed, then execute the next command

The shell's logical AND is &&:

$ [ ! -w file_with_w ] && echo "not writeable"
not writeable

&& means if the previous command succeeded, then execute the next command

All of this works because the test command gives a helpful exit status as described in help test:

Exit Status:
Returns success if EXPR evaluates to true; fails if EXPR evaluates to
false or an invalid argument is given.

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

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