2

I'm looking to loop through a directory of files and match them with regex, then subsitute the appropriate character:

#somefile.txt
%somefile.txt
>somefile.txt

# arr=(\% \> \#) ;for f in *; do echo -- "$f" "${f//${arr[@]}/}"; done

should echo:

#somefile.txt -- somefile.txt
%somefile.txt -- somefile.txt
>somefile.txt -- somefile.txt

instead i get:

#somefile.txt -- #somefile.txt
%somefile.txt -- %somefile.txt
>somefile.txt -- >somefile.txt

which results in no change, nada, zilch.

the array works if i use arr(\%) with a single character, which defeats the whole purpose.

#somefile.txt -- #somefile.txt
%somefile.txt -- somefile.txt
>somefile.txt -- >somefile.txt
2
  • Minor note: I assume the double-hyphen is misplaced in the provided echo call? Commented May 23, 2014 at 2:50
  • the double-hyphen is just a separator during the echo if that's what you mean.
    – user68332
    Commented May 23, 2014 at 4:53

2 Answers 2

3

The core issue here is that the ${PARAMETER/PATTERN/STRING} expansion PATTERN doesn't accept an array as PARAMETER does, and hence ${arr[@]} expands to "% > #".

The effect of this can be seen if you add a few files:

# touch "% > #somefile.txt"
# touch "%>#somefile.txt"

Your command would result in:

#somefile.txt -- #somefile.txt
%somefile.txt -- %somefile.txt
>somefile.txt -- >somefile.txt
%>#somefile.txt -- %>#somefile.txt
% > #somefile.txt -- somefile.txt

The last line hopefully showing exactly what is going on.

Another has suggested using piping $f through sed and doing magic there, and that'd be a good option as it would allow for more complex transformations, however assuming that you only want to remove leading characters, using bash's ${PARAMETER#WORD} parameter expansion would suffice:

# gpat=" ;for f in *; do echo -- "$f" "${f#$gpat}"; done

For myself, that line results in:

>somefile.txt -- somefile.txt
#somefile.txt -- somefile.txt
%somefile.txt -- somefile.txt
%>#somefile.txt -- >#somefile.txt
% > #somefile.txt --  > #somefile.txt

I'm calling the pattern $gpat as that's what it is - a bash globbing pattern. It's not a regular expression.

Note that ${PAR#WORD} is non-greedy, and matches the shortest possible pattern - ${PAR##WORD} matches the longest possible pattern. However, with gpat="[%>#]", that will only match one character anyway:

# gpat="[%>#]" ;for f in *; do echo "$f" -- "${f##$gpat}"; done

%somefile.txt -- somefile.txt
>somefile.txt -- somefile.txt
#somefile.txt -- somefile.txt
%>#somefile.txt -- >#somefile.txt
% > #somefile.txt --  > #somefile.txt

If we have the extglob shell option set (check with shopt extglob, set with shopt -s extglob, unset with shopt -u extglob - it's probably set if you have a reasonable bashrc), we can use the extended globbing patterns:

# gpat="+([%>#])" ;for f in *; do echo "$f" -- "${f##${gpat}}"; done

%somefile.txt -- somefile.txt
>somefile.txt -- somefile.txt
#somefile.txt -- somefile.txt
%>#somefile.txt -- somefile.txt
% > #somefile.txt --  > #somefile.txt

Notably, though, in all the above examples, "% > #somefile.txt" becomes " > #somefile.txt" due to the space following the % not being captured by the pattern.

Of course, ${PARAMETER/PATTERN/STRING} (and the // similar) also accept globbing patterns, but I've only realised this (literally) as I type these words, and I can't really be bothered going back through the above to adjust it to reflect that. Either way, it works exactly the same, and of course you'd only want to use PAR/PAT/ to remove middle-of-string characters.

6
  • This works nicely on linux, unfortunately on OS X it seems to ignore it - which was where I intend using this. Thank you very much for the in-depth and insightful explanation; it's always nice to learn something :) (although I can't up-vote I give you a +2)
    – user68332
    Commented May 23, 2014 at 4:59
  • Ah, of course - I glossed over the OS X tag, sorry. After a quick bit of searching, it appears that the default OS X shell might not be /bin/bash - Could you provide the output of echo $SHELL and bash --version? Commented May 23, 2014 at 5:30
  • echo $SHELL = /bin/bash and bash --version = GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
    – user68332
    Commented May 23, 2014 at 5:43
  • Should work, then... What's the output of for f in *; do echo -- "$f" "${f//[%>#]/}"; done? Commented May 23, 2014 at 5:57
  • Beautiful, that one works! tyvm :)
    – user68332
    Commented May 24, 2014 at 3:05
3

this seems to work for me ...

$ ls
#somefile.txt  %somefile.txt  >somefile.txt

$ for f in *; do j=`echo $f | sed -e's/[\%\#\>]//g'`; echo "$f -- $j"; done
#somefile.txt -- somefile.txt
%somefile.txt -- somefile.txt
>somefile.txt -- somefile.txt
1
  • Thanks that works. Is there are way to do this while putting the regex pattern into an array? just curious.
    – user68332
    Commented May 23, 2014 at 5:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .