I have a homework assignment: "for each unique user, report which group they are a member of and when they last logged in"
So far I have:
#!/bin/sh
echo "Your initial login:"
who | cut -d' ' -f1 | sort | uniq
echo "Now is logged:"
whoami
echo "Group ID:"
id -G $whoami
case $1 in
"-l") last -Fn 10 | tr -s " " ;;
*) last -Fn 10 | tr -s " " | egrep -v '(^reboot)|(^$)|(^wtmp a)|(^ftp)' | cut -d" " -f1,5,7 | sort -uM | uniq -c
esac
My question is: how I can show the each unique user? The script above only show the more recent user logged in the system, but I need all unique users.
Can anyone help?
Script using last
#!/bin/bash
while read user; do
echo "User '$user':"
echo -e "\t Last login: $(last -1R "$user" | awk 'NR==1{if($0 ~ /^$/){print "Never"}else{$1=$2="";print}}')"
echo -e "\t Groups: $(getent group | awk -F: -v user="$user" '$0 ~ user{a[i++]=$1} END{for(item in a)printf("%s ", a[item])}')"
done < <(getent passwd | awk -F: '{print $1}')
Output
Names have been changed to protect the innocent
User 'foo':
Last login: Oct 19 15:07:19 -0700 2010
Groups: foo groupA groupB
User 'bar':
Last login: Nov 16 11:40:23 -0800 2008
Groups: bar groupA groupC groupD
User 'baz':
Last login: Never
Groups: baz groupA groupD
The following link has some alternate ways of listing users. They involve reading /etc/passwd.
http://www.linuxquestions.org/linux/answers/Networking/How_to_list_all_your_USERs
There seems to be no other way than reading /etc/passwd, for example:
$ for u in `cat /etc/passwd | cut -d: -f1`; do echo $u `id -Gn $u`; done
To loop through any unique user you could get the content of the passwd file and get the first token of each line.
I'd suggest using getent passwd to read passwd, since /etc/passwd only contains users from local machine files (eg: no users from LDAP or other PAM plugins).
getent passwd | cut -d':' -f1
This command will return one user per line.
Then last and id will tell you their last login and their group:
for user in `getent passwd | cut -d':' -f1`
do
id ...
last ...
done
Related
I need to automatically create a user by reading lines of a file that contains username, home directory and full name.
I am new to bash shell scripting and it is very confusing to me.
There is something wrong with my adduser command. It gives the error - adduser: Only one or two names allowed.
following is the full script -
while read line;
do
fieldnumbers=$(echo $line | grep - o " " | wc - l)
username=$(echo $line | cut -d' ' -f 1)
home=$(echo $line | cut -d' ' -f 2)
firstname=$(echo $line | cut -d' ' -f 3)
if [[ "$fieldnumbers" -eq b4 ]]
then
middlename=""
else
middlename=$(echo $line | rev | cut -d' ' -f 2)
lastname=$(echo $line | rev | cut -d' ' -f 1)
password=$(echo pwgen 7 1) #create random password
fullname="$firstname $middlename $lastname"
echo "username is : $username"
sudo adduser --gecos $fullname --disabled-password --home $home $username
echo 'username:$password' | chpasswd
echo "Password is for $username is: $password"
done < users.txt
I am sure that this script is riddled with syntax errors. Please help. my brain is fried.
Always quote your variables unless you deliberately want to split the value into separate words.
sudo adduser --gecos "$fullname" --disabled-password --home "$home" "$username"
Also, you have to use double quotes around strings containing variables, not single quotes, if you want the variables to be expanded.
Difference between single and double quotes in Bash
So this line:
echo 'username:$password' | chpasswd
should be:
echo "username:$password" | chpasswd
eval: syntax error at line 1: `then' unexpected
Hi, I am having issues with this particular loop and couldn't find a solution to it, any ideas why?
getent passwd | cut -f1 -d":" | sort -n | uniq -c |\ while read x ; do [ -z "${x}" ] && break set - $x if [ $1 -gt 1 ]; then gids=`getent passwd |\ nawk -F: '($1 == n) { print $3 }' n=$2 | xargs` echo "Duplicate Group Name ($2): ${gids}" fi done
If you run the code through shellcheck and correct the errors which it shows (except for one problematic warning), the code will become:
getent passwd | cut -f1 -d":" | sort -n | uniq -c |
while read -r x ; do
[ -z "${x}" ] && break
set - $x
if [ "$1" -gt 1 ]; then
gids=$(getent passwd | nawk -F: '($1 == n) { print $3 }' n="$2" | xargs)
echo "Duplicate Group Name ($2): ${gids}"
fi
done
The code still seems to have issues, one of which is that it looks for duplicate user names but the print out claims that it found duplicate group names.
I would suggest replacing the above with:
getent passwd | awk -F: '{c[$1]++; uids[$1]=uids[$1]" "$3} END{for (name in c) if (c[name]>1) printf "Duplicate User Name (%s):%s\n",name, uids[name]}'
How the awk code works
In the output of getent passwd, the user name will be in field 1 and the user ID will be in field 3.
c[$1]++; uids[$1]=uids[$1]" "$3
This counts the number of times that user name $1 occurs and saves the count in array c. It also saves the user ID, $3, associated with each name in array uids.
END{for (name in c) if (c[name]>1) printf "Duplicate User Name (%s):%s\n",name, uids[name]}
After we have finished processing getent's output, this looks for user names for which the count is greater than 1 and prints the info.
Multiline version of suggested code
getent passwd | awk -F: '
{
c[$1]++
uids[$1] = uids[$1] " " $3
}
END{
for (name in c)
if (c[name]>1)
printf "Duplicate User Name (%s):%s\n", name, uids[name]
}'
I found the way to send message to currently logged in user by username:
who | grep username | cut -c1-20 | while read line; do printf "Message Text" | write $line ; done
However, is there anyway I can send message to currently logged in user by group?
Thanks for your help.
So... To send a message to every logged in user in a particular group, on every tty they're logged in to...
#!/bin/sh
usage() {
cat <<-EOT
Usage: writegroup groupname [message]
where [message] will be taken from stdin if not provided on the command line.
EOT
}
if [ $# -eq 0 ]; then
usage
exit 1
fi
groupname="$1"
shift
message="$*"
if [ -z "$message" ]; then
read message
fi
who | while read user tty junk; do
if groups "$user" | grep -wq "$groupname"; then
echo "$message" | write "$user" "$tty"
fi
done
Provided you set the variable group to the group you're interested in, you can try this:
for i in $(who -u | cut -d " " -f1 | sort | uniq); do if echo $(groups $i | cut -d " " -f3-) | grep $group >/dev/null; then echo "Message Text" | write $i; fi; done
$(who -u | cut -d " " -f1 | sort | uniq) gives the list of user logged in.
$(groups $i | cut -d " " -f3-) gives all groups a user belongs to.
The grep statement will reduce the list to the user belonging to the $group
I am trying to make a bash script that will calculate the time difference between the users' first logon and the users most recent logon. Any help is appreciated :)
This is what I have so far:
read -p "Enter a user ID: " ID
echo "You entered the following ID(s): $ID"
#/bin/egrep -i "^$ID" /etc/passwd
echo -n "The users real name is: "
/bin/grep "^$ID" /etc/passwd | cut -f5 -d :
echo -n "$ID's first login time is: "
l1=`last "$ID" | tail -n 1`
echo -n "$ID's last login time is: "
l2=`last "$ID" | head -n 1`
echo $l1
echo $l2
echo -n "The time difference is $(l1-l2) "
This is based off the assumption you want to provide a username and not an ID.
Firstly, you want to perform your captures correctly
l1=$(last "$ID" | tail -n 1)
l2=$(last "$ID" | head -n 1)
in my instance left
l1="wtmp begins Sun Nov 9 07:32:12 2014"
l2="graham pts/11 :0 Sat Nov 29 22:13 still logged in"
which is no good since we need only dates
So let's fix that. Here's some hacky parsing to get only times:
l1=$(last -RF | grep $ID | tail -n 1 | tr -s ' ' | cut -d ' ' -f 3,4,5,6)
l2=$(last -RF "$ID" | head -n 1 | tr -s ' ' | cut -d ' ' -f 3,4,5,6)
I grep for l1 because last leaves the last logged in, but for consistency, I just grab the last row. last -RF removes the host (-R), since we're not interested and makes the time a bit nicer (-F). tr trims all additional spaces and cut, delimited by a blank, grabs the date.
We want to compute the time between, so let's change both to datetime strings and subtract:
a=$(date -ud "$l2" +"%s")
b=$(date -ud "$l1" +"%s")
d=$(( a-b ))
Finally let's print
echo "$ID's last login time is: $l1"
echo "$ID's first login time is: $l2"
echo "The time difference is $d seconds"
Your script seems to contain a number of errors.
/bin/grep "^$ID" /etc/passwd | cut -f5 -d :
This matches the /etc/passwd file at the start (^) for the uid, AFAIK, the passwd file always starts with the username, not the uid.
Perhaps you are confused about what a user id means; in UNIX, a user id always refers to the numerical ID every user has; a username refers to the loginname that you type when you login.
In any case, using getent is a more reliable method; we could grep for :$uid:, but that might break if a group id is the same as a user id (among other scenarios). getent will also work with both a user id, and a user name.
Also, using /bin/grep is almost always a bad idea; lookups in $PATH are almost always better (so just use grep).
l1=`last "$ID" | tail -n 1`
last expects a username, not a user id; perhaps there is a flavour that also accepts a uid (?); in any case, using the username is more reliable.
echo -n "The time difference is $(l1-l2)"
The dates from last are in a string format (Sat Nov 1 00:39); you can't just subtract those as integers, you first need to parse them with date.
Here is what a working version might look like; I've also made some other (minor) improvements that could be useful for you:
#!/bin/sh
# Keep asking for the uid until *something* is entered
while :; do
read -p "Enter a user ID: " uid
[ -n "$uid" ] && break
done
# Get line from /etc/passwd
passwd=$(getent passwd "$uid")
# Exit code was non-zero; the uid is unknown
if [ $? -ne 0 ]; then
echo "User id '$uid' is unknown"
exit 1
fi
# Get data from passwd
username=$(echo "$passwd" | cut -d: -f1)
realname=$(echo "$passwd" | cut -d: -f5)
# Get info from last, strip last 2 lines since they're not useful for us. Use
# ISO format so that date can parse them
lastlog=$(last --time-format iso "$username" | head -n-2)
# Get first & last line; we only need the date
last_login=$(echo "$lastlog" | head -n1 | tr -s ' ' | cut -d ' ' -f 4)
first_login=$(echo "$lastlog" | tail -n1 | tr -s ' ' | cut -d ' ' -f 4)
# Parse dates with date, output time in seconds since 1-1-1970 ('epoch')
diff=$(( $(date --date "$last_login" +%s) - $(date --date "$first_login" +%s) ))
# Format the date
diff_fmt=$(date --date #$diff +'%d days %H hours %M minutes %S seconds')
# Output info
echo "Found user $username ($realname) for userid $uid"
echo "First recorded login: $first_login"
echo "Last recorded login: $last_login"
echo "Difference: $diff_fmt (total of $diff seconds)"
Unfortunately, this will only work on Linux systems; making work on all UNIX flavours required a bit more work (shell scripting is often difficult to do portable)
Example output:
[~]% sh test.sh
Enter a user ID: 1001
Found user martin (Martin Tournoij) for userid 1001
First recorded login: 2014-11-01T00:13:28+0100
Last recorded login: 2014-11-30T06:08:54+0100
Difference: 30 days 06 hours 55 minutes 26 seconds (total of 2526926 seconds)
I am trying to create a script that will use the /etc/passwd file to create a listing like this:
Full Name: Blah Blah User Account: bblah User ID: 5000 Last logged in: Sun Feb 21 18:13
Full Name: Mike Mike User Account: mmike User ID: 5001 Last logged in: Sun Feb 21 18:13
With my script, I get it to show
Full Name: Blah Blah User Account: bblah User ID: 1000
because I grep the 1000 accounts in my passwd file. As soon as I do the 5000 series, I get all the full names, then all the user accounts, and then all the user ids listed. How do I go about fixing this? How do I go about adding the "Last logged in" bit?
Is there a way to do this without using awk?
Script so far is:
#!/bin/bash
passfile=/etc/passwd
for i in $(grep 5000 ${passfile} | cut -d : -f 5)
do
account=$(grep 5000 /etc/passwd | cut -d : -f 3)
username=$(grep 5000 /etc/passwd | cut -d : -f 1)
echo Full Name:${i} Username: ${username} User ID: ${account} Last login: ${lastlogin)
done
Last logged in is done by last command:
last username
Use this:
#!/bin/bash
echo -en "BEGIN{\n}{\n system(var)\n}\n" > h.awk
while read in; \
do \
uname=`echo $in | sed -r 's/:/\ /g' | awk '{print $1}'`; \
p1=`echo $in | sed -r 's/:/\ /g' | awk '{print "name: "$5, "username: "$1, "id: "$3}'`; \
p2=`echo | awk -f ./h.awk var="last $uname| head -n 1"|awk '{print $5 $6}'`; \
echo $p1 last login: $p2; \
done < /etc/passwd
If you need more detail of last login to be prompted, use this one:
echo -en "BEGIN{\n}{\n system(var)\n}\n" > h.awk
while read in; \
do \
uname=`echo $in | sed -r 's/:/\ /g' | awk '{print $1}'`; \
p1=`echo $in | sed -r 's/:/\ /g' | awk '{print "name: "$5, "username: "$1, "id: "$3}'`; \
p2=`echo | awk -f ./h.awk var="last $uname| head -n 1"|awk '{print $4" "$5 $6" "$7}'`; \
echo $p1 last login: $p2; \
done < /etc/passwd