How retrieve the creation date of photos with a script - bash

What I want to do is reorganize files in my Camera Roll folder. Using the creation date I want to Put them inside folders according to Year/Month Format using their creation date.
In this answer, they explain how to make folders and organize them:
https://stackoverflow.com/a/1314394/4980886
#!/bin/bash
find $PHOTO_DIR -regextype posix-extended -type d -regex '.*/[0-9]{4}/[0-9]{2}/[0-9]{2}$' |
while read dir; do
newdir="$(echo $dir | sed 's#/\([0-9]\{4\}\)/\([0-9]\{2\}\)/\([0-9]\{2\}\)$#/\1-\2-\3#')"
mv "$dir" "$newdir"
rmdir "$(dirname $dir)"
rmdir "$(dirname $(dirname $dir))"
done
But it doesn't address how to get the creation date, maybe I should get the metadata from EXIF data. How to do either?

According to exiftool man page 'Renaming examples'
exiftool '-Directory<DateTimeOriginal' -d %Y/%m/%d "$dir"
and if only copying is required, there is an option:
exiftool -o . '-Directory<DateTimeOriginal' -d %Y/%m/%d "$dir"
has the same effect as above except files are copied instead of moved.

i have written a bash script to copy files straight off my iphone/ipad and copy them into folders on a target drive based on image creation date. i use a program called exiftool found at http://www.sno.phy.queensu.ca/~phil/exiftool/
for a given image, i extract the creation datetime into an array using
DATEBITS=( $(exiftool -CreateDate -FileModifyDate -DateTimeOriginal "$IMGFILE" | awk -F: '{ print $2 ":" $3 ":" $4 ":" $5 ":" $6 }' | sed 's/+[0-9]*//' | sort | grep -v 1970: | cut -d: -f1-6 | tr ':' ' ' | head -1) )
where $IMGFILE is the path to the image file. you can then get access to the year, month, day etc using
YR=${DATEBITS[0]}
MTH=${DATEBITS[1]}
DAY=${DATEBITS[2]}
HR=${DATEBITS[3]}
MIN=${DATEBITS[4]}
SEC=${DATEBITS[5]}
it is then trivial to create the directory you need to stash the image file in

