Bash array with folder paths and wildcards

5,960

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:

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.

Share:
5,960

Related videos on Youtube

JuanD
Author by

JuanD

Updated on September 18, 2022

Comments

  • JuanD
    JuanD almost 2 years

    enter image description here

    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
      Pankaj Goyal about 5 years
      I 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 be for rootpath in /srv/*/folderA; do find "$rootpath" [...]; done.
    • Kusalananda
      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
      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 the find command.
  • Kusalananda
    Kusalananda about 5 years
    No, just use /srv/*/folderA unquoted (or '/srv/'*'/folderA'). It will expand correctly at the time of assigning to dir_array.
  • JuanD
    JuanD about 5 years
    @XrXca your suggestion gave me the following message: [: /srv/level1A/folderA: binary operator expected
  • JuanD
    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
    JuanD about 5 years
    Your suggestion of using '/srv/'*'/folderA' worked for me. Thanks again.
  • XrXca
    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
    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 to set -- 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.