Take user name as input: if user is logged in, display running processes belonging to the user

12,366

Solution 1

You can use the who -u command to list the users who are logged in. Then you could use grep to look for a specific user in the output, and exit with success if found, or exit with failure otherwise.

#!/bin/bash

read -p 'Enter username to check: ' user
if who -u | grep -q "^$user "; then
    top -u "$user"
else
    echo "User $user is not logged in"
fi

(Thanks to @dessert for shortening the who -u | ... part!)

Solution 2

To me it looks like you have misunderstood the task a little. I guess the script is something a sysadmin would use to check on users, so "Who are you?", and checking that the user running the script has entered their own username (which is what your script tries to do) is not what you are supposed to do!

I think janos' answer and dessert's answer do exactly what you want, and I don't have a better answer. But here are some tips to debug your script...

test aka [

You used [ which is a shell builtin. You can see some information about it by running help [, but as that info tells you, it is a version of the test command, and more information can be found under help test. When I run your script (which I named script) I get these errors:

script: line 6: if[zanna == zanna]: command not found
script: line 7: syntax error near unexpected token `then'
script: line 7: `then'

you needed three spaces to separate the if and [ commands from their arguments. Also, as Sergiy pointed out in a comment, until Bash version 4.4, the == operator did not exist in test or [. Ubuntu 16.04 is using Bash 4.3. I have Bash 4.4 in 17.10. You can correct the syntax error like this:

if [ "$user" = "$name" ]

You could also have avoided assigning an extra variable by making that test

if [ "$user" = "$(whoami)" ]

but brevity does not always yield clarity, especially in shell scripts, and besides, this isn't what you really want to do, because the user running the script has to be logged in to run a script, so even the syntactically correct version of your script would only be checking that the user typed their username correctly, which isn't what the task asks for.

read

read is a Bash builtin command. You can get information on it by running help read.

When you use the read command, you probably want to use the -r option to prevent any backslashes in the input from acting as escapes.

Also, rather than echoing the user prompt, you can use read's -p option and write the prompt after -p for a more concise script.

echo "Who are we checking on? "
read -r user

does the same as

read -rp "Who are we checking on? " user

You also don't strictly need to name the variable, since read puts the input into a variable REPLY automatically. You can test this:

read -rp "Who are we checking on? "
echo "$REPLY"

Of course, you often want to name the variable. I think user was a good choice for this one.

if

if is also a shell builtin, so you can find out about it by running help if. You'll see that it checks the command after it and executes the then branch if the exit status of that command was zero (ie it was successful). janos' answer uses if without test or [ by simply collecting the exit status of grep:

who -u | grep -q "^$user "

searches the output of who -u to see if any line starts with the value of the $user variable. ^ is start of line, and the -q option suppresses output. grep's exit status is zero if it finds a match, so if $user is found, the then branch of the if structure will be executed, but if grep fails to find anything, the else branch of the if structure will be executed (if it exists).

dessert's answer uses only the shell's logic to define actions for success and failure of a command - even if is not necessary here :)

test again

Since checking exit status suffices, I don't think you need the [ or test command at all. If you really want to use it, you could use something like

[ -n "$(w -h | grep "^$user ")" ]

The whole command substitution must be quoted or [ will complain of too many arguments if there are multiple words in the output (since $user will be one word, we could also use grep -o to output only the match). -n is true if the string is not empty.

An alternative to who -u is w -h. I'm not sure which is more portable or reliable. I've noticed that who gives slightly different output in different terminal emulators, although not different enough to stop this script from working.

Quoting

Most of the time, you want to quote variables in scripts to prevent further expansions. The utilities you use to create users on Ubuntu will not allow you to create a username with spaces or any other characters that could cause the shell to perform an expansion, so I don't think it's strictly essential to quote the variable in this script. Still, it's possible for such usernames to exist, and it a good habit to quote variables that don't need to be unquoted.


For completeness, here is my version without [ (it's essentially the same as janos'):

#!/bin/bash

read -rp "Who are we checking on? "
if w -h | grep -q "^$REPLY "; then
    echo "here's what "$REPLY" is doing:"
    top -u "$REPLY"
else
    echo "looks like "$REPLY" is not logged in."
fi

Solution 3

What you're asking for can actually be done with a oneliner:

read -p"Who are you? " u&&((who -u|grep -q "^$u "&&top -u"$u")||echo "$u is not logged in")

But as you requested a script, here's the much nicer script version:

#!/bin/bash
read -p "Who are you? " user && ((    # prompt for username, save as $user
who -u | grep -q "^$user "   &&       # test if user is logged in
top -u "$user"                   ) || # run top with that username
echo "$user is not logged in"   )     # error message

Explanations

  • read -p "Who are you? " user – prompt for the username and save it as variable user
  • && – if that's successful, …
  • (…) – start a subshell and do in it – if you don't want to start a subshell you can use { …;} instead, the space as well as the semicolon are mandatory
  • who -u | grep -q "^$user " – call who and grep for the username at the beginning of the line (^) with a preceding space (so that des doesn't match dessert)
  • top -u "$user" – run top with the username
  • || – if that's not successful, i.e. if the user is not logged in
  • echo "$u is not logged in" – print error message

NB: This can very well also be done with an if expression as shown in the other answers. I stuck to logical operators and brackets here because that's another way of doing it and I wanted to show how.

Share:
12,366

Related videos on Youtube

rumana khanom
Author by

rumana khanom

Updated on September 18, 2022

Comments

  • rumana khanom
    rumana khanom over 1 year

    I want to write a script that takes the name of a user as command line input. If that user is logged in then it will display the processes the user is running. If he/she is not logged in, then the script should mention this.

    My attempt is not working:

    #!/bin/bash
    echo "Who are you?"
    read user
    echo $user
    name=$(whoami)
    if[$user == $name]
    then
      top -u $user
    else
      echo "not logged in"
    fi
    
    • goo
      goo over 6 years
      Run your script with bash -x, "who the user is" is already provided in the $USER environment variable.
    • dessert
      dessert over 6 years
      You are checking whether the user is the same as the one who runs the script, not whether he is logged in. How about top -u $user || echo "$user is not logged in"? This would save you the whole if expression…
    • dessert
      dessert over 6 years
      By the way: Always quote variables with input you got from a user!
  • janos
    janos over 6 years
    @Zanna I meant to delete that line from the original script, and keep only the read -p I added. Thanks for spotting it!
  • Sergiy Kolodyazhnyy
    Sergiy Kolodyazhnyy over 6 years
    Couple things: test doesn't have == operator, that's only in [[ or arithmetic compound command ((...)), and arithmetic expansion $((...)). Be careful with grep "^$user" as this doesn't account for usernames like jdoe and jdoe1, so that may lead to flags. Good answer, I like it.
  • Zanna
    Zanna over 6 years
    @SergiyKolodyazhnyy I've found out why it worked for me - the == operator was added to test in Bash 4.4 (which I have on 17.10)!
  • Sergiy Kolodyazhnyy
    Sergiy Kolodyazhnyy over 6 years
    Good find ! This also means one more incompatibility between /bin/sh and /bin/bash. I'm gonna have to go add that on Ubuntu wiki
  • dessert
    dessert over 6 years
  • Sergiy Kolodyazhnyy
    Sergiy Kolodyazhnyy over 6 years
    @dessert Yep, and that's one of the reasons it could break commands if shell decides to ... oh, maybe do filename expansion on username ... See askubuntu.com/a/983961/295286
  • Zanna
    Zanna over 6 years
    oh, alright then, edited slightly :P