Script to delete files older than x days and also taking an input for multiple paths

Hi ,

I am a newbie!!!

I want to develop a script for deleting files older than x days from multiple paths. Now I could reach upto this piece of code which deletes files older than x days from a particular path. How do I enhance it to have an input from a .txt file or a .dat file? For eg: For deleting *.csv files older than Num_days, I am using below piece of code:

 
cd $FILEPATH
find ./ -type d ! -name . -prune -o -mtime +$Num_days -name "*.csv" -exec rm {} \;

I am using # ! /bin/sh

You could read the paths from the file using a while loop. Then call find once per path, substituting the value read into the find command twice, as the path argument and the argument to the first -name predicate.

Regards and welcome to the forum,
Alister

1 Like

There are several ways to do this. Assuming there are no whitespace characters in any of the directory names in the list of directories in your text file, the following is probably the shortest:

find $(cat dirlist.txt) -type d ! -name . -prune -o -mtime +$Num_days -name "*.csv" -exec rm {} \;

If there might be space or tab characters in directory names:

saveIFS="$IFS"
IFS=""
while read dir
do find "$dir" -type d ! -name . -prune -o -mtime +$Num_days -name "*.csv" -exec rm {} \;
done < dirlist.txt
IFS="$saveIFS"

If there are any newline characters in the directory names in your list, you have my sympathy and will not be able to use anything that has a newline separated list of directories (i.e., a text file) to store the directory list.

1 Like

Don, is there a reason to not simply override IFS in read's environment? Also, I may be overly paranoid, but I use -r to defend against backslashes.

while IFS= read -r dir ... 

Regards,
Alister

---------- Post updated at 12:29 PM ---------- Previous update was at 12:05 PM ----------

Because of -name . -prune not matching the current path, neither of those will work correctly.

A cd fixes the second suggestion:

while IFS= read -r d; do
(
    cd "$d" &&
    find . -type d ! -name . -prune -o -mtime +$Num_days -name '*.csv' -exec rm {} \;
)
done < dirlist.txt

If the paths are absolute, the subshell isn't needed.

An unwieldy solution that doesn't cd:

while IFS= read -r d; do
    find "$d" -type d \( -name "${d##*/}" -exec test "$d" = {} \; -o -prune \) -o -mtime +$Num_days -name '*.csv' -exec rm {} \;
done < dirlist.txt

For find implementations which support a -maxdepth primary (GNU, *BSD, etc) which simplifies restricting the traversal:

while IFS= read -r d; do
    find "$d" -maxdepth 1 -mtime +$Num_days -name '*.csv' -exec rm {} \;
done < dirlist.txt

Regards,
Alister

2 Likes

Using read's -r option is a good idea for this application, and just setting IFS for the read is also fine.

Depending on what I'm doing, I frequently just set IFS="" at the start of a script knowing that it won't affect the invoking shell execution environment. I saved and restored it here in case someone runs this code without putting it in a subshell environment; but just setting it for read has the same effect.

I was refining my recommendations when you posted your reply. Apologies for the mismatched post/quote.

Regards,
Alister

1 Like

Thank you both Alister and Don, but somehow I could not understand this line

while IFS= read -r d;

What is read -r helping us with?

read -r reads raw. If backslash escape sequences occur in your input, they will not be interpreted. E.g., if the input has \n , it will be treated as \n and not as a new-line. You'll get the input as is with no such interpretation by read .

1 Like

Hi ,

For the same code, I have an additional requirement. How can we have 2 parameters passed from the input file dirlist.txt? The two parameters would be 1. Teh directory path 2. The retention days

In short , now I wanna write a script taht would delete files older than the retention period. This period should be different for different directory paths. Till now I have written this piece of code.

 
while IFS= read -r d; do
(
        cd "$d"
        files2del=`ls *.dat `
        if [ ${files2del:-NO} = "NO" ]
        then 
                echo " No files to delete" | mailx -s " ATTENTION: NO FIES TO DELETE " $EMAILLIST
        else
                find "$d" -type d ! -name . -prune -o -mtime +$NUM_DAYS -name '*.dat' -print
                echo " Files to delete : $files2del " | mailx -s " Files deleted are from path $d " $EMAILLIST
        fi
 
)
done < dirlist.txt

The dirlist.txt can have sample input as

 
/path1 7
/path2 4
/path3 9

wherein 7,4 and 9 are retention days for which the files have to be kept and the files older than these should be deleted.

Change while IFS= read -r d; do in your script to while IFS= read -r d NUM_DAYS; do

Hi,
One more requirement for the same code. Along with the two parameters in the input file, I want to add one more parameter i.e. file extension. This parameter will be different for different paths.
Th input file will now look like this:

 
/path1 7 *.dat
/path2 4 *.csv
/path3 9 *.txt

For this, my command goes like this:

 
while IFS=' ' read -r dir NUM_DAYS file_extn; do
(
files2del=$(find . -type d ! -name . -prune -o -mtime +$NUM_DAYS -name '$file_extn' )                                                                                                      
        echo "$files2del" >> $LOGFILE 2>&1
)
 done < dirlist.txt

The problem is , it is not listing any file in the output. Can anyone please let me know where am I going wrong?

Try changing '$file_extn' in your find command to "$file_extn" .

Note that if there are any spaces, tabs, or newlines in any of the matched filenames, this may produce ambiguous output in $LOGFILE.

With single quotes, $file_extn won't be expanded and find will literally look for $file_extn instead of the string stored in the variable file_extn .

1 Like

Thats working. thanks!!! :slight_smile: