stringzshwhitespaceglobparameter-expansion

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.


Solution

  • 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.