Command to check whether the current user has write permission
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.
41 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 foundLet'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; fiso 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 directoryLooks 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
writeableYou 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
writeableBut 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
writeableIf 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 writeableAs a script, you could write that command like this:
#!/bin/bash
if [ -w "$1" ]; then echo "writeable"
else echo "not writeable"
fiThen you could give it execute permission (replace script with the actual filename)
chmod u+x scriptand 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 writeableIf 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 writeableAs 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 zannaFor 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.