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)
Related
(if server shows time like 08:14 - then print server is up but if server shows date like Nov13 - print server is not up and send an alert message)
#!/bin/ksh
var1="$(ps -ef | grep 'abc' | grep -v 'grep' | tr -s " " | cut -d ' ' -f5)"
var2="$(date "+%H:%M")"
date="$(date "+%b%d")"
if [ "$var1" == "$var2" ]; then
echo "Server has been bounced" > final.txt
echo " Success"
elif expr "$var1" ">" "$var2" >/dev/null; then
echo "Server has been bounced" > final.txt
echo " Success" > final.txt
elif [ "$var1" == "$date" ]; then
echo "Server is not bounced" > final.txt
echo " Failure" > final.txt
mail -s " Failure, Server is not bounced " final.abc#acb.com
fi
I tried to take the output of var1 with this following command of the server 5th column to check server is bounced or has been up or not
var1=echo "ps -ef | grep 'abc' | grep -v 'grep' | tr -s " " | cut -d ' ' -f5"
var1 output will be either date or time depends on if the server is bounced or not, if bounced shows time if not then shows date
var2=echo "date "+%H:%M" "
var2 output will be today's time.
Keeping in mind that time can be different also not on real time it is checking so if var1 output shows 03:14 and current time is 05:16 then also need to show server is up which i am trying with this
else if expr "$var1" ">" "$var2" >/dev/null
and then this command
date=echo "date "+%b%d" "
date output will be just a format like (Nov15) if the var1 output will be in the format of date variable then have to show server is not bounced and not up
but if var1 output will show time like 02:12 then have to compare with format of var2 and show server is bounced and up
stuck with the script in middle time script is working but date one is not giving the output
With the following you could get the amount of seconds since midnight:
$(($(date '+(%H*60+%M)*60+%S')))
Using -o etimes in ps you can get the amount of seconds since the process is running.
If you know the PID, you can do: ps -p <pid> -o etimes=. If you do not know the PID, you can use grep as you were already doing:
ps -e -o etimes,command|grep abc|awk '{print $1}'
Once you have both values, you can compare if the process running time is greater than amount of seconds since midnight.
I have this script :
#!/bin/bash
ps -eo lstart,pid,cmd --sort lstart | while read line 2> /dev/null
do
if [ "$(date -d "${line::24}" "+%Y%m%d%H%M%S")" -gt "$(date -d "Thu Apr 7 00:55:38" "+%Y%m%d%H%M%S")" ] 2> /dev/null
then echo "Date : $(date -d "${line::24}" "+%d/%m/%Y %H:%M:%S") | PID & CMD : ${line:25:29}" >> process.log 2> /dev/null
fi 2> /dev/null
done
sort process.log | uniq > process.log
#sort process.log | uniq -u | tee process.log
My script runs automatically every 10 seconds, so I would like the identical lines to be deleted. As you can see, I tried with uniq but it doesn't work. I would like all lines in my file to be deleted if they are identical.
As I did, the second time the script is executed, there is nothing in the output file and I don't understand why.
I would also like nothing to be displayed in my terminal when the script runs. I used tee but when executing the uniq command, it returns an output in my terminal... How to remove it?
I thank you in advance for your help and wish you a good day
Thanks a lot
You should not parse ps output ever, especially not of lstart. Also, you are running date in the loop all the time, again and again.
I think something along this would be better to do:
some_date=$(date -d "Thu Apr 7 00:55:38" +%s)
now=$(date +%s)
how_much_time_ago=$(( now - some_date ))
ps -eo etimes,pid,cmd --sort etimes |
awk -v v="$how_much_time_ago" '$1 > v' |
while IFS=' ' read line etimes pid cmd; do
printf "Date : %s | PID & CMD : %s %s\n" \
"$(date -d "$((now - etimes))" "+%d/%m/%Y %H:%M:%S")" \
"$pid" "$cmd"
done |
sort |
uniq > process.log
Note that you can pipe the output of a while .... done | stuff loop to another thing normally. Instead of sprinkling 2>/dev/null everywhere, try to actually solve the issue, not hide the error.
We have a requirement to report on the number of lines written to a 7 day cycle of log files. The log files are called - [filename].log.1.gz for today, [filename].log.2.gz for yesterday up to [filename].log.7.gz for the 7th day
I was hoping to create a script that would output the numbers at once, instead of running the zcat [filename].log.1.gz | wc -l command against each line. I was also hoping to have a meaningful message against each outputted value
I can write a bash script that will do each line as the name of the files are the same, but I was hoping for something a bit more elegant
Instead of this
zcat position.log.3.gz | wc -l
zcat position.log.4.gz | wc -l
zcat position.log.5.gz | wc -l
zcat position.log.6.gz | wc -l
zcat position.log.7.gz | wc -l
I was hoping for something more like this
for i in {1..7}
c=$(zcat position.log.$i.gz | wc -l)
message=$"The count for "
date1=$(date --date='$i days ago')
result=$"$message$date1$c"
echo $result
done
However, I can't get this to run.
Any ideas?
You script with small fixes:
# for loop starts with a 'do'
for i in {1..7}; do
# indentation makes everything redable
# always quote your variables
c=$(zcat "position.log.$i.gz" | wc -l)
# The "" is just a string, no '$' sign before it
message="The count for "
# The '$i' is literally `$i`, if you want to _expand_ a variable, you have to use "$i"
date1=$(date --date="$i days ago")
# same as above, drop the $
result="$message$date1$c"
# always quote your variables
echo "$result"
done
Or maybe a tiny little bit shorter:
for i in {1..7}; do
echo "The count for $(date --date="$i days ago") is $(zcat "position.log.$i.gz" | wc -l)"
done
I want to add some users who are in this file like:
a b
c d
e f
firstname lastname always
#!/bin/bash
Lines=$(cat newusers.txt | wc -l)
first=$(cat newusers.txt | awk '{print $1}')
last=$(cat newusers.txt | awk '{print $2}')
#test
echo $Lines;
echo $first;
echo $last;
until [ -z $1]; then
useradd - m -d /home/$1 -c "$1 + $2" $1
fi
before loop it works fine but I can't add newline.
The echo shows a c e and second for lastname b d f.
I tried to add newline in but it doesn't works.
What can i use for this?
Because I guess I can't add the user because of the newline problem.
I also searched on stackoverflow to find out a way to check if the user already exists by /dev/null but which variable do i have to use for it?
It's easier to process the file line by line:
while read first last ; do
useradd -m -d /home/"$first" -c "$fist + $last" "$first"
done < newusers.txt
I do not understand what you mean to do by your code, but if you want to read the file line by line and get the values of different fields then you can use the following code snippet:
#!/bin/bash
filename="newusers.txt"
while read -r line
do
fn=$( echo "$line" |cut -d" " -f1 )
ln=$( echo "$line" |cut -d" " -f2 )
echo "$fn $ln"
done < "$filename"
Note: You cannot add users the way you want to using bash script; since you will be prompted for password which must be supplied using tty you can use expect to program it; or use system calls.
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