Sourcing date hour in Bash - bash

I need to write a Bash script that source another script (config script) for hours. If the hour mentioned in config script matches the Linux past hour it needs to print the hour.
$ cat ConfHours.sh
#!/bin/bash --
Hours=(0 1 2 22 23)
$ cat Foo.sh
#!/bin/bash --
source /home/Geo/ConfHours.sh
arrayHours=( ${HOURS} )
for v in "${arrayHours[#]}"
do
HOUR=$(( $(date +%H) -1))
if [ "${HOUR}" == "v" ] ; then
HOUR = ${HOUR}
echo $HOUR
fi
done
When I run Foo.sh, I do not get anything. Could you please correct me where I am wrong?

Some errors:
source /home/Geo/ConfHours.sh
arrayHours=( ${HOURS} )
ConfHours defines a variable named Hours -- different variable
for v in "${arrayHours[#]}"
do
HOUR=$(date -d "1 hour ago" +%H)
You don't need to define this every time through the loop: put it before the for statement
if [ "${HOUR}" == "v" ] ; then
missing $ for the v variable
$HOUR will contain a leading 0 (due to %H)
a better test: if (( 10#$HOUR == 10#$v ))
HOUR = ${HOUR}
No spaces around the = allowed for variable assignment. Why are you trying to redefine the variable to itself?
echo $HOUR
fi
done
A more concise way to test an array contains a value is to take advantage of array string concatenation and pattern matching:
source ConfHours.sh
hour=$(date +%k) # leading space, not leading zero
if [[ " ${Hours[*]} " == *" ${hour# } "* ]]; then
echo "$hour"
fi
All spaces and quotes are required.
Don't use UPPER_CASE_VARS: here's why

Related

Problems with "while" bash

I need to control files in a folder... The script has to wait the file until it exists...
These files have the name... The format is file_d01_YYYY-MM-DD_HH:00:00. For example:
file_d01_2018-11-12_00:00:00
file_d01_2018-11-12_01:00:00
And so on, for 7 days ahead.
!/bin/bash
ZZ=`date +%k`
date=$(date +%Y%m%d)
if [[ $ZZ -ge 2 ]] && [[ $ZZ -lt 14 ]] ; then #03:45 UTC
ZZ=00
PARAM=`date +%Y%m%d`$ZZ
PARAM2=`date +%Y-%m-%d`
elif [[ $ZZ -ge 14 ]] && [[ $ZZ -lt 23 ]] ; then #15:45 UTC
ZZ=12
PARAM=`date +%Y%m%d`$ZZ
PARAM2=`date +%Y-%m-%d`
fi
rundir=/home/$PARAM/wrfv3
dir=/home/$PARAM
data=$(date +%Y-%m-%d)
data1=$(date -d "1 day" +%Y-%m-%d)
data2=$(date -d "2 day" +%Y-%m-%d)
data3=$(date -d "3 day" +%Y-%m-%d)
data4=$(date -d "4 day" +%Y-%m-%d)
data5=$(date -d "5 day" +%Y-%m-%d)
data6=$(date -d "6 day" +%Y-%m-%d)
days=( "$data" "$data1" "$data2" "$data3" "$data4" "$data5" "$data6" ) #array of days
values=( {00..23} ) #array of 24 hours
echo ${#values[#]}
# Here, using to loops, I check if files exist...for every day and hour
for day in "${days[#]}"; do
for value in "${values[#]}"; do
echo file_d01_${day}_${value}:00:00
while [ ! -f $rundir/file_d01_2018-11-15_20:00:00 ] # while file doesn't exist...wait...and repeat checking till it exists...
do
echo "waiting for the file"
sleep 10
done
echo "file exists"
sleep 5
done
done
I receive always "waiting for the file"...and they exist... where is the problema in the code?
You should add the double quotes "" to protect the path. It's a good practice. Also bash expansion escapes the : character, so maybe it is an issue in your context (not in the one i did the test).
while [ ! -e "$rundir/file_d01_2018-11-15_20:00:00" ]
I would suggest to follow those steps:
Protect the path with double quotes "" (not simple ones, otherwise $rundir won't be expanded)
Write echo "waiting for the file $rundir/file_d01_2018-11-15_20:00:00" to see what path you're testing
Additionally, use -e to see any changes (-e checks for a path existence, not only a regular file one)
Note: the brackets [ ] invokes in fact test. So, man test will give you the operators you can use and their meanings. Also nowadays bash has double brackets [[ ]] as built-in operators, more powerful, which can be used instead.
The code in the question contains:
echo file_d01_${day}_${value}:00:00
while [ ! -f $rundir/file_d01_2018-11-15_20:00:00 ]
It's calculating a file name, echoing it, and then checking for a (probably different) fixed, unchanging, file name. It should check for the calculated file name. For example:
file=file_d01_${day}_${value}:00:00
echo "$file"
while [ ! -f "$rundir/$file" ]
To make debugging easier, it would be better to have:
filepath=$rundir/file_d01_${day}_${value}:00:00
echo "$filepath"
while [ ! -f "$filepath" ]
The full code in the question has many issues (starting with a broken shebang line). It's a good idea to make Bash code Shellcheck-clean.

Day of month not an integer

I am making an adjustment a bash script of mine to output the ordinal part (st, nd, rd, th) of a day of month. I am trying to adjust it to be used within a date pattern in the date command.
Here is a simple test to determine if day of month from date command is an int:
dayOfMonth=$(date +"%d") && [[ ${dayOfMonth} =~ ^[0-9]+$ ]] && echo ${dayOfMonth} is an int || echo ${dayOfMonth} is NOT an int
Output is as I expect
01 is an int
Now I put that code into a script:
#!/bin/bash
[[ ${1} =~ ^[0-9]+$ ]] && echo ${1} is an int || echo ${1} is NOT an int
Seems OK:
dateTest.sh 12
12 is an int
But when I try to use it within a date command, the script is giving me conflicting output. echo sees 12 but the test is seeing %2
date --date='06/12/2012 07:21:22' +"`dateTest.sh %d`"
12 is NOT an int
To clarify my purpose, the end result of the script will be something like this:
#!/bin/bash
n=$(date +"%d")
if [ $# -ge 1 ] ; then
n=$1
if ! [[ $n =~ ^[0-9]+$ ]] ; then
echo Arg dateInteger [$dateInteger] must be an integer.
exit 1
fi
fi
if [ $n -ge 11 -a $n -le 13 ] ; then
echo "th"
else
case $(( $n%10 )) in
1)
echo st
;;
2)
echo nd
;;
3)
echo rd
;;
*)
echo th
;;
esac
fi
So that I can it like this:
date --date='06/12/2012 07:21:22' +"%A, %d`dateTest.sh %d` of %B %Y, %I:%M:%S %p"
which would output
Tuesday, 12th of June 2012, 07:21:22 AM
Finished script re-write. Thanks to input from #GordonDavisson, I completed my script re-write: http://pastebin.com/xZ1afqqC. Now it either outputs just the ordinal from an integer, or will output a fully formatted date where you can use standard format strings from date with the addition of "%O" for the ordinal.
You're doing things in the wrong order -- you have to turn "%d" into an integer (the day of the month) before passing it to your script, not after. Consider the command:
date --date='06/12/2012 07:21:22' +"`dateTest.sh %d`"
What this does is run dateTest.sh %d, i.e. it passes "%d" as the argument to your script. The script naturally outputs "%d is not an int". This is then used as the format string for the date command, i.e. date --date='06/12/2012 07:21:22' +"%d is not an int". The date command replaces the "%d" part with the day number, and leaves the rest alone, giving "12 is not an int".
In order to make this work, you have to get the day number first, then pass that to your script. Something like this:
dateTest.sh "$(date --date='06/12/2012 07:21:22' +"%d")"
Unfortunately, your end result script also wants a bunch of other date formatting done that can't be passed to the dateTest script. I think in that case it'd be best to do it in stages:
dayWithSuffix="$(dateTest.sh "$(date --date='06/12/2012 07:21:22' +"%d")")"
date --date='06/12/2012 07:21:22' +"%A, $dayWithSuffix of %B %Y, %I:%M:%S %p"
BTW, several general scripting suggestions:
Send error/debug output to stderr, not stdout, so it doesn't get confused with the script's regular output (part of the problem here). For example, echo "${1} is NOT an int" >&2
Speaking of which, put strings that contain variables in double-quotes (as I did in that last example) to avoid weird misparsing of whitespace, wildcards, etc. Your result script, for example, contains echo Arg dateInteger [$dateInteger] must be an integer. -- you probably don't realize that under certain circumstances the [$dateInteger] part will be replaced by a list of filenames.
Finally, use $( ... ) instead of backquotes. In most cases they're equivalent, but the parenthesis version is easier to read and avoids some weird parsing oddities of the contents. Notice how I nested two such expressions in the assignment to dayWithSuffix? That's much trickier to get right with backquotes.

Stock date in a string then split it for tests

I have some operations to do on files last modified on a specific date. I would like to get the date, stock it in a string, then split it to test if the day corresponds to what I want.
So far, I've been trying things like that:
#!/bin/bash
for i in {45..236}; do
nom=M$i
chem=/Users/nfs/helene/soft/metAMOS-1.5rc3/$nom.fastq/Assemble/out
if [ -e $chem ]; then
IN= $(date -r $chem)
arr=(${IN//\ / })
if [[ ${arr[1]} == 'juin' && ${arr[2]} == '10' ]]; then
echo $nom
#cp $chem/proba.faa /Users/nfs/helene/metagenomes/DB/$nom.faa
fi
fi
done
exit 0
But it seems like the date isn't well stocked in $IN, and I'm not sure about the space-spliting either..
Perhaps the simple mistake is that you didn't place your assignment adjacent to =. It must be:
IN=$(date -r $chem)
And here's a simplified suggestion:
#!/bin/bash
for i in {45..236}; do
nom="M${i}"
chem="/Users/nfs/helene/soft/metAMOS-1.5rc3/${nom}.fastq/Assemble/out"
if [[ -e $chem ]]; then
read month day < <(exec date -r "$chem" '+%b %d')
if [[ $month == 'Jun' && $day == 10 ]]; then
echo "$nom"
# cp "$chem/proba.faa" "/Users/nfs/helene/metagenomes/DB/$nom.faa"
fi
fi
done
exit 0
* See date --help for a list of formats.
* <() is a form of Process Substitution. Check Bash's manual for it.
* Always place your arguments around double quotes when they have variables to avoid word splitting.

Get the substring from a filename and compare to Hour (HH) of current date/timestamp using Shell Script

Say I have a filename ABC.20131212.XX.xml where XX is HH (hour). I need to get the value of XX and compare it to the current hour of the system time. If it's equal, then, i'll rename that file. So if it's 12pm (12:00), I should get the file with ABC.20131212.12.xml and rename it. How can I achieve the comparison in shell script?
Here's how to get started in pure bash without starting external commands:
a=ABC.20131212.XX.xml
# Strip off .xml
b=${a%.xml}
# Extract last 2 characters
xx=${b: -2}
and the hour can be got with
time=`date +'%H'`
Comparison and rename can be done like this
if [[ $time -eq $xx ]]
then
mv something somewhere
fi
Here's how you can do it using Bash built-in regex matching:
file=foobar.11.xml
if [[ $file =~ .*([0-9][0-9])\.xml ]]; then
if [[ "${BASH_REMATCH[1]}" -eq $(date "+%I") ]]; then
# $file has current hour in its name
fi
fi
Use the +%H option with date for comparing to a 24-hour time system.
Here is the Script you can use(Change path accordingly):
`
!/bin/bash
Path=/Your/Path/Here/*
for l in $Path
do
a=$l
b=${a%.xml}
xx=${b: -2}
time=date +"%I"
if [[ $time -eq $xx ]]
then
mv $l /New/file/name/path/newfile.txt
fi
done
exit 0`

bash loop between two given dates

I'm trying to create a script that will loop through files that have their filenames written in the following format: yyyymmdd.hh.filename.
The script is called with:
./loopscript.sh 20091026.00 23
./loopscript.sh 20091026.11 15
./loopscript.sh 20091026.09 20091027.17
The need is for the script to check each hour between those two given dates/hours.
e.g.
cat 20091026.00.filename |more
cat 20091026.01.filename |more
...
cat 20091026.23.filename |more
cat 20091027.01.filename |more
cat 20091027.02.filename |more
...
and so on.
any idea how to go about this? I don't have any difficulty with standard 0 - x loops. or simple for loops. Just not sure how to go about the above.
How about this:
#!/bin/bash
date1=$1
date2=$2
#verify dates
if ! date -d "$date1" 2>&1 > /dev/null ;
then echo "first date is invalid" ; exit 1
fi
if ! date -d "$date2" 2>&1 > /dev/null ;
then echo "second date is invalid" ; exit 1
fi
#set current and end date
current=$(date -d "$date1")
end=$(date -d "$date2 +1 hours")
#loop over all dates
while [ "$end" != "$current" ]
do
file=$(date -d "$current" +%Y%m%d.%H)
cat $file."filename" | more
current=$(date -d "$current +1 hours")
done
To process each file between two given date/hours, you can use the following:
#!/usr/bin/bash
#set -x
usage() {
echo 'Usage: loopscript.sh <from> <to>'
echo ' <from> MUST be yyyymmdd.hh or empty, meaning 00000000.00'
echo ' <to> can be shorter and is affected by <from>'
echo ' e.g., 20091026.00 27.01 becomes'
echo ' 20091026.00 20091027.01'
echo ' If empty, it is set to 99999999.99'
echo 'Arguments were:'
echo " '${from}'"
echo " '${to}'"
}
# Check parameters.
from="00000000.00"
to="99999999.99"
if [[ ! -z "$1" ]] ; then
from=$1
fi
if [[ ! -z "$2" ]] ; then
to=$2
fi
## Insert this to default to rest-of-day when first argument
## but no second argument. Basically just sets second
## argument to 23 so it will be transformed to end-of-day.
#if [[ ! -z "$1"]] ; then
# if [[ -z "$2"]] ; then
# to=23
# fi
#fi
if [[ ${#from} -ne 11 || ${#to} -gt 11 ]] ; then
usage
exit 1
fi
# Sneaky code to modify a short "to" based on the start of "from".
# ${#from} is the length of ${from}.
# $((${#from}-${#to})) is the length difference between ${from} and ${to}
# ${from:0:$((${#from}-${#to}))} is the start of ${from} long enough
# to make ${to} the same length.
# ${from:0:$((${#from}-${#to}))}${to} is that with ${to} appended.
# Voila! Easy, no?
if [[ ${#to} -lt ${#from} ]] ; then
to=${from:0:$((${#from}-${#to}))}${to}
fi
# Process all files, checking that they're inside the range.
echo "From ${from} to ${to}"
for file in [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].[0-9][0-9].* ; do
if [[ ! ( ${file:0:11} < ${from} || ${file:0:11} > ${to} ) ]] ; then
echo " ${file}"
fi
done
When you create the files 20091026.00.${RANDOM} through 20091028.23.${RANDOM} inclusive, this is a couple of sample runs:
pax> ./loopscript.sh 20091026.07 9
From 20091026.07 to 20091026.09
20091026.07.21772
20091026.08.31390
20091026.09.9214
pax> ./loopscript.sh 20091027.21 28.02
From 20091027.21 to 20091028.02
20091027.21.22582
20091027.22.30063
20091027.23.29437
20091028.00.14744
20091028.01.6827
20091028.02.10366
pax> ./loopscript.sh 00000000.00 99999999.99 # or just leave off the parameters.
20091026.00.25772
20091026.01.25964
20091026.02.21132
20091026.03.3116
20091026.04.6271
20091026.05.14870
20091026.06.28826
: : :
20091028.17.20089
20091028.18.13816
20091028.19.7650
20091028.20.20927
20091028.21.13248
20091028.22.9125
20091028.23.7870
As you can see, the first argument must be of the correct format yyyymmdd.hh. The second argument can be shorter since it inherits the start of the first argument to make it the correct length.
This only attempts to process files that exist (from ls) and of the correct format, not every date/hour within the range. This will be more efficient if you have sparse files (including at the start and the end of the range) since it doesn't need to check that the files exist.
By the way, this is the command that created the test files, if you're interested:
pax> for dt in 20091026 20091027 20091028 ; do
for tm in 00 01 02 ... you get the idea ... 21 22 23 ; do
touch $dt.$tm.$RANDOM
done
done
Please don't type that in verbatim and then complain that it created files like:
20091026.you.12345
20091028.idea.77
I only trimmed down the line so it fits in the code width. :-)
One possible solution: convert dates into standard Unix representation of "Seconds passed since the epoch" and loop, increasing this number by 3600 (number of seconds in an hour) each iteration. Example:
#!/bin/bash
# Parse your input to date and hour first, so you get:
date_from=20090911
hour_from=10
date_to=20091026
hour_to=01
i=`date --date="$date_from $hour_from:00:00" +%s`
j=`date --date="$date_to $hour_to:00:00" +%s`
while [[ $i < $j ]]; do
date -d "1970-01-01 $i sec" "+%Y%m%d.%H"
i=$[ $i + 3600 ]
done

Resources