Bash array with folder paths and wildcards
Solution 1
The shell globbing pattern *
is not expanded within double quotes. This means that your loop
for d in "${dir_array[@]}";do
is looping over patterns. In your call to dir_delete
, you use the patterns unquoted, so they would be expand there (but it never gets there). The function would however only use the first word of whatever matches the pattern in the call to find
.
The real show-stopper is that the patterns will also be expanded in the [ -d $d ]
test, which is awkward since the -d
test only ever takes a single pathname. This is, in the end, why the script fails.
Instead, make sure that the patterns are properly expanded when assigning to dir_array
:
dir_array=(
/srv/*/folderA
/srv/level1D/*/folderA
)
If folderA
or some other part of the pathnames contains spaces etc., that part of the pathname should be quoted, but the *
should not be quoted.
Also remember to double quote the expansions of all variables, unless you know the contexts in which they don't need to be quoted.
There may be quoting issues with the following lines:
if [ -d $d ];then
find $1 -type f -mindepth 1 -maxdepth 1 -mtime +$FILEAGE -delete -print
dir_delete $d
Also consider using printf
rather than echo
when outputting variable data.
Related:
- Why does my shell script choke on whitespace or other special characters?
- When is double-quoting necessary?
- Why is printf better than echo?
Solution 2
You can do this with the extended glob option which will allow you to expand wildcards within a variable with *()
shopt -s extglob
dir_array=(
"/srv/*(*)/folderA"
"/srv/level1D/*(*)/folderA"
)
The expansion would happen at the for d in ... which I think is the correct point.
Related videos on Youtube
JuanD
Updated on September 18, 2022Comments
-
JuanD almost 2 years
Given the above folder paths, I am trying to write a script that will delete files older than a certain amount of days. I am trying to use some more advanced techniques than just hard coding the paths (my real life examples has ALOT more folders). Here is what I have so far:
#!/bin/bash FILEAGE=15 #Array of folders to clean dir_array=( "/srv/*/folderA" "/srv/level1D/*/folderA" ) #function to be used for deleting files. Needs to be called with a path function dir_delete() { echo "Deleting:" find $1 -type f -mindepth 1 -maxdepth 1 -mtime +$FILEAGE -delete -print echo "" } echo "##### Looping through dir_array array and using dir_delete function to delete files older than $FILEAGE days #####" for d in "${dir_array[@]}";do if [ -d $d ];then echo "##### Deleting all files older than $FILEAGE in $d #####" dir_delete $d else echo "##### Did not find directory: \$d #####" echo "" fi done
My script is returning that it can't find the directories like so:
Did not find directory: /srv/*/folderA
Note: There will be files throughout these folders under 15 days that can't be deleted.
Update
Using @Kusalananda suggestion
'/srv/'*'/folderA'
fixed it for me. I'm leaving the wrong code in my original post above should anyone want to see what I was doing wrong.-
Pankaj Goyal about 5 yearsI don't think you can provide multiple root paths with e. g.
find /path/*/to/places
. The easiest option for me off the top of my head would befor rootpath in /srv/*/folderA; do find "$rootpath" [...]; done
. -
Kusalananda about 5 years
*
will not be expanded when it's quoted. Do you know that the mtime of a directory only updates when a file is deleted or created in it? -
Kusalananda about 5 years@DopeGhoti
find
does support multiple search paths, but the*
is expanded in the wrong place. It should be expanded in the array, not in the execution of thefind
command.
-
-
Kusalananda about 5 yearsNo, just use
/srv/*/folderA
unquoted (or'/srv/'*'/folderA'
). It will expand correctly at the time of assigning todir_array
. -
JuanD about 5 years@XrXca your suggestion gave me the following message:
[: /srv/level1A/folderA: binary operator expected
-
JuanD about 5 years@Kusalananda I think your solution is working. Testing a few more scenarios now. Thank you much. I dont know if I can accept a sub comment as an answer.
-
JuanD about 5 yearsYour suggestion of using
'/srv/'*'/folderA'
worked for me. Thanks again. -
XrXca about 5 years@Kusalananda: Either should work, I tend to use the () construct when I'm using extglobs just because it's obvious it's not "normal", when a block of code is copied from one script to another, and I use the @(A|B|C).construct quite a bit.so it's "similar' in my mind.
-
Kusalananda about 5 years@XrXca I'm just looking at the code and noticing that the only thing the script is using
bash
for (and not/bin/sh
) is that single array. Changing it toset -- patterns
would allow it to run under/bin/sh
(the loop would loop over"$@"
instead). My personal preference is to use/bin/sh
, which is why I commented the way I did. Apart from that, you may well be right.