How to check if running as root in a bash script - bash

I'm writing a script that requires root level permissions, and I want to make it so that if the script is not run as root, it simply echoes "Please run as root." and exits.
Here's some pseudocode for what I'm looking for:
if (whoami != root)
then echo "Please run as root"
else (do stuff)
fi
exit
How could I best (cleanly and securely) accomplish this? Thanks!
Ah, just to clarify: the (do stuff) part would involve running commands that in-and-of themselves require root. So running it as a normal user would just come up with an error. This is just meant to cleanly run a script that requires root commands, without using sudo inside the script, I'm just looking for some syntactic sugar.

The $EUID environment variable holds the current user's UID. Root's UID is 0. Use something like this in your script:
if [ "$EUID" -ne 0 ]
then echo "Please run as root"
exit
fi
Note: If you get 2: [: Illegal number: check if you have #!/bin/sh at the top and change it to #!/bin/bash.

A few answers have been given, but it appears that the best method is to use is:
id -u
If run as root, will return an id of 0.
This appears to be more reliable than the other methods, and it seems that it return an id of 0 even if the script is run through sudo.

In a bash script, you have several ways to check if the running user is root.
As a warning, do not check if a user is root by using the root username. Nothing guarantees that the user with ID 0 is called root. It's a very strong convention that is broadly followed but anybody could rename the superuser another name.
I think the best way when using bash is to use $EUID, from the man page:
EUID Expands to the effective user ID of the current user, initialized
at shell startup. This variable is readonly.
This is a better way than $UID which could be changed and not reflect the real user running the script.
if (( $EUID != 0 )); then
echo "Please run as root"
exit
fi
A way I approach that kind of problem is by injecting sudo in my commands when not run as root. Here is an example:
SUDO=''
if (( $EUID != 0 )); then
SUDO='sudo'
fi
$SUDO a_command
This ways my command is run by root when using the superuser or by sudo when run by a regular user.
If your script is always to be run by root, simply set the rights accordingly (0500).

My one-liner:
if [ "$(id -u)" -ne 0 ]; then echo "Please run as root." >&2; exit 1; fi

In this answer, let it be clear, I presume the reader is able to understand the difference betweeen modern shells like bash, zsh and others vs portable POSIX shells like dash.
I believe there is not much to explain here since the highly voted answers do a good job of explaining much of it.
Yet, if there is anything to explain further, don't hesitate to comment, I will do my best by filling the gaps.
Optimized all-round solution for performance and reliability; all shells compatible
New solution:
# bool function to test if the user is root or not
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }
Benchmark (save to file is_user_root__benchmark)
#+------------------------------------------------------------------------------+
#| is_user_root() benchmark |
#| "Bash is fast while Dash is slow in this" |
#| Language: POSIX shell script |
#| Copyright: 2020-2021 Vlastimil Burian |
#| M#il: info[..]vlastimilburian[..]cz |
#| License: GPL 3.0 |
#| Version: 1.2 |
#+------------------------------------------------------------------------------+
readonly iterations=10000
# intentionally, the file does not have an executable bit, nor it has a shebang
# to use it, just call the file directly with your shell interpreter like:
# bash is_user_root__benchmark ## should take a fraction of one second
# dash is_user_root__benchmark ## could take around 10 seconds
is_user_root () { [ "${EUID:-$(id -u)}" -eq 0 ]; }
print_time () { date +"%T.%2N"; }
print_start () { printf '%s' 'Start : '; print_time; }
print_finish () { printf '%s' 'Finish : '; print_time; }
printf '%s\n' '___is_user_root()___'; print_start
i=1; while [ "$i" -lt "$iterations" ]; do
is_user_root
i=$((i+1))
done; print_finish
Examples of use and duration:
$ dash is_user_root__benchmark
___is_user_root()___
Start : 03:14:04.81
Finish : 03:14:13.29
$ bash is_user_root__benchmark
___is_user_root()___
Start : 03:16:22.90
Finish : 03:16:23.08
Explanation
Since it is multitude times faster to read the $EUID standard bash variable, the effective user ID number, than executing id -u command to POSIX-ly find the user ID, this solution combines both into a nicely packed function. If, and only if, the $EUID is for any reason not available, the id -u command will get executed, ensuring we get the proper return value no matter the circumstances.
Why I post this solution after so many years the OP has asked
Well, if I see correctly, there does seem to be a missing piece of code above.
You see, there are many variables which have to be taken into account, and one of them is combining performance and reliability.
Portable pure POSIX solution + Example of usage of the above function
#!/bin/sh
# bool function to test if the user is root or not (POSIX only)
is_user_root ()
{
[ "$(id -u)" -eq 0 ]
}
if is_user_root; then
echo 'You are the almighty root!'
# You can do whatever you need...
else
echo 'You are just an ordinary user.' >&2
exit 1
fi
Conclusion
As much as you possibly don't like it, the Unix / Linux environment has diversified a lot. Meaning there are people who like bash, zsh, and other modern shells so much, they don't even think of portability (POSIX). It is nowadays a matter of personal choice and needs.

