Search code examples
bash7zip

Reading filename using regexp in Bash


I am writing a bash script that calls an external program which generates a zip file of the form ########.zip which the '#'s# can be any decimal digit. There is no way to predict what the digits will be.

So far I have been able to get by using the regexp [0-9][0-9]*zip for things like moving the zip file around and unzipping it. However, now I need to add a txt file to the archive under the directory name ######## where the digits are the same as the archive name. So, if the archive is 12345678.zip, I need to add the file 12345678/foo.txt.

The only way I can see to do this is to create the directory '12345678' and copy the file there and then add the directory to the archive. However I can't figure out how to get the name of the archive so that I can create the directory.

The best I could some up with was to use backticks to assign the output of ls -l to a variable and then parse that to get just the filename part. But there has to be an easier way?

I am using 7z as the zip utility if it matters.

Thanks!


Solution

  • What you're asking for is not a regex but a "glob expression". In particular, if you want to match any number of digits, this can be done with a bash feature called extended globbing, or "extglobs". Read more at https://wiki.bash-hackers.org/syntax/pattern#extended_pattern_language.

    #!/usr/bin/env bash
    #              ^^^^- NOT sh
    
    shopt -s extglob
    for f in +([[:digit:]]).zip; do
      digits=${f%.zip}
      mkdir -p "$digits"
      echo hello >"$digits/foo.txt"
      zip "$f" "$digits/foo.txt"
    done
    

    In the parameter expansion digits=${f%.zip}, we take .zip off the end of the filename, and store the rest in the variable digits.


    Extending the above to work in a temporary directory and clean up after ourselves:

    #!/usr/bin/env bash
    #              ^^^^- NOT sh
    
    shopt -s extglob
    olddir=$PWD
    for f in +([[:digit:]]).zip; do
      digits=${f%.zip}
      tempdir="$(mktemp -d -t zipupdate.XXXXXX)"
      (
        cd "$tempdir" || exit
        mkdir -p "$digits" || exit
        echo hello >"$digits/foo.txt" || exit
        zip "$olddir/$f" "$digits/foo.txt"
      )
      rm -rf "$tempdir"
    done