.bashrc code to execute 1 time a day upon first login - linux

Is there a way to make a specific piece of code in my .bashrc file execute only on the first log-in of a specific day of the week? I already know that using the command substitution
"$(date +%u)" will give me a number from 1-7 that corresponds to each day of the week (1 being Monday). However, i do not want this code to execute all day for every subsequent log-in. Any tips would be much appreciated. Thanks in advance!

You should not have to write anything to disk.
I would extract the day out of the commands:
lastlog -u $USER
and
date
Then do the appropriate matches/comparisons.
The logic would be something like:
get day from date
if day from date is the magic day, then
get day from from lastlog -u $USER
if day does not match today's day then
run your command

You can also use what is called 'semaphore file', something like this:
if [[ ! -e /tmp/$(date +%u).sem ]]
then
touch /tmp/$(date +%u).sem
# Do your one-time stuff
fi
However, which approach you choose, I would recommend you to use a full date (date +"%Y%m%d") to avoid potential bug if the user login on Monday, and his next login is in the next Monday.

date +%u | ## Generate timestamp (could be a better date-spec)
tee timestamp.tmp | ## Save a copy for later usage
cmp - timestamp || ## Fail if date-spec changed
{
## In that case, update timestamp
mv timestamp.tmp timestamp &&
## And only if that succeeds, run your code
echo "Once a day" ;
}
I prefer to touch the timestamp BEFORE running de command, because it is usually safer not to run anything at all than running it repeatedly. (The partition could have been remounted read-only, the disk might be full, permissions could have been changed...)

Related

Unable to output script results with column/table formatting

Answered - previously titled 'Cron job for shell script not running'
I recently downloaded Speedtest onto my Raspberry Pi, and wrote a script to output the results in csv format to a CSV file.
I'm trying to do this regularly via a cron job, but for some reason, it won't execute the shell script as intended.
Here's the script below. I've commented/cut out a lot to try and find the issue
#!/bin/bash
# Commented out if statement detects presence of data file and creates one if it doesn't exist. Was going to adjust later to include variables/input options if I wanted to used script on alternate systems, but commented out while working on main issue.
file='/home/User/Documents/speedtestdata.csv'
# have tried this with and without quotes, does not seem to make a difference either way
#HEADERS='/usr/bin/speedtest-cli --csv-header'
SPEEDTEST='/usr/bin/speedtest-cli --csv'
# Used absolute path for the executable
#LOG=/home/User/scripts/testreclog.txt
#DATE=$( date )
# Was using the above to log steps of script running successfully with timestamp, commented out
#if [ ! -f $file ]
#then
# echo "Creating results file">>$LOG
# touch $file
# $HEADERS > $file
#fi
#echo "Running speedtest">>$LOG
$SPEEDTEST >> $file
#echo "Formatting results">>$LOG
#column -s, -t < $file
# this step was used to format the log file neatly
#echo "Time completed ",$DATE>>$LOG
And here's how the crontab currently looks
# Edit this file to introduce tasks to be run by cron.
#
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
#
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
*/5 * * * * /bin/bash /home/User/scripts/testandrec.sh
# 2> /home/User/scripts/testrecerror.txt
# Was attempting to log errors to this file, nothing seen so commented out on a newline.
#* * * * * /home/User/scripts/testscript.sh test to verify cron works (it does)
I've added my scripts folder to the end of my path, but for some reason this only shows up when I'm using the Pi directly, when I ssh in I'm missing the scripts folder on the end.
However, given that I've used absolute path for everything I'm not sure why this would be an issue.
First I tested whether a simple Cron job would work, so I created testscript.sh, which simply returned 'Test' and a timestamp to a specific file and used the same shebang, and used the absolute paths, and functioned as intended.
I have checked systemctl for Cron, restarted Cron with sudo service cron restart and made sure a new line is in place in the crontab.
I have tried with and without /bin/bash in the cron tab entry, it seemingly hasn't made a difference.
I tried cd /home/User/scripts && ./testandrec.sh but no luck.
I changed the run time to every 5 then every 10 minutes, which has not worked.
I have noticed that when I ran the script manually with column -s, -t < $file left in, when cating the results file it is formatted as intended.
However, the next instance of when the cron job should run reverts this to CSV with a , as a delimitter, so clearly something is running.
To confuse matters further, I think the script may be firing once after restarting cron, and then not working when it should be running subsequently. When I leave the column line in, this appears to just revert the formatting, but if I comment it out it appears to run a speed test and append the results, but only once. However, I may be wrong on this and reproducing it
If I instead try 0 * * * * /usr/bin/speedtest-cli --csv >> /home/User/Documents/speedtestdata.csv && column -s, -t < /home/User/Documents/speedtestdata.csv, it appeared to perform/append speedtest but does not action the column command.
I would much rather neatly tie up the process in a shell script, however, rather than have the above which isn't very DRY code.
I've looked extensively, but none of the solutions I've found on this site or others have fixed the issue.
Any troubleshooting suggestions/help would be greatly appreciated.
Here you go - the solution is simple:
#!/bin/bash
# Commented out if statement detects presence of data file and creates one if it doesn't exist. Was going to adjust later to include variables/input options if I wanted to used script on alternate systems, but commented out while working on main issue.
file='/home/User/Documents/speedtestdata.csv'
# have tried this with and without quotes, does not seem to make a difference either way
#HEADERS='/usr/bin/speedtest-cli --csv-header'
SPEEDTEST='/usr/bin/speedtest-cli --csv'
# Used absolute path for the executable
#LOG=/home/User/scripts/testreclog.txt
#DATE=$( date )
# Was using the above to log steps of script running successfully with timestamp, commented out
#if [ ! -f $file ]
#then
# echo "Creating results file">>$LOG
# touch $file
# $HEADERS > $file
#fi
#echo "Running speedtest">>$LOG
$SPEEDTEST | column -s, -t >> $file
Just check the last line ;)