There is a simple check for the user being root.
# Fancy red-colored `error` function with `stderr` redirection with `exit`.
error ()
{
{ printf '\E[31m'; echo "$#"; printf '\E[0m'; } >&2
exit 1
}
# Test for root user.
if [ "$EUID" -eq 0 ]; then
error "Do not run this as the root user"
fi
This also assumes that you want to exit with a 1 if you fail. The error function is some flair that sets output text to red (not needed, but pretty classy if you ask me).

As #wrikken mentioned in his comments, id -u is a much better check for root.
In addition, with proper use of sudo, you could have the script check and see if it is running as root. If not, have it recall itself via sudo and then run with root permissions.
Depending on what the script does, another option may be to set up a sudo entry for whatever specialized commands the script may need.

1- Read official GNU Linux documentation, there are many ways to do it correctly.
2- make sure you put the shell signature to avoid errors in interpretation:
#!/bin/bash
3- this is my script
#!/bin/bash
if [[ $EUID > 0 ]]; then
echo "Please run as root/sudo"
exit 1
else
#do your stuff
fi

Very simple way just put:
if [ "$(whoami)" = "root" ]; then
# you are root
else
# you are not root
fi
The benefit of using this instead of id is that you can check whether a certain non-root user is running the command, too; eg.
if [ "$(whoami)" = "john" ]; then
# you are john
else
# you are not john
fi
Editor's Note: Either prefer using POSIX-compatible test command and comparison [... = ...] or you can use Bash/+other shells specific [[... == ...]].

One simple way to make the script only runnable by root is to start the script with shebang:
#!/bin/su root

If the script really requires root access then its file permissions should reflect that. Having a root script executable by non-root users would be a red flag. I encourage you not to control access with an if check.
chown root:root script.sh
chmod u=rwx,go=r script.sh

Try the following code:
#!/bin/sh
if [ "$(id -u)" -ne 0 ]; then
echo "Sorry, you are not root." >&2
exit 1
fi
Note on edit: Backticks are considered deprecated by now (many further notes).

The problem using: id -u, $EUID and whoami, is all of them give false positive when I fake the root, like this:
$ fakeroot
id:
$ id -u
0
$EUID:
$ echo $EUID
0
whoami:
$ whoami
root
Then a reliable (and hacking) way is verify if the user has access to the /root directory:
$ ls /root >/dev/null 2>&1 && is_root=true || is_root=false; echo "$is_root"

id -u is much better than whoami, since some systems like Android may not provide the word root.
Example command:
# whoami
Example output:
whoami: unknown uid 0

As far as I know, the correct way to check it, is as follows:
#!/bin/sh
if [ "$(id -u)" -eq 0 ]; then
echo "You are root."
else
echo "You are NOT root." >&2
fi
OP's Note: you may see "Testing For Root" section on linuxcommand.org.
Editor's Note: I have slightly adjusted this shell snippet to be POSIX-compatible.

Check if you are root and quit if you are not:
if ((EUID != 0)); then
echo "Root or Sudo Required for script ( $(basename $0) )"
exit
fi
Or in this example, try to create a directory in root location then try after rights were elevated.
Check if you are root and if not elevate if possible :
# Fails to create these dirs (needs sudo)
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)
if ((EUID != 0)); then
echo "Granting root privileges for script ( $(basename $0) )"
if [[ -t 1 ]]; then
sudo "$0" "$#"
else
exec 1> output_file
gksu "$0" "$#"
fi
exit
fi
echo "Root privileges granted..."
# Creates Dirs as it now has rights
mkdir /test-dir-$(basename $0)
rmdir /test-dir-$(basename $0)

