I have folders with the naming convention:
yearmonthday_jobcode_descriptor
ie 20101007_GR1234_Whatsit
I'm attempting to write a script that does a few things:
move any folders where the date is older than $X into ./old
move any folders where the date is in the future ./old
move any folders that dont conform to the convention ./old
delete any folders in ./old where the date is older than $X + $Y
Here is my current loop, as you can see my bash scripting is pretty mediocre.
#!/bin/bash
for file in $1/*; do
if [ -d $file ]; then
echo $(echo $file | grep -oE "[0-9]{8}_[A-Z]*[0-9]*_?.*")
fi
done
I can handle all the looping, my main concern at the moment is how to extract the folder names into variables that can be compared for dates. The current regex above just checks for conformity to the convention.
You can use the cut command to tokenize the string (-d specifies the delimiter, -f specifies which field you want to keep) -
echo 20101007_GR1234_Whatsit | cut -d'_' -f1
gives
20101007
From there, you can use the date command to parse the date-
foo=`date -d 20101007 +%s`
will convert the date string to the epoch time (seconds since Jan 1, 1970) which can then be easily compared.
If you don't want to mess with epoch time, you can call date multiple times to parse the different parts -
day=`date -d 20101007 +%d`
month=`date -d 20101007 +%m`
year=`date -d 20101007 +%Y`
but it will make your comparisons a little more complicated.
$ foo="20101007_GR1234_Whatsit"
$ echo ${foo%%_*}
20101007
#!/bin/bash
ls -d ????????_[A-Z][A-Z]*_* | awk 'BEGIN{
x=mktime("2010 10 08 00 00 00") # set your X here, whatever it is
y=mktime("2010 10 02 00 00 00") # set your Y here, whatever it is
olddir="/tmp/old"
}
{
yr=substr($0,1,4) # get year
mth=substr($0,5,2) # get month
day=substr($0,7,2) # get day
t= mktime(yr" "mth" "day" 00 00 00") #convert to secs for comparison
if (t < 0) { print "Wrong format: "$0 ;next}
if ( t > x){
print $0 " is later than X: "x
}else{
print "moving "$0" to old"
cmd="mv $0 "oldir
print cmd
#system(cmd) #uncomment to do actual moving
}
if ( t> (x+y) ){
..............
}
}'
This was my final solution, for anyone attempting to do this in the future:
#!/bin/bash
ARCHIVE="old"
NOW=`date +%s`
GRACE_PERIOD=$((60*60*24*7*2))
CUTOFF=$(($NOW-$GRACE_PERIOD))
DEADLINE=$(($CUTOFF-$GRACE_PERIOD))
function getos {
UNAME=`uname`
PLATFORM="unknown"
if [[ "$UNAME" == 'Linux' ]]; then
echo 'linux'
elif [[ "$UNAME" == 'Darwin' ]]; then
echo 'osx'
else
echo 'linux'
fi
}
function dateToTimestamp {
DATE="$1"
OS=`getos`
TIMESTAMP='empty'
if [[ "$OS" == 'linux' ]]; then
TIMESTAMP=`date -d $DATE +%s`
elif [[ "$OS" == 'osx' ]]; then
TIMESTAMP=`date -j -f "%Y%m%d" "$DATE" "+%s"`
fi
echo $TIMESTAMP
}
function movetoarchive {
ITEM="$1"
if [ ! -d "$ARCHIVE" ]; then
mkdir $ARCHIVE
fi
FOLDER=`date +"%Y%m%d"`
if [ ! -d "$ARCHIVE/$FOLDER" ]; then
mkdir $ARCHIVE/$FOLDER
fi
mv $ITEM $ARCHIVE/$FOLDER/$ITEM
}
function deletefromarchive {
ITEM="$1"
WD=`pwd`
MATCH=$(echo $WD | grep -oE ".*?/$WORKING_DIR/$ARCHIVE$")
if [ -n "$MATCH" ]; then
rm -rf $ITEM;
fi
}
WORKING_DIR="$1";
cd "$WORKING_DIR"
for file in *; do
if [ -d $file ]; then
if [ "$file" != "$ARCHIVE" ]; then
MATCH=$(echo $file | grep -oE "[0-9]{8}-[A-Z]*[0-9]*-?.*")
if [ -z "$MATCH" ]; then
movetoarchive $file
else
DATE=$(echo $file | cut -d'-' -f1);
TIMESTAMP=`dateToTimestamp $DATE`
if [[ "$TIMESTAMP" > "$NOW" ]]; then
movetoarchive $file
elif [[ "$TIMESTAMP" < "$CUTOFF" ]]; then
movetoarchive $file
fi
fi
fi
fi
done
cd $ARCHIVE
for file in *; do
if [ -d $file ]; then
MATCH=$(echo $file | grep -oE "^[0-9]{8}$")
if [ -z "$MATCH" ]; then
deletefromarchive $file
else
TIMESTAMP=`dateToTimestamp $file`
if [[ "$TIMESTAMP" > "$NOW" ]]; then
deletefromarchive $file
elif [[ "$TIMESTAMP" < "$DEADLINE" ]]; then
deletefromarchive $file
fi
fi
fi
done
Related
I'm trying to develop a shell script which asks the user to enter three filenames and then the script will output the most recent file that was created. However, every time I run my code, it automatically goes to the elseif section and displays "No filename found ....EXITING"
What mistake am I doing in my code?
read -p "Enter three filenames : $1 $2 $3 "
if [ -f "$1" ] && [ -f "$2" ] && [ -f "$3" ]
then
ls -t $read | head n1
else
echo "No filename found .......EXITING"
fi
To read into multiple variables, you have to specify the variable names:
read -rp "Enter three filenames: " f1 f2 f3
-r makes sure backslashes aren't interpreted in the input.
Now, instead of $1 etc., you use f1 and friends:
if [[ -f $f1 ]] && [[ -f $f2 ]] && [[ -f $f3 ]]; then
I've used the Bash-specific [[ ]] instead of [ ] here as it is more forgiving regarding quoting (and does it for us).
And finally, parsing ls is an antipattern. You could compare the files directly instead:
newest=$f1
[[ $f2 -nt $newest ]] && newest=$f2
[[ $f3 -nt $newest ]] && newest=$f3
printf '%s\n' "$newest"
All together:
read -rp "Enter three filenames: " f1 f2 f3
if [[ -f $f1 ]] && [[ -f $f2 ]] && [[ -f $f3 ]]; then
newest=$f1
[[ $f2 -nt $newest ]] && newest=$f2
[[ $f3 -nt $newest ]] && newest=$f3
printf '%s\n' "$newest"
else
echo "No filename found" >&2
exit 1
fi
Although I was able to figure out my first script and get it to run, I am having issues with my second script. Basically I need to retrieve my deleted files and restore them in my created home directory. I was able to get some of question 2, and all of 3 and 4 to work successfully. The issue I have with question 2 is if I type up the file that I want to restore, it will pull up the file and inode number which is fine. However, if I run the script without the desired file, it will show a bunch of inodes and the command "Missing Operand". The missing operand command is one that I want since I created it in order to test that the files I want to restore exist. I have attached my code below. Again, I wonder what I got wrong. I did some of the suggestions shellcheck offered but I still got the same results.
Here is my code:
#!/bin/bash
#2.
fileName=$1
inode=$(ls -i $1 | cut -d " " -f1)
echo "safe_rm_restore $1_$inode"
#3.
function movingfile(){
if [ -e $HOME/deleted ] ;
then
mv $1_$inode $HOME/deleted $(grep $1 $HOME/.restore.info | cut -d" " -f3)
fi
}
movingfile $1
#4.
function safe(){
filename=$1
if [ ! -e $1 ] ;
then
echo "safe_rm_restore :cannot restore '$1': No such file or directory"
exit 1
elif [ $# -eq 0 ] ;
then
echo "safe_rm_restore: cannot restore '$1' : Missing operand"
exit 1
fi
}
safe $1
##5.
function restoreFile(){
if [ -e $(grep $1 $HOME/.restore.info | cut -d" " -f2) ] ;
then
read -p "File currently exists. Do you want to overwrite y/n?: word"
if [[ $word = "yes" || $word = "Y" || $word = "y" ]] ;
then
rm $(grep $1 $HOME/.restore.info | cut -d" " -f2)
mv $HOME/deleted $(grep $1/.restore.info | cut -d" " -f2)
fi
if [[ $word = "n" ]] ;
then
rm $1
exit 0
else
echo "Invalid Option"
exit 1
fi
fi
}
restoreFile $1
##6.
function restored(){
remove=$(grep -v $1 $HOME/.restore.info | tee $HOME/.restore.info)
if [[ ! -e $HOME/deleted && -e $(grep $1 $HOME/.restore.info | -d":" -f2)]];
then
$remove
fi
}
restored $1
I've followed the suggestions that you all gave me (and it moved me towards the right direction) but I still had some issues getting my operations to fully work. For instance, if no file name was given, my script was supposed to say Missing Operand but instead it gives all inode numbers in my file and my script freezes for sometime. Here is my updated second bash script. The indention looks great from my UNIX system but is somehow skewed when I transfer it on here. Thank you again!
#!/bin/bash
##2.
fileName="$1"
inode="$(ls -i $1 |cut -d" " -f1)"
echo "safe_rm_restore $1_$inode"
touch "$1"
##3.
movingfile(){
if [ -e $HOME/deleted ] ;
then
touch $1_$inode
mv $1_$inode $HOME/deleted $(grep $1 $HOME/.restore.info | cut -d" " -f4)
else
exit 1
fi
}
movingfile $1
##4.
safe(){
filename=$1
if [ ! -e $1 ] ;
then
echo "safe_rm_restore :cannot restore '$1': No such file or directory"
exit 1
elif [ $# -eq 0 ] ;
then
echo "safe_rm_restore: cannot restore '$1' : Missing operand"
exit 1
fi
}
safe $1
##5.
restoreFile(){
if [ -e $(grep $1 $HOME/.restore.info | cut -d" " -f4) ] ;
then
read -p "File currently exists. Do you want to overwrite y/n?:" word
if [[ $word = "yes" || $word = "Y" || $word = "y" ]] ;
then
mv $1_$inode $HOME/project $(grep $1/.restore.info | cut -d" " -f4)
fi
if [[ $word = "n" ]] ;
then
rm -r $1_$inode
exit 0
else
echo "Invalid Option"
exit 1
fi
fi
}
restoreFile $1
##6.
restored(){
remove=$(grep -v $1 $HOME/.restore.info | tee $HOME/.restore.info)
if [[ ! -e $HOME/deleted && -e $(grep $1 $HOME/.restore.info |cut -d":" -f4) ]];
then
$remove
fi
}
restored $1
I'm missing some images that should have been archived when this script runs. I think this may be to do with my indentations or my Md5 sum. I have tried everything I can think of.
here is the code with out the correct indentations:
#!/bin/sh
if [ ! -d "$1" ]; then
echo Directory "$1" cannot be found. Please try again.
exit
fi
if [ $# -eq 1 ]; then
echo "usage: Phar image_path archive_path"
exit
fi
if [ -d "$2" ]; then
echo "archive exists"
else
echo "the directory 'archive' does't exist. Creating directory 'archive'."
mkdir -p ~/archive
fi
find $1 -iname "IMG_[0-9][0-9][0-9][0-9].JPG" | cat > list.txt
[ -f ~/my-documents/md5.txt ] && rm md5.txt || break
while read line;
do md5sum $line | xargs >> md5.txt
done < list.txt
sort -k 1,1 -u md5.txt | cat > uniquemd5.txt
cut -d " " -f 2- uniquemd5.txt > uniquelist.txt
sort uniquelist.txt -r -o uniquelist.txt
for line in $(cat uniquelist.txt)
do
file=$(basename $line) path="$2/file"
if [ ! -f $path ];
then
cp $line $2
else
cp $line $path.JPG
fi
rm uniquelist.txt md5.txt uniquemd5.txt list.txt
done
This loop
while read line;
do md5sum $line | xargs >> md5.txt
done < list.txt
should probably be
while read line;
do md5sum "$line"
done < list.txt > md5.txt
Quote parameter expansions, and it's unclear why you needed.
I am trying to write a batch file which checks if the file names present in a text file are present in a folder.
I have a folder called PAN where i have a text file named as PRAS_filelist.txt which stores all the file names. And a folder PRAS in which all my files are present.
My requirement is it should scan the folder and at the end, it should populate a error message in a file PRAS_PP_ERROR_LOG. but now it is coming out if any one file is not present since i have used exit.
Below is the code
`rm -f ../SessLogs/PRAS_PP_ERROR_LOG`
for i in `cat ./PAN/PRAS_filelist.txt`
do
file_name=$i
if [ -e ./PAN/PRAS/"$file_name"_* ]
then
if [ -s ./PAN/PRAS/"$file_name"_* ]
then
a=`sed -n '1p' ./PAN/PRAS/"$file_name"_*|cut -d'|' -f1`
b=`sed -n '$p' ./PAN/PRAS/"$file_name"_*|cut -d'|' -f1`
if [ "$a" == "H" ] && [ "$b" == "T" ]
then
trailer_record=`sed -n '$p' ./PAN/PRAS/"$file_name"_*|cut -d'|' -f2`
record_count=`wc -l ./PAN/PRAS/"$file_name"_*|cut -d' ' -f1`
r_c=`expr $record_count - 1`
if [ $trailer_record -eq $r_c ]
then
`cp ./PAN/PRAS/"$file_name"_* ./PAN/PRAS/STANDARDIZE/"$file_name"`
`sed -i '$d' ./PAN/PRAS/STANDARDIZE/"$file_name"`
else
echo "ERROR in file $file_name: Count of records is $r_c and trailer is $trailer_record">../SessLogs/PRAS_PP_ERROR_LOG
fi
else
echo "ERROR in file $file_name: header or trailer is missing">../SessLogs/PRAS_PP_ERROR_LOG
exit 1
fi
else
echo "ERROR in file $file_name: empty file">../SessLogs/PRAS_PP_ERROR_LOG
exit 1
fi
else
echo "ERROR: $file_name doesnot exist">../SessLogs/PRAS_PP_ERROR_LOG
exit 1
fi
done
refactoring your code a bit:
One odd thing is that you claim to have a list of filenames, but then you append an underscore before you check the file exists. Do the actual filenames actually have underscores?
#!/bin/bash
shopt -s nullglob
logfile=../SessLogs/PRAS_PP_ERROR_LOG
rm -f $logfile
while read file_name; do
files=( ./PAN/PRAS/${file_name}_* )
if (( ${#files[#]} == 0 )); then
echo "ERROR: no files named ${file_name}_*"
continue
elif (( ${#files[#]} > 1 )); then
echo "ERROR: multiple files named ${file_name}_*"
continue
fi
f=${files[0]}
if [[ ! -s $f ]]; then
echo "ERROR in file $f: empty file"
continue
fi
a=$(sed '1q' "$f" | cut -d'|' -f1)
b=$(sed -n '$p' "$f" | cut -d'|' -f1)
if [[ "$a" != "H" || "$b" != "T" ]]; then
echo "ERROR in file $f: header or trailer is missing"
continue
fi
trailer_record=$(sed -n '$p' "$f" | cut -d'|' -f2)
r_c=$(( $(wc -l < "$f") - 1 ))
if (( trailer_record == r_c )); then
sed '$d' "$f" > ./PAN/PRAS/STANDARDIZE/"$file_name"
else
echo "ERROR in file $f: Count of records is $r_c and trailer is $trailer_record"
fi
done < ./PAN/PRAS_filelist.txt | tee $logfile
I have a different HTML files in a folder. How to rename the files so that they have the names of:
1.html
2.html
3.html
...
This can make it:
i=1
for file in /your/folder/*
do
mv $file ${i}.html
i=$((i+1)) #((i++)) was giving errors (see comments)
done
It loops through all files in /your/folder and renames them according to the number $i that keeps increasing.
Here is my script
#!/bin/sh
#
# batchrename - renames files like 01.ext, 02.ext ...
#
# format : batchrename <list of files>
# or: -r <extension> <<list of files> or <dir>>
# -r - recoursively
counter=0
extrec=""
if [ "$#" -lt "1" ]; then
echo -e "\n\t\tUsage:\n\tbatchrename [opt]\nopt:"
echo -e "-r <ext> <folder> (or file list) -- renames recoursively ALL files"
echo -e "\tin folder <folder> (or by file list given) with extension .<ext>"
echo -e "<folder> -- renames ALL files in folder given"
echo -e "<file list> -- renames ALL files of given filelist.\n\n"
exit 0
fi
Name="$*"
if [ "$1" = "-r" ]; then
extrec="$2"
shift
shift
Name="$*"
[ "$Name" = "" ] && Name="./"
fi
echo -e "\n\t\t\tRENAMING"
for file in $Name
do
file=`echo "$file" | sed "s/<>/ /g"`
if [ -d "$file" ];then
echo -e "\nDiving into \033[38m $file \033[39m"
cd "$file"
if [ "$extrec" != "" ]; then
batchrename -r $extrec `ls -1 | sed "s/\ /<>/g"`
else
batchrename `ls -1 | sed "s/\ /<>/g"`
fi
cd ../
continue
fi
ext=`ext "$file"`
if [ "$ext" = "ion" ]; then
continue
fi
if [ "$extrec" = "" -o "$ext" = "$extrec" ];then
counter=`expr $counter + 1`
echo -e "Progress: $counter files\r\c"
mv "$file" "rnmd$counter.$ext"
fi
done
echo -e "\n\n\t\t\tENDING"
digits=`echo $counter|awk '{print length ($0)}'`
cnt=1
while [ $digits -gt $cnt ]
do
f=`ls -S -1|grep "rnmd[0-9]\{$cnt\}\."`
rename rnmd rnmd0 $f
cnt=`expr $cnt + 1`
done
if [ "$counter" -gt "0" ]; then
rename rnmd "" rnmd*
fi
echo -e "\n\t\t\tDone !!!\n"
After renaming all your files will looks like 001.file, 002.file, ... and so on. Amount of leading zeros depends on amount of files. So, after renaming ls will show right order of files!
It use intermediate script ext:
#!/bin/sh
#
# ext - returns file suffix (case-unsensitive)
#
File="$*"
if [ -d "$File" ]; then
echo ""
exit 0
fi
EXT=`echo $File|sed 's/.\{1,\}\.//g'`
if [ "$EXT" = "$File" ]; then
EXT=""
fi
echo $EXT| tr '[:upper:]' '[:lower:]'
Here is a similar code, just to add to rename with the same prefix and append an incremental value
declare -i x=1
for f in $(find -type f); do
mv -v $f ${f%/*}/change_me_$x ;
x=$x+1;
done