U. Windl
U. Windl

Reputation: 4373

Is space special for pattern replacement in BASH?

Trying to remove leading and trailing blanks (SPC) from a variable's value using BASH, I feel that it does not work as described. I started with ${var%% } and ${var## } which should remove the longest pattern, but it seems only one blank is removed:

% val="  a  ";echo "|${val}|${val## }|"
|  a  | a  |
% val="  a  ";echo "|${val}|${val%% }|"
|  a  |  a |

Then I tried pattern matching which seems no remove nothing (they also should repeatedly remove a blank at the start or end of the value):

% val="  a  ";echo "|${val}|${val//# }|"
|  a  |  a  |
% val="  a  ";echo "|${val}|${val//% }|"
|  a  |  a  |

I feel I made some simple mistake, but it seems I'm sitting on my eyes: Why doesn't this work?

(I did already visit these answers, but they did not include "my solution": How to trim whitespace from a Bash variable?, How to remove space from string?)

Upvotes: 1

Views: 131

Answers (3)

U. Windl
U. Windl

Reputation: 4373

The problem is not that space (SPC) is handled specially; instead the problem is that pattern as used in the manual page of bash refers to pathname patterns, and not to regular expressions (unless extglob is set (which seems to be set by default)). That is ' *' is not a sequence of blanks, but instead it's a blank followed by anything.

The second problem is that "the longest matching pattern" is not determined by applying the match repeatedly until it fails, but only once. So if the pattern does not contain a *, % and %% produce the same result, just as # and ## do).

The problems in ${val//# } are:

  • // and /# cannot be combined; only one variant can be used.

  • pattern in ${parameter/pattern/string} is not a regular expression, but a pathname pattern (still).

This means that there is no benefit of using ${val/# } over using ${val# }. To remove all leading and trailing blanks the following (somewhat complex) code can be used:

val="  a  "
echo -n "|${val}"
while [ "_$val" != "_${val# }" ]
do
    val="${val# }"
done
while [ "_$val" != "_${val% }" ]
do
    val="${val% }"
done
echo "|${val}|"

So when using extglob, the solution looks like this (as pointed out in a previous answer):

al="  c  "
echo -n "|${val}"
val="${val##+( )}"
val="${val%%+( )}"
echo "|${val}|"

Upvotes: 0

pynexj
pynexj

Reputation: 20768

Space (0x20) is not special for both glob patterns and RE patterns.

For your problem I would take advantage of the extglob shell option:

[STEP 108] # shopt -s extglob
[STEP 109] # v='    foo    '
[STEP 110] # echo "|${v##+( )}|"
|foo    |
[STEP 111] # echo "|${v%%+( )}|"
|    foo|
[STEP 112] # echo "|${v%%+([[:blank:]])}|"
|    foo|
[STEP 113] # echo "|${v##+([[:blank:]])}|"
|foo    |
[STEP 114] #

Upvotes: 2

Mario
Mario

Reputation: 1032

You can archive this with sed.

To remove all leading empty space and empty spaces on the end of a string ...

sed 's/^[ \t]*//;s/[ \t]*$//'

Upvotes: -1

Related Questions