How to make a script to rename files

I have an actioncam that saves my video in a folder into the SD Card.
Using linux, here is the path:
/media/mattiapdo/EOS_DIGITAL/_REC/100MEDIA
Files are saved in the REC_0001.AVI format
I would write a script that renames each file using the writing date.
Furthermore I notice that for some strange reason, the date and the hour are different from the effective: for example, 12/07/2017 10:30 is written as 09/02/2011 07:55
As the camera is very old and minimal, I can't reset the correct date and the correct hour so I would prefer to manipulate them in aftermath.
The goal would be to rename REC_0001.AVI in 2017_07_12__10_30.AVI
Does anyone have any ideas?
You can use the date command to print the elapsed seconds since, Unix lingo, the Epoch, aka 1970-01-01 UTC. Assuming that the camera date is in Anglo format, and that by default date likes the Anglo format, you have to swap the month and day in your date
$ date --date='09/02/2011 07:55' +%s
1314942900
$ date --date='07/12/2017 10:30' +%s
1499848200
$
so that you can compute a Delta between the real date and the camera idea of time
$ Delta=$(($(date --date='07/12/2017 10:30' +%s)-$(date --date='02/09/2011 07:55' +%s)))
$ echo $Delta
184905300
$
You haven't (yet?) told us how you fetch the date from the camera, but let's
say that
$ camera=$(fetch_date $current_file_name)
and assuming that $camera is in a format that date likes,
$ fromEpoch=$(($(date --date="$camera" +%s)+$Delta))
the last step is to get back the date in a format that you like , I suggest
the ISO 8601 format, so that your files are correctly sorted by ls
$ corrected_date=$(date --date="#$fromEpoch" +%Y-%m-%dT%H:%M)
$ cp $current_file_name other_directory/$corrected_date.AVI
The boring details about the date command, that is indeed VERY flexible and useful, are available using
$ man date
I hope that you can write your script with the info that I gave you, thank you for the question.
Addendum
Caveat emptor: totally untested
$ cat script
Delta=$(($(date --date='07/12/2017 10:30' +%s)-$(date --date='02/09/2011 07:55' +%s)))
mkdir -p ATTIC
mv *AVI ATTIC
for file in ./ATTIC/*.AVI ; do
########## fetch_date command is a placeholder for the real command
cam_date=$(fetch_date "$file")
cam_fromEpoch=$(date --date="$cam_date" +%s)
correct_fromEpoch=$(($cam_fromEpoch+$Delta))
ISO_8601=$(date --date="#$correct_fromEpoch" +%Y-%m-%dT%H:%M)
cp $file $ISO_8601.AVI
done
# cleanup, e.g. list current directory and ATTIC and ask if ATTIC is to be removed
$

rsync only files younger than xy days

I am using rsync to copy photos form our satellite servers into main server. So the script doing it is basically connecting from PC to PC and executing rsync.
I have been trying to use find to determine files younger than xy days (it will be days, but number can vary). Specifing the files with --files-from=<() BUT the command find /var/dav/davserver/ -mtime -3 -type f -exec basename {} \; is on some machines very very slow, and even makes rsync to timeout. Also they are servers, so running this command every few minutes would cost too much processor power that I don't want to take away.
The second aproach was to take advantage of the way we are storing those files, under /var/dav/davserver/year/month/day/ directory. However as I started to work on it, I have realized that I need to write quite a some code do take care of end of months and years, even more that number of days is not fixed (it can be more than 31 days, thus this scrip could need to run through several months).
So I was wondering if there is not some easier way how to achieve this without killing source PCs processor or write a whole new library to take care of all month/year ends?
EDIT:
I have prepared script that generates paths to files for me. What I did, is that I left handling end of months/year for date..
#!/bin/bash
DATE_now=`date +"%Y-%m-%d"`
DATE_end=`date -d "-$1 days" +"%Y-%m-%d"`
echo "Date now: $DATE_now | Date end: $DATE_end"
start_d=`date +%s`
end_d=`date -d "-$1 days" +%s`
synced_day=$DATE_now
synced_day_s=$start_d
daycount=1
echo "" > /tmp/$2_paths
while [ $synced_day_s -ge $end_d ]; do
DAY=$(date -d "$synced_day" '+%d')
MONTH=$(date -d "$synced_day" '+%m')
YEAR=$(date -d "$synced_day" '+%Y')
SYNC_DIR="/var/dav/davserver/$YEAR/$MONTH/$DAY/**"
echo "Adding day ($synced_day) directory: \"$SYNC_DIR\" to synced paths | Day: $daycount"
echo $SYNC_DIR >> /tmp/$2_paths
synced_day=$(date -d "$synced_day -1 days" +"%Y-%m-%d")
synced_day_s=$(date -d "$synced_day" +%s)
daycount=$((daycount+1))
done
and counting down days using it, than just extract needed info. This script gives me a list of directories to rsync:
rrr#rRr-kali:~/bash_devel$ bash date_extract.sh 8 Z00163
Date now: 2017-06-29 | Date end: 2017-06-21
Adding day (2017-06-29) directory: "/var/dav/davserver/2017/06/29/**" to synced paths | Day: 1
Adding day (2017-06-28) directory: "/var/dav/davserver/2017/06/28/**" to synced paths | Day: 2
Adding day (2017-06-27) directory: "/var/dav/davserver/2017/06/27/**" to synced paths | Day: 3
Adding day (2017-06-26) directory: "/var/dav/davserver/2017/06/26/**" to synced paths | Day: 4
Adding day (2017-06-25) directory: "/var/dav/davserver/2017/06/25/**" to synced paths | Day: 5
Adding day (2017-06-24) directory: "/var/dav/davserver/2017/06/24/**" to synced paths | Day: 6
Adding day (2017-06-23) directory: "/var/dav/davserver/2017/06/23/**" to synced paths | Day: 7
Adding day (2017-06-22) directory: "/var/dav/davserver/2017/06/22/**" to synced paths | Day: 8
rrr#rRr-kali:~/bash_devel$ cat /tmp/Z00163_paths
/var/dav/davserver/2017/06/29/**
/var/dav/davserver/2017/06/28/**
/var/dav/davserver/2017/06/27/**
/var/dav/davserver/2017/06/26/**
/var/dav/davserver/2017/06/25/**
/var/dav/davserver/2017/06/24/**
/var/dav/davserver/2017/06/23/**
/var/dav/davserver/2017/06/22/**
rrr#rRr-kali:~/bash_devel$
However, now is my problem to use this list, I have been trying to use many combination of --include and --exclude commands with both --include-files and --include-from BUT I am getting only 2 results: either everything is being rsynced, or nothing.
Since you already have files ordered by date (in directories), it's easy and efficient to just rsync those directories:
#!/bin/bash
maxage="45" # in days, from today
for ((d=0; d<=maxage; d++)); do
dir="/var/dav/davserver/$(date -d "-$d day" +"%Y/%m/%d")"
rsync -avrz server:"$dir" localdir
done
We're using date to calculate today - x days and iterate over all days from 0 to your maxage.
Edit: use arithmetic for loop instead of iterating over GNU seq range.
So, I have solved it with combination of:
Script generating paths according to actual date. Details are presented in my edit of initial post. It simply uses date to go through previous days and manage month and year ends. And generate paths from those dates. However radomir's solution is simplier, so I will use it. (Its basically same as I did, just simplier way to write it down).
than I have used combination of --include-files=/tmp/files_list and -r a.k.a. --recursive argument to use this list of paths properly.
(It was copying only empty directories without -r. And everything or nothing if I have used --include-from instead of --include-files)
Final rsync command is:
rsync --timeout=300 -Sazrv --force --delete --numeric-ids --files-from=/tmp/date_paths app_core#172.23.160.1:/var/dav/davserver/ /data/snapshots/
However this solution is not deleting old files on my side, despite --delete argument. Will probably need to make an extra script for it.

Find Difference in timestamps in seconds

I have 2 files with timestamps in the format of MMDDYYYY-HHMMSS.
For eg. 04192012-000623 and 04192012-000854.
I need to be able to find the difference between the 2 in seconds.
Special cases to check for
the dates straddling midnight. For eg: 04172012-115500 & 04182012-000200.
it shouldn't matter which file comes in first, etc.
I am running ksh with no access to the date -d flag. Can anyone point me in the right direction on how to shell script this? (It is going to be a part of a larger shell script so no other languages please)
This is intended to be run on both solaris and linux ksh. Thanks in advance.
As a starter (from my provided link above):
#! /usr/bin/ksh
echo enter first time stamp
read TIME1
echo enter second time stamp
read TIME2
H1=${TIME1%:+([0-9])}
M1=${TIME1#+([0-9]):}
H2=${TIME2%:+([0-9])}
M2=${TIME2#+([0-9]):}
H1=${H1#0}
M1=${M1#0}
H2=${H2#0}
M2=${M2#0}
((MAM1=H1*60+M1))
((MAM2=H2*60+M2))
((MAM1>MAM2)) && ((MAM2=MAM2+1440))
((diff=MAM2-MAM1))
echo diff = $diff
exit 0
$ ./timestamp
enter first time stamp
17:30
enter second time stamp
18:05
diff = 35
$ ./timestamp
enter first time stamp
23:59
enter second time stamp
00:01
diff = 2
$
if this is just for fun, good luck :). if it's for actual practical purposes, see my strptime wrapper at How to get the difference between now and a different date (in minutes) using ksh (or another shell script)?, it should be easily adaptable.
This solution uses gnu-date and bashisms, since I don't know ksh.
d1=04192012-000623
dd1="${d1:4:4}/${d1:0:2}/${d1:2:2} ${d1:9:2}:${d1:11:2}:${d1:13:2}"
d1=04192012-000854
dd2="${d1:4:4}/${d1:0:2}/${d1:2:2} ${d1:9:2}:${d1:11:2}:${d1:13:2}"
echo $(($(date -d "$dd1" +%s) - $(date -d "$dd2" +%s)))
Maybe ${var:from:len} is not available in ksh to cut parts from strings, then you have to replace it with something else, maybe sed.
stat -c followed by one of the following
%X time of last access, seconds since Epoch
%Y time of last data modification, seconds since Epoch
%Z time of last status change, seconds since Epoch

How to exclude a certain day of the week in cron, and only on a certain week

Basically, I want to run a job on the first, second, fourth, and if available, fifth friday of each month. Or in other words, all Fridays except for the third Friday.
Edit: this is more java oriented, there is no access to a bash environment.
You can manually check for it in a script:
date "%u"
gives you the day of the week (same as the dow field in cron)
To get the week number of each month, you have to do something funky.
date +"%V"
gives you the week of the month, so you'd have to look at
x=`date +"%V"`
y=`date +"%V" -d $(date +"%Y%m01")`
week_of_month=$((x-y))
week_of_month starts at 0 so you would want to check if it is not two.
So in your cron, you would run the script every friday, and put a check at the top of the script:
x=`date +"%V"`
y=`date +"%V" -d $(date +"%Y%m01")`
week_of_month=$((x-y))
if [ "$week_of_month" == "2" ]; then
echo "Will not run second friday of the month"
exit;
fi

Resources