Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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
share|improve this question
    
Minor note: I assume the double-hyphen is misplaced in the provided echo call? –  ChemicalRascal May 23 at 2:50
    
the double-hyphen is just a separator during the echo if that's what you mean. –  user68332 May 23 at 4:53

2 Answers 2

up vote 2 down vote accepted

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.

share|improve this answer
    
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 May 23 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? –  ChemicalRascal May 23 at 5:30
    
echo $SHELL = /bin/bash and bash --version = GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12) –  user68332 May 23 at 5:43
    
Should work, then... What's the output of for f in *; do echo -- "$f" "${f//[%>#]/}"; done? –  ChemicalRascal May 23 at 5:57
    
Beautiful, that one works! tyvm :) –  user68332 May 24 at 3:05

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
share|improve this answer
    
Thanks that works. Is there are way to do this while putting the regex pattern into an array? just curious. –  user68332 May 23 at 5:01

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.