It is important to notice that whenever you run a script using sudo the 'user context' or environment will switch to root.
But Teo what that means?
Well, my young padawan, this means that if a padawan user runs a script that contains a tilde (~) using sudo, whenever the bash will expand ~ the result will be /root and not /home/<user> (i.e., in this case /home/padawan), or if you create either a directory or a file the owner and group will be root and not the that executed the script in this case padawan, because the user environment was switched.
For instance, lets check this script install-app.sh:
#!/bin/bash
ROOT_UID=0 # Only users with $UID 0 have root privileges.
E_NOTROOT=87 # Non-root exit error.
## Prevent the execution of the script if the user has no root privileges
if [ "${UID:-$(id -u)}" -ne "$ROOT_UID" ]; then
echo 'Error: root privileges are needed to run this script'
exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
service mysql start
...
If we run using sudo:
sudo install-app.sh
This will create directories and a config file will look like this:
##
## ~ (/root)
drwxr-xr-x 17 root root 4096 Nov 23 20:45 ./
drwxr-xr-x 5 root root 4096 Nov 15 19:04 ../
...
drwxr-xr-x 3 root root 4096 Nov 25 14:30 app/
...
drwxr-xr-x 2 root root 4096 Nov 16 19:08 tmp/
## ~/app (/root/app)
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ./
drwxr-xr-x 17 root root 4096 Nov 25 14:33 ../
drwxr-xr-x 2 root root 4096 Nov 25 14:33 init/
## ~/app/init (/root/app/init)
drwxr-xr-x 2 root root 4096 Nov 25 14:33 ./
drwxr-xr-x 3 root root 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root 0 Nov 25 14:33 config
## /home/<user>/app/conf
drwxr-xr-x 2 <user> <user> 4096 Nov 25 14:43 ./
drwxr-xr-x 3 <user> <user> 4096 Nov 25 14:30 ../
-rw-r--r-- 1 root root 0 Nov 25 14:43 profile
As you can she the script is a total mess. Now the <user> cannot get access to the profile file neither can modify the config without sudo. At the beginning seams to be something not important but trust me if your project gets bigger someone will run the script and mess with your system.
Recommendation
My recommendation will be: request to the user to verify if is a sudoer or not. Then, add sudo to the commands that require it.
Applying this changes to the script will be like this:
#!/bin/bash
E_NOTROOT=87 # Non-root exit error.
## Prevent the execution of the script if the user has no root privileges
## Check if is sudoer
if ! $(sudo -l &>/dev/null); then
echo 'Error: root privileges are needed to run this script'
exit $E_NOTROOT
fi
...
mkdir -vp ~/app/init
touch config
...
touch /home/<user>/app/init/profile
sudo service mysql start
...
This modification allows the user to run the script like this:
install-app.sh
The user will be requested to insert his password to verify if is sudoer. After,mkdir -vp ~/app/init will create the file in the user's home:
/home/<user>/app/init
/home/<user>/app/init/config
/home/<user>/app/init/profile
Also, I recommend to get the users homer directory and use it as a constant.
## Defines user home directory
USER_HOME_DIR=$(getent passwd ${SUDO_USER:-$USER} | cut -d: -f6)
...
mkdir -vp "$USER_HOME_DIR/app/init"
...

I stumbled on the same issue. The line #!/bin/su root would be an elegant method, but does not work on certain (higher) versions of bash, as mentioned by #basickarl. Further - this method will probably fail when you start the script with /bin/bash ./some_script.(b)sh.
I found an other page on SO where the use of the environment variables SUDO_UID and SUDO_USER were used. When a normal user invokes a script with sudo these are available in the script. Otherwise they are empty. Also: when you are actually logged on as root these values are empty (or unset - not entirely sure) as well.
So this is the solution I'm currently using:
isSudo()
{
local _fn="${FUNCNAME[0]}"
if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
>2 echo -e "$_fn: invoked as sudo-user ($SUDO_USER)."
else
>2 echo -e "$_fn: invoked as root ($SUDO_USER)."
fi
return 0
fi
>2 echo "$_fn: not as root or as sudoer."
return 1
}
Then use it like this eg.:
if $(isSudo) ; then
echo -e "Script started as root or as sudoer."
else
echo -e "Script not started as root or with sudo"
fi
The >2 in front of the echo is to redirect the echo to stderr otherwise the if statement goes haywire and it's just for a little debugging. In stead of using an echo, you could save "root", "sudoer" or "user" in a local variable and let the function echo that back and then perform a case on that like this:
how_invoked()
{
local _invoked_as="USER"
if [[ $(/usr/bin/id -u) -eq 0 ]] ; then
if [[ ! -z "$SUDO_UID" && ! -z "$SUDO_USER" ]] ; then
_invoked_as="SUDOER"
else
_invoked_as="ROOT"
fi
fi
echo -n "$_invoked_as" # -n prevents newline
}
And then use it like this:
INVOKED_AS=$(how_invoked)
case $INVOKED_AS in
USER)
echo -e "Invoked as user." ;;
SUDOER)
echo -e "Invoked as sudoer." ;;
ROOT)
echo -e "Invoked as root." ;;
*)
echo -e "I'm confused" ;;
esac
Or if you just want to test for one of those:
if [ "$(how_invoked)" == "SUDOER" ] ; then
echo -e "You are a sudoer."
else
echo -e "You are NOT a sudoer."
fi
Hope this helps.

