Tyler Crompton
Tyler Crompton

Reputation: 12652

Trimming whitespace from the ends of a string in Zsh

How does one remove any and all whitespace from the ends of a string in Zsh without spawning another process?

After looking at the documentation for expansion in Zsh (namely sections 14.3 Parameter Expansion and 14.8.1 Glob Operators)—also viewable via $ man zshexpn—I've written the following code:

${${var##[:space:]##}%%[:space:]##}

But Zsh doesn't seem to recognize the [:space:] glob operator. As per my understanding of the following statement in the documentation, it should:

In the expansions discussed below that require a pattern, the form of the pattern is the same as that used for filename generation; see Filename Generation. Note that these patterns, along with the replacement text of any substitutions, are themselves subject to parameter expansion, command substitution, and arithmetic expansion.

Is this a bug in Zsh or am I overlooking something?

For now, I'm just using ${${var## ##}%% ##} which at least substitutes any and all space characters at the ends.

I'm using Zsh 5.8.

Upvotes: 2

Views: 3087

Answers (3)

MarrekNožka
MarrekNožka

Reputation: 443

$ foo=" \n \t  ab dd ef   \n"

$ print $foo | xargs
ab dd ef

Upvotes: -2

chahu418
chahu418

Reputation: 415

You're really close.

  1. You need to modify your POSIX Basic Regular Expression character class syntax a bit
  2. You can simplify the expression by using the (MS) (matching substring) parameter expansion flags.
${(MS)var##[[:graph:]]*[[:graph:]]}

Solution

In the example below, the parameter we will strip leading/trailing whitespace from has the identifier var. Substitute this as necessary in your own code.

typeset var=$'\n\t   abc   def   \n\t'

Printing a parameter stripped of any leading/trailing whitespace:

print -n -- ${(MS)var##[[:graph:]]*[[:graph:]]}

Output: abc def

Upvotes: 6

Shawn
Shawn

Reputation: 52344

To quote from the zsh Glob Operator documentation:

Note that the square brackets are additional to those enclosing the whole set of characters, so to test for a single alphanumeric character you need ‘[[:alnum:]]’.

So just using [:space:] instead of [[:space:]] is your problem.


Aside:

There are a few ways to say "Match multiple copies of the previous thing" in zsh glob patterns. One, which you used, requires the EXTENDED_GLOB option to be turned on, is to use x##, which matches one or more occurrences of the pattern x (There's also x#, which matches zero or more occurrences of the pattern x). Another, which needs the KSH_GLOB option to work, is +(x), which also matches one or more occurrences of x (And *(x) matches 0 or more.) This style has the advantage of working with ksh and bash (The latter needs the extglob option enabled) and thus might be more familiar to someone coming from another shell.

So:

$ foo=$(printf " \t bar baz \t ") # Spaces and tabs before and after
$ printf ">%s<\n" "$foo"                               
>    bar baz     <
$ printf ">%s<\n" "${${foo##[[:space:]]##}%%[[:space:]]##}"
>bar baz<
$ setopt KSH_GLOB                                            
$ printf ">%s<\n" "${${foo##+([[:space:]])}%%+([[:space:]])}"
>bar baz<

removes all the leading and trailing whitespace characters like you want.

Also note this nesting of expansions is zsh-specific. It won't work in other shells, where the two deletions will require multiple steps.

Upvotes: 2

Related Questions