0

Trying to solve a code-golf problem using zsh, but my regex isn't working correctly.

Requirement
Given input string $1, delete all spaces immediately to the left of any ! character.

Examples:

Input ($1)          Expected Result:
" ! a! !b c!d !" => "! a!!b c!d!"
"a    !"         => "a!"
"!    a"         => "!    a"
"!    !    !"    => "!!!"
"! "             => "! "

I want a solution using zsh builtins only, this was the closest I could get:

<<<${(S)1// *!/!}

Unfortunately --try it online-- this results in

!!!b!d!
!    a
!!!
! 

As you can see, the first line has been mangled too enthusiastically by the * match. The zsh documentation (sec 5.9.2 & 5.9.3 of the Guide) is rather confusing on this point.

the + precedence operator also doesn't work :( <<<${(S)1//+( )!/!}

roblogic
  • 227

1 Answers1

2

That's not a regular expression in the usual regex syntax. It's a wildcard pattern. Sh wildcard patterns are less expressive than regular expressions. Ksh, bash and zsh have wildcard patterns that are as expressive as regular expressions, but with different syntaxes. See also Why does my regular expression work in X but not in Y?

The normal way to do this in zsh would be to turn on the extended_glob option (which pretty much everyone does pretty much all the time) and use the # wildcard which matches any number of the preceding (like * in the usual regex syntax).

setopt extended_glob
no_spaces_before_bang=${original_string// #!/!}

Your attempt failed for two reasons. First, * in wildcard patterns means “any sequence of characters”. Second, non-greedy matching would defeat the purpose: it would cause no space to be matched.

In ksh, and in bash after shopt -s extglob, and in zsh after setopt ksh_glob, you can use *( ) to match zero or more spaces, or +( ) to match one or more spaces. Either would do here.

For normal use, you'd just turn on extended_glob. For code golf, that's a pretty high price to pay. Maybe you could shrink space-bang to bang in a loop: repeat $#a a=${a/ !/!}. Or you could enter in the “zsh with extended_glob turned on” category, which is the language in which zsh completion functions are written.