My one-liner:
[ "$(whoami)" != root ] && echo "Please, run as root." >&2 && exit 1
Tested under Debian, Ubuntu and Docker.

My two cents, running in Bash:
#!/bin/bash
# Display the UID variable
echo "Your UID is $UID"
# Check for root user via UID
if [ "$UID" -eq 0 ]; then
echo "You are root"
else
echo "You are not root user" >&2
fi

My check for the root user, running in Bash:
#!/bin/bash
if [ "$UID" -eq 0 ]; then
echo "You are root."
else
echo "You are just an ordinary user." >&2
fi

Related

.profile has statements to call .bashrc, but .bashrc not executed

I'm using Ubuntu 16.04. In my $HOME/.profile, it has
echo `date` ':.profile executed starts' >> /tmp/testtmp.txt
echo $BASH_VERSION >> /tmp/testtmp.txt
if [ -n "$BASH_VERSION" ]; then
echo 'if 1 entered' >> /tmp/testtmp.txt
# include .bashrc if it exists
if [ -f "$HOME/.bashrc" ]; then
echo 'if 2 entered' >> /tmp/testtmp.txt
. "$HOME/.bashrc"
fi
else
echo 'if 1 not entered' >> /tmp/testtmp.txt
fi
Every time I login, it never even reaches the first IF.
The test file shows:
Sat Jan 14 05:15:57 EST 2017 :.profile executed starts
if 1 not entered
Sat Jan 14 05:15:57 EST 2017 :.profile executed ends
I echoed $BASH_VERSION, it shows 4.3.46(1)-release. So if if [ -n "$BASH_VERSION" ] is meant to check if that variable exists, (apparently yes), why is it not even reaching the first IF?
From the result, it looks like $BASH_VERSION is not generated until the first time I invoked terminal application.
As those IF statements in .profile are original, I don't understand if $BASH_VERSION is not generated when you login in, what is the purpose to have these statements originally.
Thanks
(This was getting overly long as a comment :) )
See this question on Ask Ubuntu. .profile is probably running under sh, not bash, so BASH_VERSION isn't set in .profile.
To test this, add
ps >> /tmp/testtmp.txt
to your .profile. You will see the process name of the shell you're running under, which may be dash per this.
To see what your default shell is, grep <whatever your username is> /etc/passwd - you will see /bin/<whatever>sh.
If indeed /bin/bash is not set as your default shell, you can change it (per this answer):
sudo chsh -s /bin/bash <whatever your username is>

Trying to find out if chmod has failed