I've been working on sorting 14,000 photos from 1997 on. It's a bear because not all are digital cameras, especially the early ones. I used the 'CreateDate' tag in the image files.
I wrote the following BASH script to perform the sorting based on EXIF data, if possible. It looks for the EXIF 'CreateDate' tag first. If found, it uses it. If not, it looks for the 1st 8 characters of the file name to see if they are a VALID date (ANDROID, etc). If neither tests positive, it looks for the EXIF 'FileModifyDate' tag, which can be inaccurate. If the EXIF 'CreateDate' is found, the date prefix (YYYMMDD-) will be added to the file name (if it doesn't already exist) and then it will be moved into it's appropriate directory. If all three tests fail, the file is left alone for user intervention.
If you're lucky, and the camera supported it, and the photographer enabled it, the date may be imprinted on the image for you. I had to sort MANY photos this way, so be sure to look at the photos that are not processed and those that use the EXIF 'FileModifyDate' method of sorting.
Also, if you find you have some of those KODAK PCD picture files, you can use ImageMajick's "convert" like so:
convert $OrigFileName[$SUFX] -colorspace RGB "$(basename $OrigFileName .pcd).jpg"
However, ImageMajick doesn't always copy all the tags to the new image file but you can use EXIFTOOL to transfer the tags to your new image file like so:
exiftool -tagsFromFile $OrigFileName $(basename $OrigFileName .pcd).jpg
Code:
#! /bin/bash
# This script is used to sort photos. It uses the EXIFTOOL to
# 1st attempt to extract the photo's "CreateDate". If it is
# invalid, then the file name is checked to see if it begins
# with "YYYYMMDD", which is also checked for validity. If the
# prior two checks fail, then the photo's "FileModifyDate" is
# used but can be inaccurate.
# If a valid creation date is found and the file name is NOT
# date-encoded, then the file is renamed with a "YYYYMMDD-"
# prefix.
#=======================================================================
# Revision index:
# 2019-0704: KSV - Created and tested.
#=======================================================================
DEBUG=0 # Debugging
DRYRUN=0 # Do everything but move the files
NOTE="" # Notes
# ANSI COLORS
CRE="$(echo -e '\r\033[K')"
RED="$(echo -e '\033[1;31m')"
GRN="$(echo -e '\033[1;32m')"
YEL="$(echo -e '\033[1;33m')"
BLU="$(echo -e '\033[1;34m')"
MAG="$(echo -e '\033[1;35m')"
CYN="$(echo -e '\033[1;36m')"
WHT="$(echo -e '\033[1;37m')"
NML="$(echo -e '\033[0;39m')"
#=======================================================================
# Functions
#=======================================================================
# Enter with: YEAR, MONTH and DAY
# Returns: 0=invalid date, 1=valid date
# EX: IsValidDate $YEAR $MONTH $DAY
IsValidDate() {
#echo "Parm: Y=$1,${#1} M=$2,${#2} D=$3,${#3}" >/dev/stderr
if ([ "$1" -ge "1950" ] && [ "$1" -le "2050" ]) || \
([ "$2" -ge "01" ] && [ "$2" -le "12" ]) || \
([ "$3" -ge "01" ] && [ "$3" -le "31" ]) ; then
echo "1" # valid date
else
echo "0" # invalid date
fi
}
# Dump debugging info
# EX: $(DumpDebug $FN $FILE $EXT $FN_WD $DATE $YEAR $MONTH $DAY "$NOTE")
DumpDebug() {
#echo "1=${#FN}, 2=${#FILE}, 3=${#EXT}, 4=${#FN_WD}, 5=${#DATE}, 6=${#YEAR}, 7=${#MONTH}, 8=${#DAY}, 9=${#NOTE}" >/dev/stderr
echo "================================"
echo "FN = $1"
echo "FILE = $2"
echo "EXT = $3"
echo "FN_WD = $4"
echo "DATE = $5"
echo "YEAR = $6"
echo "MONTH = $7"
echo "DAY = $8"
echo "ValidDate = $(IsValidDate $6 $7 $8)"
echo "NOTE = $9"
echo "================================"
}
#=======================================================================
# Script starts here
#=======================================================================
# Use exiftool to find video and photos
#exiftool -filename *.[JjGg][PpIi][GgFf] *.[Jj][Pp][Ee][Gg] *.[Mm][PpOo][Gg4Vv] 2>/dev/null | awk {'print $4'} | \
find . -maxdepth 1 -type f -iname "*.[JjGg][PpIi][GgFf]" -or \
-iname "*.[Jj][Pp][Ee][Gg]" -or \
-iname "*.[Mm][PpOo][Gg4Vv]" | \
while read FN ; do
FN=$(basename $FN) # strip the leading "./"
if [ -e $FN ] && [ ${#FN} != 0 ] ; then # be sure the file exists!
EXT=${FN##*.} # extract the extension
FILE=${FN%.*} # extract the base file name
# First attempt to see if there is a valid date prefix in the file name.
YEAR=$(echo ${FN:0:4} | egrep -E ^[0-9]+$ ) # insure digits only
MONTH=$(echo ${FN:4:2} | egrep -E ^[0-9]+$ ) # insure digits only
DAY=$(echo ${FN:6:2} | egrep -E ^[0-9]+$ ) # insure digits only
DATE="$YEAR:$MONTH:$DAY" # create a DATE string
# Check the filename's derived date from for validity (not NULL strings)
# and that the date falls within the proper range
if ([ ! -z "${YEAR}" ] && [ ! -z "${MONTH}" ] && [ ! -z "${DAY}" ]) && \
[ $(IsValidDate $YEAR $MONTH $DAY) == 1 ] ; then
if [ $DEBUG == 1 ] ; then echo "ValidDate: $(IsValidDate $YEAR $MONTH $DAY)" ; fi
FN_WD=0 # date prefix exists, do not append the derived date to the filename.
else
FN_WD=1 # append the derived date prefix to the filename.
fi
# Next, attempt to find an EXIF CreateDate from the file, if it exists.
DATE=$(exiftool -s -f -CreateDate $FN | awk '{print $3}')
# Perform sanity check on correctly extracted EXIF CreateDate
if [ "${DATE}" != "-" ] && [ "${DATE}" != "0000:00:00" ] ; then
# Good date extracted, so extract the year, month and day
# of month from the EXIF info
echo "A valid ${WHT}CreateDate${NML} was found, using it."
YEAR=${DATE:0:4}
MONTH=${DATE:5:2}
DAY=${DATE:8:2}
NOTE="(by CreateDate)"
else
# EXIF CreateDate invalid or not found, so attempt to derive the
# date from the file name.
YEAR=$(echo ${FN:0:4} | egrep -E ^[0-9]+$ ) # insure digits only
MONTH=$(echo ${FN:4:2} | egrep -E ^[0-9]+$ ) # insure digits only
DAY=$(echo ${FN:6:2} | egrep -E ^[0-9]+$ ) # insure digits only
DATE="$YEAR:$MONTH:$DAY" # create a DATE string
# check the extracted date from filename for validity (not NULL strings)
# and that the date falls within the proper range
#if [ -z "${YEAR}" ] || [ -z "${MONTH}" ] || [ -z "${DAY}" ] ; then
if ([ ! -z "${YEAR}" ] && [ ! -z "${MONTH}" ] && [ ! -z "${DAY}" ]) && \
[ $(IsValidDate $YEAR $MONTH $DAY) == 1 ] ; then
echo "A valid ${WHT}FileNameDate${NML} was found, using it."
NOTE="(by file name)"
else
# EXIF CreateDate and FileNameDate extraction failed, so attempt
# to extract the EXIF FileModifyDate from the file, if it exists.
DATE=$(exiftool -s -f -FileModifyDate $FN | awk '{print $3}')
# Perform sanity check on correctly extracted EXIF FileModifyDate
if [ "${DATE}" != "-" ] && [ "${DATE}" != "0000:00:00" ] ; then
# Good FileModifyDate found, extract the year, month and
# day of month from the EXIF info
echo "A valid EXIF CreateDate and FileNameDate were not found!"
echo " The innacurate ${WHT}FileModifyDate${NML} will be used."
YEAR=${DATE:0:4}
MONTH=${DATE:5:2}
DAY=${DATE:8:2}
NOTE="(!inaccurate! by FileModifyDate)"
FN_WD=0 # date prefix exists, do not append the derived date to the filename.
else
echo "Invalid date retrieved!"
if [ $DEBUG == 1 ] ; then
echo "Length = ${#YEAR}-${#MONTH}-${#DAY}"
fi
echo "Skipping File: $FN..."
echo
fi
fi
fi
# Modify the filename if a valid EXIF CreateDate or FileNameDate was found.
if [ $FN_WD == 0 ] ; then
FILE=${FILE}.${EXT}
else
FILE=${YEAR}${MONTH}${DAY}-${FILE}.${EXT}
fi
# Debug output
if [ $DEBUG == 1 ] ; then DumpDebug $FN $FILE $EXT $FN_WD $DATE $YEAR $MONTH $DAY "$NOTE" ; fi
# We have a date, hopefully a good one, move the file
if [ $DRYRUN == 0 ] ; then
# create the directory structure. Pipe errors to NULL
mkdir -p $YEAR/$MONTH/$DAY >/dev/null 2>&1
# move the file to the appropriate directory
echo " -> Moving $FN to $YEAR/$MONTH/$DAY/$FILE $NOTE"
mv $FN $YEAR/$MONTH/$DAY/$FILE
echo
else
echo "Dryrun: Moving $FN to $YEAR/$MONTH/$DAY/$FILE"
echo
fi
# Clear the variables
FN=""; FILE=""; EXT=""; FN_WD=""; DATE=""; YEAR=""; MONTH=""; DAY=""; NOTE=""
else
echo
echo "File $FN not found!"
echo
fi
done

have been banging my head around the same or similar problem.
Exiftool is a powerful thing, however it has some limitations and it doesn't really solve file duplicate problem.
This is what I use in my script
# EXIFSubSecCreateDateParser extracts EXIF metadata: the year, month, day, hour, minute, second, subsecond,
# and generates date and note
EXIFSubSecCreateDateParser() {
# Define a variable and pass the arguments
EXIF_OUTPUT="${1}"
# Substitute dots with a common colon delimiter
EXIF_OUTPUT_SUBSTITUTE="${EXIF_OUTPUT//./:}"
# Define delimiter
DELIMITER=":"
# Concatenate the delimiter with the main string
DELIMITED_EXIF_OUTPUT="${EXIF_OUTPUT_SUBSTITUTE}${DELIMITER}"
# Split the text based on the delimiter
EXIF_OUTPUT_ARRAY=()
while [[ "${DELIMITED_EXIF_OUTPUT}" ]]; do
EXIF_OUTPUT_ARRAY+=( "${DELIMITED_EXIF_OUTPUT%%${DELIMITER}*}" )
DELIMITED_EXIF_OUTPUT="${DELIMITED_EXIF_OUTPUT#*${DELIMITER}}"
done
# Assign the array values to the corresponding variables
YEAR="${EXIF_OUTPUT_ARRAY[0]}"
MONTH="${EXIF_OUTPUT_ARRAY[1]}"
DAY="${EXIF_OUTPUT_ARRAY[2]}"
HOUR="${EXIF_OUTPUT_ARRAY[3]}"
MINUTE="${EXIF_OUTPUT_ARRAY[4]}"
SECOND="${EXIF_OUTPUT_ARRAY[5]}"
SUBSECOND="${EXIF_OUTPUT_ARRAY[6]}"
DATE="${YEAR}:${MONTH}:${DAY}"
}
# Attempting to extract EXIF metadata from the file to an array.
EXIF_EXTRACT=( $(exiftool -s -f -SubSecCreateDate -CreateDate -Model "${WIPSortedFileAbsolutePath}" | awk '{print $3":"$4}') )
# Assigning the array values to variables
EXIF_SubSecCreateDate_OUTPUT="${EXIF_EXTRACT[0]}"
EXIF_CreateDate_OUTPUT="${EXIF_EXTRACT[1]}"
EXIF_Model_OUTPUT="${EXIF_EXTRACT[2]}"
# Setting Target Directory structure with TargetDirectoryStructure
DestinationStructure="${YEAR}/${MONTH}/${DAY}"
# Setting Filename format
FormatedFileName="${MODEL}-${YEAR}${MONTH}${DAY}-${HOUR}${MINUTE}${SECOND}-${SUBSECOND}.${NormalisedFileExtension}"
# Moving the file to the Desination File Path
mv "${File}" "${DestinationPath}/${DestinationStructure}/${FormatedFileName}"
I found some answers but piecemeal solutions did not go far enough. I ended up writing my own thing with a kick-start based off Johnny Quest's script.
Anyway, have a look at https://github.com/ivang-coder/Neatly-Sorted
It might give you some ideas for your endeavor.

Related

bash script - ignoring whitespaces in script parameters

I'm quite new to bash scripting and I've ran out of ideas in my homework script.
The script takes 4 arguments - pathToDirectory, [-c/-m/-r] == copy/move/remove, ext1 and ext2 (example of running a script: script.sh /home/user/somefolder -c a.txt b.sh ).
The script should find all files in /home/user/someFolder (and its all subfolders) that contain 'a.txt' in their names and (in -c and -m case) rename that 'a.txt' part to 'b.sh' and depending on -c/-m argument either create a new file or just rename an existing file (in -r case it just removes the file) and then write in stdout something like 'old name => new name'.
example output of a script mentioned above:
/home/user/someFolder/bbb.txt => /home/user/someFolder/bba.txt
Well, that was not a problem to implement, everything worked until I posted my code to our upload system (evaluates our script).
The very first Upload System's try to run my script looked like "script.sh /something/graph 1 -c .jpg .jpeg".
The problem now is, that the whole '/something/graph 1' is a path and that whitespace before '1' ruins it all.
expected output: ./projekty/graph 1.jpg => ./projekty/graph 1.jpeg
my script output: ./projekty/graph => ./projekty/graph.jpeg
1.jpg => 1.jpeg
What I have so far:
if [ "$2" = "-r" ]; then
for file in $(find $1 -name "*$3"); do
echo $file
rm -f $file
done
elif [ "$2" = "-c" ]; then
for file in $(find "$1" -name "*$3") ; do
cp "$file" "${file//$3/$4}"
echo $file "=>" ${file%$3}$4
done
elif [ "$2" = "-m" ]; then
for file in $(find $1 -name "*$3"); do
mv "$file" "${file//$3/$4}"
echo $file "=>" ${file%$3}$4
done
else
echo Unknown parameter >&2
fi
My tried&notworking&probablystupid idea: as the -r/-c/-m parameter should be at $2, I was able to detect that $2 is something else (assumpting something that still belongs to the path) and append that $2 thing to $1, so then I had a variable DIR which was the whole path. Using shift I moved all parameters to the left (because of the whitespace, the -r/-m/-c parameter was not on $2 but on $3, so I made it $2 again) and then the code looked like: (just the -c part)
DIR=$1
if [ "$2" != "-r" ] && [ "$2" != "-c" ] && [ "$2" != "-m" ]; then
DIR+=" $2"
shift
fi
if [ "$2" = "-c" ]; then
for file in $(find "$DIR" -name "*$3") ; do
cp "$file" "${file//$3/$4}"
echo $file "=>" ${file%$3}$4
done
fi
when i echoed "$DIR", it showed the whole path (correctly), but it still didn't work..
Is there any other/better/any way how to fix this please ? :/
Thanks in advance !
As the target string needs to be replaced only at the very end of a filename, "${file//$3/$4}" is a bad idea.
Example: ./projekty/graph.jpg.jpg.jpg.graph.jpg
Passing a string prone to unquoted expansion to a loop is a no better idea either.
The fact is that find works as expected and its output looks like:
./projekty/graph 1.jpg
But inside a loop it is expanded incorrectly:
./projekty/graph
1.jpg
To avoid this, you can save the output of find to a variable and then tokenize it until no text is left:
list="$(find $1 -name "*$3")"
while [ -n "$list" ]; do
file="${list%%$'\n'*}"
list="${list#$file}"
list="${list#$'\n'}"
# your commands here
# ...
done

navigating directories and sub-directories and moving the files using shell

Directory structure
MyDirectory
-data/
-DATA-TT_20160714_soe_test_testbill_52940_1.lst
-output/
-DATA-TT_20160714_soe_test_testbill_52940_1.pdf
-Backup/
enter code here
#!/bin/bash
for i in $( ls ); do
echo $i
#cd $i
#cd $i/data/
echo $i
cd $i/data/
echo $i/data/
$(ls *1.lst)
cd ../output
$(ls *1.pdf)
done
I need to navigate in the directories and the sub directories where
the
input and output files are kept. These file have
date in YYYYMMDD format which I need to compare with current date.
If the difference is greater than 1 month I need to
zip those file and move them to Backup directory. The "DATA-TT" part
is constant.
Can anyone help me in this.There may be many directories with same sub directories structure.Example MyDirectory1,MyDirectory2,MyDirectory3
Have to leave for a meeting. I'll leave you the script I've been working on to help you:
#!/bin/sh
# in your case filename would be the variable used in the for loop
filename=$(find DATA-TT*)
# Part gets the date from the filename
part=$(echo $filename | grep -Eo '[[:digit:]]{8}')
echo "$filename -> $part"
# limit_date is current date -30 days
limit_date=$(date +'%Y%m%d' -d "now 30 days")
echo $cur_date
# this part compares and echoes, you can try it and replace echoes with ziping, moving, etc.
if [ $part -ge $limit_date ]
then
echo "part is older than 1 month"
else
echo "part had not yet reached 1 month age"
fi
This one for the data directory the same for the output
#!/bin/bash
list=($(find data -name "DATA-TT_*"))
limit=`date +%Y%m%d -d "-30 days"`
for i in ${list[#]}; do
filedate=$(echo $i| cut -d'_' -f 2)
if [ "$limit" -gt "$filedate" ]; then
mv $i backup
fi
done

Remove folders based on naming pattern

I have a list of 400 folders with the following pattern:
backup_01_01_2013/
backup_01_02_2013/
backup_01_03_2013/
...
backup_08_25_2014/
A new folder is created every day via a cron job.
I want to delete all folders EXCEPT:
Keep the 30 most recent folders
Keep the first of every month
How can I delete all the unnecessary folders using Bash in Linux?
So, assuming they're all in the same directory.
I'll point out I tested portions of this script, but not all of it, so you'll probably want to do some modification/testing before you actually run it - lest you end up with all your directories removed.
# Find the date 30 days ago.
recent_dirs=`date -d "-30 days" +%d-%m-%Y`
rec_month=`echo "${recent_dirs}" | cut -d '-' -f2`
rec_day=`echo "${recent_dirs}" | cut -d '-' -f1`
rec_month=`echo "${recent_dirs}" | cut -d '-' -f3`
# Go to your "home" directory for the directories.
cd /path/to/home/directory
for i in *; do
# Check to see if the element is a directory.
if [ -d "${i}" ]; then
echo "Processing directory - ${i} ... "
# Determine the date information for the directory.
cur_month=`cat "${i}" | cut -d '_' -f1`
cur_day=`cat "${i}" | cut -d '_' -f2`
# Keep all directories from the first of the month.
if [ "${first_day}" -eq "01" ]; then continue; fi
# Keep all directories from the current month and current year.
if [ "${cur_month}" -eq "${rec_month}" ]; then
if [ "${cur_year}" -eq "${rec_year}" ]; then continue; fi
fi
# Keep all directories from last month, but newer than today's DAY. I'm not
# a pro at bash arithmetic, so you might have to goof with the subshell
# math to get it to work quite right.
if [ "${cur_month} -eq $(( expr $rec_month - 1 )) ]; then
if [ "${cur_day}" -gt "${rec_day}" ]; then continue; fi
fi
# So at this point, I think we've dealt with everything, except the case where
# the current month is January, and so you're trying to save December's
# directories from last year. I think this handles it.
if [ "${rec_month}" -eq "01" ]; then
if [ "${cur_month} -eq "12" ]; then
if [ "${cur_year}" -eq $(( expr ${rec_year} - 1 )) ]; then continue; fi
fi
fi
# If we haven't stopped processing the directory by now, it's time
# remove our directory.
rm -fr "${i}"
else
echo "Skipping non-directory "${i}"...
do
exit 1
Something this won't do is deal with months with 31 days in it, so you may end up in many cases having saved 31 directories, instead of 30. I get the impression that you're trying to do a cleanup, and not a strict compliance routine, though...
Quick & dirty - try 'ls -ltr' combined with tail -30 to get all but 30. run a for loop and rm -rf

How to grave the Date from this command in bash script

!/bin/bash
# When a match is not found, just present nothing.
shopt -s nullglob
# Match all .wav files containing the date format.
files=(*[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]*.wav)
if [[ ${#files[#]} -eq 0 ]]; then
echo "No match found."
fi
for file in "${files[#]}"; do
# We get the date part by part
file_date=''
# Sleep it to parts.
IFS="-." read -ra parts <<< "$file"
for t in "${parts[#]}"; do
# Break from the loop if a match is found
if [[ $t == [0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] ]]; then
file_date=$t
break
fi
done
# If a value was not assigned, then show an error message and continue to the next file.
# Just making sure there is nothing in Array and date before it moves on
if [[ -z $file_date ]]; then
continue
fi
file_year=${file_date:0:4}
file_month=${file_date:4:2}
mkdir -p "$file_year/$file_month"
# -- is just there to not interpret filenames starting with - as options.
echo "Moving: ./"$file "to: " "./"$file_year"/"$file_month
mv "$file" "$file_year/$file_month"
done
Now there are files I would need to do the date to gra the date and then move it like I do now. for example there is a file called meetme.. its a wav file and I have DIR with YYYY/MM and would like to move thoses files without YYYYMMDD in file name already
If you're writing a program to do something with that information, then you might prefer seconds-since-epoch and use date to get the date in the desired format.
$ date -d #$(stat --format='%Y' testdisk.log) +%Y%m%d
20130422
You can also get the ascii representation, and then manipulate the string.
$ stat --format='%y' testdisk.log
2013-04-22 09:11:39.000000000 -0500
$ date_st=$(stat --format='%y' testdisk.log)
$ date_st=${date_st/ */}
$ date_st=${date_st//-/}
$ echo ${date_st}
20130422

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