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 replacing, from a feed title, all chars except letters and digits with a dash to use the result as a safe filename for any filesystem:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

However I'd like to condense all repeating dashes with a single one like Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

I found I can achieve it using a two pass substitution:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

I thought I could do it in a single pass like:

$ echo ${t//[^A-Za-z0-9]+/-}

but it doesn't work.

Any clue?

Note: I don't want to go with sed or other tools

share|improve this question
add comment

3 Answers

up vote 2 down vote accepted

You need something more powerful than traditional shell wildcards. In bash, set the extglob option, which gives you access to regular expressions in glob patterns through an unusual syntax inherited from ksh.

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}
share|improve this answer
    
Thanks, there was a comment from fered under jw013 answer with this solution. Some info on compatibility with other shells of this syntax? I'm not concerned about it that much, just to know more about shopt and which shells support it. –  neurino Nov 1 '11 at 11:41
    
@neurino shopt is specific to bash. The pattern syntax that it enables is always available in all ksh variants. In zsh, this syntax must be enabled with setopt ksh_glob. POSIX has no such feature, its wildcards are less powerful than regexps. Shells other than bash/ksh/zsh, which in practice mostly means ash nowadays, tend to stick to POSIX wildcards. –  Gilles Nov 1 '11 at 13:53
    
well, at this point I prefer more compatibility and flexibility with a bit more overhead: echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'. I accept your answer as it exactly do what asked in question. –  neurino Nov 1 '11 at 20:44
    
@neurino If you want portability to other shells, then you can go with glenn jackman's answer. By the way, note that the ${var/PATTERN/REPLACEMENT} construct is also specific to ksh/bash/zsh. –  Gilles Nov 1 '11 at 21:07
    
I prefer sed as I know better its syntax and behavior, I can easily add a statement to remove starting/trailing dashes, I don't need to care about \n char. Is sed way less available than tr? –  neurino Nov 1 '11 at 21:12
show 1 more comment

tr is a good tool for this job

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}
share|improve this answer
    
Thank you, +1, I never recall about tr... However I was trying to get it done in Bash, otherwise I'd go with sed: echo "$t" | sed -r 's/[^A-Za-z0-9]+/-/g' –  neurino Oct 31 '11 at 18:07
add comment

If you want to stay with pure bash, you'll have to settle for the two-pass solution. Bash string substitutions use globs, as in pathname expansion, and not regular expressions. The only special characters in globs are *, ?, and [], whose rough equivalents in regular expressions are .*, ., and []. Take a look at the Wooledge wiki and the bash(1) man page sections on Parameter Expansion and Pathname Expansion for more info.

Just as a comment, a two-pass expansion in pure bash is still likely to be faster than trying to do the same thing by invoking an external program, so I wouldn't worry about it too much.

share|improve this answer
    
Thanks, I'll check the link. My worry is I have to do this work more than once in the whole script so my only concern was about having the same code repeated over and over compromising readability. Anyway I'm coming up with a polite solution I'm going to post. Cheers –  neurino Oct 30 '11 at 23:34
    
You could put that code in a function to avoid repeating code. –  jw013 Oct 30 '11 at 23:38
    
It's what I'm doing but, as you know, bash functions cannot return strings... or, at least, it was what I thought before 10 minutes ago :) –  neurino Oct 30 '11 at 23:48
4  
Here are some examples with do-s and-don't-s -- Bash Extended Globbing .. For the above example, it would be: shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}" –  Peter.O Oct 31 '11 at 2:19
1  
@fered: thank you, very interesting, I'll check it out. Your link url has an extra char and returns a 404, the working one is Bash Extended Globbing –  neurino Oct 31 '11 at 9:40
show 1 more comment

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.