Im just wondering if there is any way to say 'if chmod does not change permissions of the file, then ...'
if ! chmod ${1} ${2} #if chmod did not excecute correctly
then
echo "${0}:ERROR: ${2} has not been changed." 1>&2
exit 6 #exiting due to failure. status 6
fi
I have tried this and several other ways but I can only add to the current setting rather than change it...
i.e if i start with -rwxr-xr-x i change it to -rwxrwxrwx
nothing else seems to work
Your command is correct. But it only detects when the chmod can not operate (e.g. when you don't own the file, when the FS is read-only, etc.).
If you also want to detect that the chmod didn't change anything, try this:
if ! res=$(chmod -c -- "${1}" "${2}") || [ -z "$res" ]
then
echo "${0}:ERROR: ${2} has not been changed." 1>&2
exit 6 #exiting due to failure. status 6
fi
The -c flag is specific to GNU chmod. Here is the description:
-c, --changes
like verbose but report only when a change is made
-v, --verbose
output a diagnostic for every file processed

How to check if a file exists in a shell script

I'd like to write a shell script which checks if a certain file, archived_sensor_data.json, exists, and if so, deletes it. Following http://www.cyberciti.biz/tips/find-out-if-file-exists-with-conditional-expressions.html, I've tried the following:
[-e archived_sensor_data.json] && rm archived_sensor_data.json
However, this throws an error
[-e: command not found
when I try to run the resulting test_controller script using the ./test_controller command. What is wrong with the code?
You're missing a required space between the bracket and -e:
#!/bin/bash
if [ -e x.txt ]
then
echo "ok"
else
echo "nok"
fi
Here is an alternative method using ls:
(ls x.txt && echo yes) || echo no
If you want to hide any output from ls so you only see yes or no, redirect stdout and stderr to /dev/null:
(ls x.txt >> /dev/null 2>&1 && echo yes) || echo no
The backdrop to my solution recommendation is the story of a friend who, well into the second week of
his first job, wiped half a build-server clean. So the basic task is to figure out if a file exists,
and if so, let's delete it. But there are a few treacherous rapids on this river:
Everything is a file.
Scripts have real power only if they solve general tasks
To be general, we use variables
We often use -f force in scripts to avoid manual intervention
And also love -r recursive to make sure we create, copy and destroy in a timely fashion.
Consider the following scenario:
We have the file we want to delete: filesexists.json
This filename is stored in a variable
<host>:~/Documents/thisfolderexists filevariable="filesexists.json"
We also hava a path variable to make things really flexible
<host>:~/Documents/thisfolderexists pathtofile=".."
<host>:~/Documents/thisfolderexists ls $pathtofile
filesexists.json history20170728 SE-Data-API.pem thisfolderexists
So let's see if -e does what it is supposed to. Does the files exist?
<host>:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $?
0
It does. Magic.
However, what would happen, if the file variable got accidentally be evaluated to nuffin'
<host>:~/Documents/thisfolderexists filevariable=""
<host>:~/Documents/thisfolderexists [ -e $pathtofile/$filevariable ]; echo $?
0
What? It is supposed to return with an error... And this is the beginning of the story how that entire
folder got deleted by accident
An alternative could be to test specifically for what we understand to be a 'file'
<host>:~/Documents/thisfolderexists filevariable="filesexists.json"
<host>:~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $?
0
So the file exists...
<host>:~/Documents/thisfolderexists filevariable=""
<host>:~/Documents/thisfolderexists test -f $pathtofile/$filevariable; echo $?
1
So this is not a file and maybe, we do not want to delete that entire directory
man test has the following to say:
-b FILE
FILE exists and is block special
-c FILE
FILE exists and is character special
-d FILE
FILE exists and is a directory
-e FILE
FILE exists
-f FILE
FILE exists and is a regular file
...
-h FILE
FILE exists and is a symbolic link (same as -L)
Internally, the rm command must test for file existence anyway,
so why add another test? Just issue
rm filename
and it will be gone after that, whether it was there or not.
Use rm -f is you don't want any messages about non-existent files.
If you need to take some action if the file does NOT exist, then you must test for that yourself. Based on your example code, this is not the case in this instance.
If you're using a NFS, "test" is a better solution, because you can add a timeout to it, in case your NFS is down:
time timeout 3 test -f
/nfs/my_nfs_is_currently_down
real 0m3.004s <<== timeout is taken into account
user 0m0.001s
sys 0m0.004s
echo $?
124 <= 124 means the timeout has been reached
A "[ -e my_file ]" construct will freeze until the NFS is functional again:
if [ -e /nfs/my_nfs_is_currently_down ]; then echo "ok" else echo "ko" ; fi
<no answer from the system, my session is "frozen">
You could also uses stat :
stat /
File: /
Size: 4096 Blocks: 8 IO Block: 4096 directory
Device: fd01h/64769d Inode: 2 Links: 26
Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2009-01-01 02:00:00.000000000 +0200
Modify: 2009-01-01 02:00:00.000000000 +0200
Change: 2009-01-01 02:00:00.000000000 +0200
Birth: -
On a path that doesn't exist, you will get:
stat /aaa
stat: cannot stat '/aaa': No such file or directory

Checking Regular File and Directory Ownership and Group Membership in a Bash Shell Script

I am writing shell scripts in Bash 3.2.57(1)-release under Solaris 10 (uname -a yields "SunOS hostname 5.10 Generic_150400-35 sun4v sparc sun4v").
The ultimate problem I'm trying to solve is, within a bash shell script, determining the owner and group membership of a regular file or directory.
Unfortunately, stat is not available to me.
With stat unavailable, the next best thing I know of is parsing the owner and group membership out of the output of ls -l. I do not like this approach in case portability ever becomes an issue and in case I ever come across filenames with special characters in them that throw the parsing off. But, parsing the output of ls -l is what I've got at the moment...
This leads to two questions.
QUESTION ONE
In the absence of stat and with the condition of excluding ls -l as part of the solution, is there an accepted, not-too-hackish way to determine the owner and group membership of a regular file or directory on my specific platform (Bash 3.2.57(1)-release under Solaris 10)?
In Googling around, this does seem to be a real challenge.
If anyone has suggestions on alternatives to parsing ls, I'd sure appreciate hearing them.
QUESTION TWO
Here is my current best attempt via the parsing of the output of ls -l:
#!/usr/bin/bash
###
###
###
# This functions returns success if:
# 1. The ownership of the pass file is root AND
# 2. The group membership of the passed file is root
#
# This function returns failure otherwise.
function check_if_ownership_and_group_membership_are_root
{
if [[ $# -ne 1 ]]; then
echo
echo "Usage: ${FUNCNAME[0]} <filename>"
echo "Actual (incorrect) usage: ${FUNCNAME[0]} $*"
echo "ABORTING SCRIPT; Failure point is designated \"Point 1\""
exit 1
fi
local FILENAME="${1}"
# Check ownership of "${FILENAME}".
if [[ $(ls -l "${FILENAME}" | awk '{print $3}')="root" ]]; then
echo
echo "Part 1: Test of ownership being root supposedly passed"
echo "Actual ownership: " $(ls -l "${FILENAME}" | awk '{print $3}')
# The test of ownership being root passed. Continue on to test the group membership being root.
else
echo
echo "Part 2: Test of ownership being root supposedly failed"
echo "Actual ownership: " $(ls -l "${FILENAME}" | awk '{print $3}')
# The test of ownership being root did not pass. Return failure.
return 1
fi
# Check the group membership of "${FILENAME}".
if [[ $(ls -l "${FILENAME}" | awk '{print $4}')="root" ]]; then
echo
echo "Part 1: Test of group membership being root supposedly passed"
echo "Actual group membership: " $(ls -l "${FILENAME}" | awk '{print $4}')
# The ownership test previously passed, and now the group membership test passed.
# Return success.
return 0
else
echo
echo "Part 2: Test of group membership being root supposedly failed"
echo "Actual group membership: " $(ls -l "${FILENAME}" | awk '{print $4}')
# The test of group membership being root did not pass. Return failure.
return 1
fi
# Should never be able to get here. Abort the script.
echo
echo "ABORTING SCRIPT; Failure point is designated \"Point 2\""
exit 1
}
# Show what ls is being invoked.
echo "Here is what ls will be used:"
type ls
# For this example, I'll just use ad hoc file test.txt to demonstrate the problem I'm having.
FILENAME=./test.txt
touch "${FILENAME}"
###
###
###
# Test the success case of the file ownership being root and the file group membership being root.
chown root "${FILENAME}"
chgrp root "${FILENAME}"
# Display the actual file ownership and group membership.
echo
echo "Test of success case starting; here's the file being tested:"
ls -l "${FILENAME}"
# Perform the check of ownership being "root" and group membership being "root".
if check_if_ownership_and_group_membership_are_root "${FILENAME}"; then
echo
echo "FINAL RESULT: SUCCESS"
else
echo
echo "FINAL RESULT: FAILURE"
fi
###
###
###
# Test the failure case of the file ownership not being root or the file group membership not being root.
chown nobody "${FILENAME}"
chgrp other "${FILENAME}"
# Display the actual file ownership and group membership.
echo
echo "Test of failure case starting; here's the file being tested:"
ls -l "${FILENAME}"
# Perform the check of ownership being "root" and group membership being "root".
if check_if_ownership_and_group_membership_are_root "${FILENAME}"; then
echo
echo "FINAL RESULT: SUCCESS"
else
echo
echo "FINAL RESULT: FAILURE"
fi
This results in the following output:
bash-3.2# ./script.sh
Here is what ls will be used:
ls is /usr/bin/ls
Test of success case starting; here's the file being tested:
-rw------- 1 root root 16 Aug 25 13:34 ./test.txt
Part 1: Test of ownership being root supposedly passed
Actual ownership: root
Part 1: Test of group membership being root supposedly passed
Actual group membership: root
FINAL RESULT: SUCCESS
Test of failure case starting; here's the file being tested:
-rw------- 1 nobody other 16 Aug 25 13:34 ./test.txt
Part 1: Test of ownership being root supposedly passed
Actual ownership: nobody
Part 1: Test of group membership being root supposedly passed
Actual group membership: other
FINAL RESULT: SUCCESS
As can be seen, the conditions I'm testing with the "if" statements always pass. What am I doing wrong?
(Even though I'm hoping someone can guide me away from parsing ls, I'd still like to know the answer to this question in any case for education's sake.)
Sincere thanks to all responders in advance.
You need spaces around the comparison operator =:
[[ $(ls -l "${FILENAME}" | awk '{print $4}') = "root" ]]
Also, you should use == instead of =, when you write the condition with double brackets [[ ... ]].
Generally relying on the output of ls is not recommended
if ! [[ $(stat --format '%G' "${FILENAME}") = "root" ]]; then
echo "do the thing"
fi
The Shellcheck wiki discusses some of the pitfalls of relying on the output of ls: https://github.com/koalaman/shellcheck/wiki/SC2012
Quote:
ls is only intended for human consumption: it has a loose,
non-standard format and may "clean up" filenames to make output easier
to read.

Shell script help

I need help with two scripts I'm trying to make as one. There are two different ways to detect if there are issues with a bad NFS mount. One is if there is an issue, doing a df will hang and the other is the df works but there is are other issues with the mount which a find (mount name) -type -d will catch.
I'm trying to combine the scripts to catch both issues to where it runs the find type -d and if there is an issue, return an error. If the second NFS issue occurs and the find hangs, kill the find command after 2 seconds; run the second part of the script and if the NFS issue is occurring, then return an error. If neither type of NFS issue is occurring then return an OK.
MOUNTS="egrep -v '(^#)' /etc/fstab | grep nfs | awk '{print $2}'"
MOUNT_EXCLUDE=()
if [[ -z "${NFSdir}" ]] ; then
echo "Please define a mount point to be checked"
exit 3
fi
if [[ ! -d "${NFSdir}" ]] ; then
echo "NFS CRITICAL: mount point ${NFSdir} status: stale"
exit 2
fi
cat > "/tmp/.nfs" << EOF
#!/bin/sh
cd \$1 || { exit 2; }
exit 0;
EOF
chmod +x /tmp/.nfs
for i in ${NFSdir}; do
CHECK="ps -ef | grep "/tmp/.nfs $i" | grep -v grep | wc -l"
if [ $CHECK -gt 0 ]; then
echo "NFS CRITICAL : Stale NFS mount point $i"
exit $STATE_CRITICAL;
else
echo "NFS OK : NFS mount point $i status: healthy"
exit $STATE_OK;
fi
done
The MOUNTS and MOUNT_EXCLUDE lines are immaterial to this script as shown.
You've not clearly identified where ${NFSdir} is being set.
The first part of the script assumes ${NFSdir} contains a single directory value; the second part (the loop) assumes it may contain several values. Maybe this doesn't matter since the loop unconditionally exits the script on the first iteration, but it isn't the clear, clean way to write it.
You create the script /tmp/.nfs but:
You don't execute it.
You don't delete it.
You don't allow for multiple concurrent executions of this script by making a per-process file name (such as /tmp/.nfs.$$).
It is not clear why you hide the script in the /tmp directory with the . prefix to the name. It probably isn't a good idea.
Use:
tmpcmd=${TMPDIR:-/tmp}/nfs.$$
trap "rm -f $tmpcmd; exit 1" 0 1 2 3 13 15
...rest of script - modified to use the generated script...
rm -f $tmpcmd
trap 0
This gives you the maximum chance of cleaning up the temporary script.
There is no df left in the script, whereas the question implies there should be one. You should also look into the timeout command (though commands hung because NFS is not responding are generally very difficult to kill).

Resources