How to trim whitespace from a Bash variable?
Asked Answered
C

52

1339

I have a shell script with this code:

var=`hg st -R "$path"`
if [ -n "$var" ]; then
    echo $var
fi

But the conditional code always executes, because hg st always prints at least one newline character.

  • Is there a simple way to strip whitespace from $var (like trim() in PHP)?

or

  • Is there a standard way of dealing with this issue?

I could use sed or AWK, but I'd like to think there is a more elegant solution to this problem.

Cervix answered 15/12, 2008 at 21:24 Comment(7)
Related, if you wanted to trim space on an integer and just get the integer, wrap with $(( $var )), and can even do that when inside double quotes. This became important when I used the date statement and with filenames.Astaire
"Is there a standard way of dealing with this issue?" Yes, use [[ instead of [. $ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1Preference
If it helps, at least where am testing it on Ubuntu 16.04. Using the following matches trim in every way: echo " This is a string of char " | xargs. If you however have a single quote in the text you can do the following: echo " This i's a string of char " | xargs -0. Note that I mention latest of xargs (4.6.0)Pinot
The condition isn't true because of a newline as backticks swallow the last newline. This will print nothing test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi, this however will test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi - so there must be more than just a newline at the end.Hourigan
A="123 4 5 6 "; B=echo $A | sed -r 's/( )+//g';Veraveracious
This question would benefit from one of the answers being excepted.Ursi
As @mecki alluded to, if the code exactly as given does not work, that's because output isn't solely a newline; perhaps there's a carriage return or other unprintable character? (All the other answers instructing you to use "trim" are entirely missing the point.)Poultry
M
1565

A simple answer is:

echo "   lol  " | xargs

Xargs will do the trimming for you. It's one command/program, no parameters, returns the trimmed string, easy as that!

Note: this doesn't remove all internal spaces so "foo bar" stays the same; it does NOT become "foobar". However, multiple spaces will be condensed to single spaces, so "foo bar" will become "foo bar". In addition it doesn't remove end of lines characters.

Mindful answered 15/12, 2008 at 21:24 Comment(22)
Nice. This works really well. I have decided to pipe it to xargs echo just to be verbose about what i'm doing, but xargs on its own will use echo by default.Dielu
On Windows this is a very good solution. I usually use gnu utils binaries without bash (eg. cygwin, unxutils, custom builds, mingw, git for windows, gnuwin32...) and sed and awk support is not very consistent across these.Bereave
Also, both sed, awk and bash are tools so old that they use a syntax that can feel "arcane". People who are not often using them, wanting to do a simple string manipulation, would easily feel overwhelmed by ' or " or \ or / or // or ^ or $, asking themselves just which one was it? Can't believe there is no more straightforward solution to this simple thing... There is.Bereave
An other reason for using xargs is why introduce dependency? xargs is a binary that can be used with other shells (I even managed to use it with cmd.exe). Since it's quite straightforward it doesn't depend on any learning curve as a tool for being immediately useful either.Bereave
Using grep . can further prevent the output of empty lines.Singlehearted
Nice trick, but be carefull, you can use it for one-line string but −by xargs design− it will not just do triming with multi-line piped content. sed is your friend then.Darbie
The only problem with xargs is that it will introduce a newline, if you want to keep the newline off I would recommend sed 's/ *$//' as an alternative. You can see the xargs newline like this: echo -n "hey thiss " | xargs | hexdump you will notice 0a73 the a is the newline. If you do the same with sed: echo -n "hey thiss " | sed 's/ *$//' | hexdump you will see 0073, no newline.Lothaire
Careful; this will break hard if the string to xargs contains surplus spaces in between. Like " this is one argument ". xargs will divide into four.Lemuroid
This is bad. 1. It will turn a<space><space>b into a<space>b. 2. Even more: it will turn a"b"c'd'e into abcde. 3. Even more: it will fail on a"b, etc.Fricassee
@Mindful Why did you remove the (very important) warning from this answer? This doesn't change the behaviour of xargs...Belga
@DanielAlder added the note, I removed that because it was too long, notes have to be short - a too long answer can be considered wrong sometimes because of TL;DR :)Mindful
I've been using xargs to trim simple strings until I discovered it fails on strings with a single quote or multiple internal spaces (as was warned above). I can't recommend this method.Garceau
This is a very quick solution when trimming simple single-word strings. I used this to strip sqlite's -line output, where the column name has useless padding script-wise.Shaniqua
I understand the downsides of this, but it's great for when you know the expected output is going to be just one value, e.g. from wc -l. I use this for removing leading spaces (which seem to be included by the macOS version of wc): ls .. | wc -l | xargsEu
@AlexanderMills thanks for the sincerity.... maybe try with this? A=$(echo " lol " | xargs); echo $AMindful
@makevoid, as for "added the note", the note you've added is very different from the previous warning. The previous warning said that this approach changes inner spaces (I'd better say: ruins them). While your note just says that the inner spaces wouldn't be fully removed (almost the opposite thing).Fricassee
To continuously trim all stdin lines, run it through xargs -l.Chandless
this is bad, it removes backslash from stringParette
this is misuse. xargs is made for another purpose and might change trimming behaviour anytime. it's bad advice to use this for trimming. As @Fricassee already pointed out: it already has some weird behaviours you would not expect when trimming strings!Hastate
xargs <<< " test teste grdgdft"Caudle
This answer doesn't remove \r or CR from the end of a variable.Crompton
This removes surrounding quotes from the content.Salmon
P
1202

Let's define a variable containing leading, trailing, and intermediate whitespace:

FOO=' test test test '
echo -e "FOO='${FOO}'"
# > FOO=' test test test '
echo -e "length(FOO)==${#FOO}"
# > length(FOO)==16

How to remove all whitespace (denoted by [:space:] in tr):

FOO=' test test test '
FOO_NO_WHITESPACE="$(echo -e "${FOO}" | tr -d '[:space:]')"
echo -e "FOO_NO_WHITESPACE='${FOO_NO_WHITESPACE}'"
# > FOO_NO_WHITESPACE='testtesttest'
echo -e "length(FOO_NO_WHITESPACE)==${#FOO_NO_WHITESPACE}"
# > length(FOO_NO_WHITESPACE)==12

How to remove leading whitespace only:

FOO=' test test test '
FOO_NO_LEAD_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//')"
echo -e "FOO_NO_LEAD_SPACE='${FOO_NO_LEAD_SPACE}'"
# > FOO_NO_LEAD_SPACE='test test test '
echo -e "length(FOO_NO_LEAD_SPACE)==${#FOO_NO_LEAD_SPACE}"
# > length(FOO_NO_LEAD_SPACE)==15

How to remove trailing whitespace only:

FOO=' test test test '
FOO_NO_TRAIL_SPACE="$(echo -e "${FOO}" | sed -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_TRAIL_SPACE='${FOO_NO_TRAIL_SPACE}'"
# > FOO_NO_TRAIL_SPACE=' test test test'
echo -e "length(FOO_NO_TRAIL_SPACE)==${#FOO_NO_TRAIL_SPACE}"
# > length(FOO_NO_TRAIL_SPACE)==15

How to remove both leading and trailing spaces--chain the seds:

FOO=' test test test '
FOO_NO_EXTERNAL_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_EXTERNAL_SPACE='${FOO_NO_EXTERNAL_SPACE}'"
# > FOO_NO_EXTERNAL_SPACE='test test test'
echo -e "length(FOO_NO_EXTERNAL_SPACE)==${#FOO_NO_EXTERNAL_SPACE}"
# > length(FOO_NO_EXTERNAL_SPACE)==14

Alternatively, if your bash supports it, you can replace echo -e "${FOO}" | sed ... with sed ... <<<${FOO}, like so (for trailing whitespace):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"
Predestine answered 15/12, 2008 at 21:24 Comment(15)
To generalize the solution to handle all forms of whitespace, replace the space character in the tr and sed commands with [[:space:]]. Note that the sed approach will only work on single-line input. For approaches that do work with multi-line input and also use bash's built-in features, see the answers by @bashfu and @GuruM. A generalized, inline version of @Nicholas Sushkin's solution would look like this: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")Tights
If you do that often, appending alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'" to your ~/.profile allows you to use echo $SOMEVAR | trim and cat somefile | trim.Roofdeck
I wrote a sed solution that only uses a single expression rather than two: sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'. It trims leading and trailing whitespace, and captures any whitespace-separated sequences of non-whitespace characters in the middle. Enjoy!Kerch
@VictorZamanian Your solution does not work if the input contains only whitespace. The two-pattern sed solutions given by MattyV and instanceof me work fine with whitespace only input.Debit
@Debit Fair point. I suppose the single expression could be made conditional, with |, so as to keep it as one single expression, rather than several.Kerch
The line echo -e "${FOO}" | wc -m counts one more character because echo adds it. Try echo -ne "${FOO}" | wc -m and get the correct count.Semiramis
An inconvenient of this method is that it uses sed, whose behavior depends on the system. In particular, sed from Solaris 10 doesn't support [[:space:]].Alicea
I was very confused by the -e option of the echo command. I'm working on a Mac and it's not recognised. Or at least not documented in the man page. FOO=' test test test ' FOO_NO_WHITESPACE=$(echo "${FOO}" | tr -d '[:space:]') works fine for me.Dickerson
Note that tr -d "[:space:]" removes both horizontal and vertical whitespace characters (=newlines). To remove just horizontal whitespace characters simply use tr -d " ".Sold
Bumped you to 1000 since it had been sitting at 999 for some time :-DHeigl
bash is evil lulz for requiring so long an answer to "how to trim whitespace" isn't there some coreutils program that can do this? xargs is one I guess but not explicitly made for that.Confirmand
It takes lots of examples because trimming a whitespace is about as easy as trimming your own hair.Hydrus
Except in this world it's easier to trim the back, you can barely see the top, and your clippers behave wildly differently depending on where you use them.Hydrus
Try sed -e 's/^[[:space:]]*//;s/[[:space:]]*$//' and eliminate the repeated edit (-e) flags. ... Better yet, use extended flags (-E) and try a single expression sed -E 's/^[ ]*|[ ]*$//;'!Sirrah
👍️ borrowing some lines to make 1 that removes trailing ',' chars, I often had this problem but now it works flawlessly, thanks for this flawless answ! echo ', a , b , c,' | sed -e 's/,*$//' ##, a , b , cDeandra
S
521

A solution that uses Bash built-ins called wildcards:

var="    abc    "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"   
printf '%s' "===$var==="

Here's the same wrapped in a function:

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"
    printf '%s' "$var"
}

You pass the string to be trimmed in quoted form, e.g.:

trim "   abc   "

This solution works with POSIX-compliant shells.

Reference

Supposititious answered 15/12, 2008 at 21:24 Comment(16)
I tried your solution but using local var="$@" compresses multiple internal-spaces into a single space. So a string with multiple-space like " a<multi-space>b c d " would translate to single-spaced "a b c d". The only way as suggested by @mkelement is to quote the input variable directly i.e. trim "$string" in which case $@ expands to a single parameter anyway.Cilice
Clever! This is my favorite solution as it uses built-in bash functionality. Thanks for posting! @San, it's two nested string trims. E.g., s=" 1 2 3 "; echo \""${s%1 2 3 }"\" would trim everything from the end, returning the leading " ". Subbing 1 2 3 with [![:space:]]* tells it to "find the first non-space character, then clobber it and everything after". Using %% instead of % makes the trim-from-end operation greedy. This is nested in a non-greedy trim-from-start, so in effect, you trim " " from the start. Then, swap %, #, and * for the end spaces. Bam!Countervail
I haven't found any unwanted side effects, and the main code works with other POSIX-like shells too. However, under Solaris 10, it doesn't work with /bin/sh (only with /usr/xpg4/bin/sh, but this is not what will be used with usual sh scripts).Alicea
Well, let's say that this is also OK for Solaris 10 even without bash, where one can put { a=a; : ${a%b}; } 2> /dev/null || exec /usr/xpg4/bin/sh -- "$0" ${1+"$@"} at the beginning of the script to use a compatible shell.Alicea
Much better solution than using sed, tr etc., since it's much faster, avoiding any fork(). On Cygwin difference in speed is orders of magnitude.Zoe
@San At first I was stumped because I thought these were regular expressions. They are not. Rather, this is Pattern Matching syntax (gnu.org/software/bash/manual/html_node/Pattern-Matching.html, wiki.bash-hackers.org/syntax/pattern) used in Substring Removal (tldp.org/LDP/abs/html/string-manipulation.html). So ${var%%[![:space:]]*} says "remove from var its longest substring that starts with a non-space character". That means you are left only with the leading space(s), which you subsequently remove with ${var#... The following line (trailing) is the opposite.Carom
This is overwhelmingly the ideal solution. Forking one or more external processes (e.g., awk, sed, tr, xargs) merely to trim whitespace from a single string is fundamentally insane – particularly when most shells (including bash) already provide native string munging facilities out-of-the-box.Olodort
Wouldn't it be easier to just var=${var##+([[:space:]])} and var=${var%%+([[:space:]])}? The bash pattern +(...) means one or more of whatever is in the parens. That way you don't have to do the weird double-expansion.Improve
Oh, and I guess the solution in my last comment requires shopt -s extglob.Improve
@CecilCurry Getting the result of trim into a variable would still require command substitution and a subshell, e.g. var=$(trim " abc "), so in a shell script the advantage over piping is minimal, right?Growl
downvote because this doesn't give any explanation. it doesn't work for me and I just don't know why (need \r\n removed that was added by recent winepath)Wheaten
@akhan, Yes, quotes are NOT needed in variable assignment in BASH: stackoverflow.com/questions/3958681/… unix.stackexchange.com/questions/97560/…Poliard
Great. Thanks for the function - added to my local library of valuabe functions.Takamatsu
You can add support for piped input, stdin, etc. with a single line addition. After the line local var="$*", add a new line (( $# )) || read -re var. Now this function can be called as in echo " hello " | trim to produce just hello.Quentinquercetin
@Quentinquercetin why -e?Helmut
@Helmut Probably just habit! Since input is not coming from a keyboard in this case, I'm not sure -e (Readline support on my system) provides any benefit and might actually be slower than native parsing.Quentinquercetin
I
123

In order to remove all the spaces from the beginning and the end of a string (including end of line characters):

echo $variable | xargs echo -n

This will remove duplicate spaces also:

echo "  this string has a lot       of spaces " | xargs echo -n

Produces: 'this string has a lot of spaces'
Islet answered 15/12, 2008 at 21:24 Comment(5)
Basically the xargs removes all the delimiters from the string. By default it uses the space as delimiter (this could be changed by the -d option).Islet
Why do you need echo -n at all? echo " my string " | xargs has the same output.Bigham
echo -n removes the end of line as wellIslet
Does not do what the question asks. For example, all spaces in the input (other than leading/trailing) will be converted to a single space. When you try to use encoding hacks like this you are relying on a number of factors you can't be sure about. Instead, use tools that are designed for processing data as data. I don't know why so many developers are so attracted to encoding bug minefields.Spurling
Removes backslashes.Heiress
K
112

Bash has a feature called parameter expansion, which, among other things, allows string replacement based on so-called patterns (patterns resemble regular expressions, but there are fundamental differences and limitations). [flussence's original line: Bash has regular expressions, but they're well-hidden:]

The following demonstrates how to remove all white space (even from the interior) from a variable value.

$ var='abc def'
$ echo "$var"
abc def
# Note: flussence's original expression was "${var/ /}", which only replaced the *first* space char., wherever it appeared.
$ echo -n "${var//[[:space:]]/}"
abcdef
Kerianne answered 15/12, 2008 at 21:24 Comment(13)
Or rather, it works for spaces in the middle of a var, but not when I attempt to anchor it at the end.Hurlow
Does this help any? From the manpage: "${parameter/pattern/string} [...] If pattern begins with %, it must match at the end of the expanded value of parameter."Stratum
@Ant, so they're not really regular expressions, but something similar?Hurlow
They're regex, just a strange dialect.Stratum
to replace all matches echo ${var// }Calmative
${var/ /} removes the first space character. ${var// /} removes all space characters. There's no way to trim just leading and trailing whitespace with only this construct.Mediation
@PaulTomblin: the bash manual calls them patterns and while they share some features with regular expressions, they're sufficiently different to warrant not calling them regular expressions so as to avoid confusion.Tights
I think that was the point I was making - that flussence was calling them regular expressions, but they're not.Hurlow
Bash patterns are not regular expressions. Bash supports both Bash pattern syntax and genuine regular expressions (e.g., via the builtin =~ operator introduced with Bash 3.0.0). The two have nothing to do with one another.Olodort
Bash “patterns” are regular expressions, only with a different syntax than “POSIX (extended) regular expressions”. You are right in saying that they are incompatible, and that Bash expects them in different places (the former in parameter substitutions, the latter after the =~ operator). As for their expressiveness: with “extended pattern matching operators” (shell option extglob), the only features that POSIX regexps have and Bash patterns lack, are ^ and $ (compare man bash § “Pattern Matching” with man 7 regex).Hazel
FWIW, var="${var//[[:space:]]/}" works fine in re-assigning the trimmed contents back to the original variable.Cornellcornelle
thanks works great for inline use of for $i <array of arguments some with spaces> do { some command expecting $i arg with spaces > path/output-catpure-$i.log }; liked that i could use path/ouput-capture-$(echo -n "${i//[[:space:]]/-}").log to get with a dash replacing spaces instead of removing them.Avernus
$ echo -n "${var//[[:space:]]/}" is what worked for cleaning up the stored vars in my case. There were no visible spaces, must have been some kind of hidden return or other character that matched a space...Darbies
O
79

Strip one leading and one trailing space

trim()
{
    local trimmed="$1"

    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"

    echo "$trimmed"
}

For example:

test1="$(trim " one leading")"
test2="$(trim "one trailing ")"
test3="$(trim " one leading and one trailing ")"
echo "'$test1', '$test2', '$test3'"

Output:

'one leading', 'one trailing', 'one leading and one trailing'

Strip all leading and trailing spaces

trim()
{
    local trimmed="$1"

    # Strip leading spaces.
    while [[ $trimmed == ' '* ]]; do
       trimmed="${trimmed## }"
    done
    # Strip trailing spaces.
    while [[ $trimmed == *' ' ]]; do
        trimmed="${trimmed%% }"
    done

    echo "$trimmed"
}

For example:

test4="$(trim "  two leading")"
test5="$(trim "two trailing  ")"
test6="$(trim "  two leading and two trailing  ")"
echo "'$test4', '$test5', '$test6'"

Output:

'two leading', 'two trailing', 'two leading and two trailing'
Overtax answered 15/12, 2008 at 21:24 Comment(4)
This will trim only 1 space character. So the echo results in 'hello world ', 'foo bar', 'both sides 'Lettuce
@Lettuce I added a better option.Overtax
This helped me, but the question specifically asked about a trailing newlin which this does not remove.Shana
@andrewlorien The original question was wrong about the trailing newline. Its real problem was some other trailing character. (The shell's `` or $() notation removes all trailing newlines)Poultry
F
53

You can trim simply with echo:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'
Flews answered 15/12, 2008 at 21:24 Comment(5)
This collapses multiple contiguous spaces into one.Scriven
Did you try it when foo contains a wildcard? e.g., foo=" I * have a wild card"... surprise! Moreover this collapses several contiguous spaces into one.Gerbold
This is an excellent solution if you: 1. want no spaces on the ends 2. want only one space between each word 3. are working with a controlled input with no wildcards. It essentially turns a badly formatted list into a good one.Forseti
Good reminder of the wildcards @Gerbold +1. Still an excelente solution, Vamp. +1 to you too.Archiepiscopal
Despite the problem with wildcharts and space in the middle, it is simple and does not need an external tool.Carmelinacarmelita
C
53

From Bash Guide section on globbing

To use an extglob in a parameter expansion

 #Turn on extended globbing  
shopt -s extglob  
 #Trim leading and trailing whitespace from a variable  
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}  
 #Turn off extended globbing  
shopt -u extglob  

Here's the same functionality wrapped in a function (NOTE: Need to quote input string passed to function):

trim() {
    # Determine if 'extglob' is currently on.
    local extglobWasOff=1
    shopt extglob >/dev/null && extglobWasOff=0 
    (( extglobWasOff )) && shopt -s extglob # Turn 'extglob' on, if currently turned off.
    # Trim leading and trailing whitespace
    local var=$1
    var=${var##+([[:space:]])}
    var=${var%%+([[:space:]])}
    (( extglobWasOff )) && shopt -u extglob # If 'extglob' was off before, turn it back off.
    echo -n "$var"  # Output trimmed string.
}

Usage:

string="   abc def ghi  ";
#need to quote input-string to preserve internal white-space if any
trimmed=$(trim "$string");  
echo "$trimmed";

If we alter the function to execute in a subshell, we don't have to worry about examining the current shell option for extglob, we can just set it without affecting the current shell. This simplifies the function tremendously. I also update the positional parameters "in place" so I don't even need a local variable

trim() {
    shopt -s extglob
    set -- "${1##+([[:space:]])}"
    printf "%s" "${1%%+([[:space:]])}" 
}

so:

$ s=$'\t\n \r\tfoo  '
$ shopt -u extglob
$ shopt extglob
extglob         off
$ printf ">%q<\n" "$s" "$(trim "$s")"
>$'\t\n \r\tfoo  '<
>foo<
$ shopt extglob
extglob         off
Cilice answered 15/12, 2008 at 21:24 Comment(6)
as you've observed trim() removes leading and trailing whitespace only.Cilice
As mkelement has already noted you need to pass the function parameter as a quoted string i.e. $(trim "$string") instead of $(trim $string). I've updated the code to show the correct usage. Thanks.Cilice
As much as I appreciate knowing about the shell options, I don't think the end result is more elegant than simply doing 2 pattern substitutionsCreed
Note that (with a recent enough version of Bash?), you can simplify the mechanism for restoring the option extglob, by using shopt -p: simply write local restore="$(shopt -p extglob)" ; shopt -s extglob at the beginning of your function, and eval "$restore" at the end (except, granted, eval is evil…).Hazel
Great solution! One potential improvement: it looks like [[:space:]] could be replaced with, well, a space: ${var##+( )} and ${var%%+( )} work as well and they are easier to read.Cullin
While using builtin functionality seems nice, extglob is very slow in my experience.Stanislaw
H
35

I've always done it with sed

  var=`hg st -R "$path" | sed -e 's/  *$//'`

If there is a more elegant solution, I hope somebody posts it.

Hurlow answered 15/12, 2008 at 21:24 Comment(8)
could you explain the syntax for sed?Wessels
The regular expression matches all trailing whitespace and replaces it with nothing.Hurlow
How about leading whitespaces?Youngstown
This strips all trailing whitespace sed -e 's/\s*$//'. Explanation: 's' means search, the '\s' means all whitespace, the '*' means zero or many, the '$' means till the end of the line and '//' means substitute all matches with an empty string.Painful
In 's/ *$//', why are there 2 spaces before the asterisk, instead of a single space? Is that a typo?Wilfordwilfred
@Wilfordwilfred because you don't want to match unless there is at least one space. 's/ +$//' might work as well.Hurlow
@PaulTomblin sure, I get that we only want to match one or more spaces, but wouldn't 's/ *$//' do the trick, instead of 's/ *$//' ? The later seems like it'd only match two or more spaces.Wilfordwilfred
You’re thinking of glob, not regex. * means 0 or more of the previous thing, so s/ *$// will match on zero spaces.Hurlow
D
28

With Bash's extended pattern matching features enabled (shopt -s extglob), you can use this:

{trimmed##*( )}

to remove an arbitrary amount of leading spaces.

Discolor answered 15/12, 2008 at 21:24 Comment(6)
Terrific! I think this is the most lightweight and elegant solution.Cameleer
See @GuruM's post below for a similar, but more generic solution that (a) deals with all forms of white space and (b) also handles trailing white space.Tights
@mkelement +1 for taking the trouble to rewrite my code snippet as a function. ThanksCilice
Works with OpenBSD's default /bin/ksh as well. /bin/sh -o posix works too but I'm suspicious.Elbring
Not a bash wizard here; what's trimmed? Is it a built-in thing or the variable that is being trimmed?Lilah
@Abhijit Sarkar - It's the latter.Gemmiparous
S
26
# Trim whitespace from both ends of specified parameter

trim () {
    read -rd '' $1 <<<"${!1}"
}

# Unit test for trim()

test_trim () {
    local foo="$1"
    trim foo
    test "$foo" = "$2"
}

test_trim hey hey &&
test_trim '  hey' hey &&
test_trim 'ho  ' ho &&
test_trim 'hey ho' 'hey ho' &&
test_trim '  hey  ho  ' 'hey  ho' &&
test_trim $'\n\n\t hey\n\t ho \t\n' $'hey\n\t ho' &&
test_trim $'\n' '' &&
test_trim '\n' '\n' &&
echo passed
Saker answered 15/12, 2008 at 21:24 Comment(6)
Amazing! Simple and effective! Clearly my favorite solution. Thank you!Postpaid
very ingenious, as it is also extremely an "one liner" helper read -rd '' str < <(echo "$str") thx!Phototherapy
@CraigMcQueen it is the variable value, as read will store at variable by its name $1 a trimmed version of its value ${!1}Phototherapy
The trim() function's parameter is a variable name: see the call to trim() inside test_trim(). Within trim() as called from test_trim(), $1 expands to foo and ${!1} expands to $foo (that is, to the current contents of variable foo). Search the bash manual for 'variable indirection'.Saker
How about this little modification, to support trimming multiple vars in one call? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }Zoe
@AquariusPower there's no need to use echo in a subshell for the one-liner version, just read -rd '' str <<<"$str" will do.Saker
C
24

You can delete newlines with tr:

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done
Columbic answered 15/12, 2008 at 21:24 Comment(2)
I don't want to remove '\n' from the middle of the string, only from the beginning or end.Cervix
The original question was wrong when it assumed that the problem was a trailing newline; the var=`hg...` would already have removed the newline if that was the case. (The actual problem was some other output.)Poultry
B
22

There are a lot of answers, but I still believe my just-written script is worth being mentioned because:

  • it was successfully tested in the shells bash/dash/busybox shell
  • it is extremely small
  • it doesn't depend on external commands and doesn't need to fork (->fast and low resource usage)
  • it works as expected:
    • it strips all spaces and tabs from beginning and end, but not more
    • important: it doesn't remove anything from the middle of the string (many other answers do), even newlines will remain
    • special: the "$*" joins multiple arguments using one space. if you want to trim & output only the first argument, use "$1" instead
    • if doesn't have any problems with matching file name patterns etc

The script:

trim() {
  local s2 s="$*"
  until s2="${s#[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

Usage:

mystring="   here     is
    something    "
mystring=$(trim "$mystring")
echo ">$mystring<"

Output:

>here     is
    something<
Belga answered 20/10, 2015 at 23:32 Comment(6)
Bah in C this would be way simpler to implement!Tailing
Sure. Unfortunately, this is not C and sometimes you want to avoid calling external toolsBelga
To make the code both more readable and copy-past compatible, you could change the brackets to escaped characters: [\ \t]Eld
@leondepeon did you try this? I tried when i wrote it and tried again, and your suggestion doesn't work in any of bash, dash, busyboxBelga
@DanielAlder I did, but as it is already 3 years ago, I can't find the code where I used it. Now however, I'd probably use [[:space:]] like in one of the other answers: https://mcmap.net/q/11871/-how-to-trim-whitespace-from-a-bash-variableEld
I've tried your solution, but it fails with non-breaking space at the end (en.wikipedia.org/wiki/Non-breaking_space). Do you have any idea how to improve your solution to U+00A0 character?Triennial
C
17

This is what I did and worked out perfect and so simple:

the_string="        test"
the_string=`echo $the_string`
echo "$the_string"

Output:

test
Condom answered 15/12, 2008 at 21:24 Comment(3)
I tried two other solution from this question/answer page and only this one worked as expected, keep-it-simple-silly strikes again!!Warmongering
You could even use process command substitution but I guess that won't work for Bourne ShellSuperabound
will not work if the_string contains wildcards, e.g. "the_string=" foo * bar "Garvy
T
14

If you have shopt -s extglob enabled, then the following is a neat solution.

This worked for me:

text="   trim my edges    "

trimmed=$text
trimmed=${trimmed##+( )} #Remove longest matching series of spaces from the front
trimmed=${trimmed%%+( )} #Remove longest matching series of spaces from the back

echo "<$trimmed>" #Adding angle braces just to make it easier to confirm that all spaces are removed

#Result
<trim my edges>

To put that on fewer lines for the same result:

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}
Thy answered 15/12, 2008 at 21:24 Comment(9)
Didn't work for me. The first one printed an untrimmed string. The second threw bad substitution. Can you explain what's going on here?Forseti
@Forseti : this is a site I use frequently that spells out how variable manipulation works in bash search for ${var##Pattern} for more details. Also, this site explains bash patterns. So the ## means remove the given pattern from the front and %% means remove the given pattern from the back. The +( ) portion is the pattern and it means "one or more occurrence of a space"Thy
Funny, it worked in the prompt, but not after transposing to the bash script file.Archiepiscopal
weird. Is it the same bash version in both instances?Thy
@Archiepiscopal #!/bin/sh is not necessarily bash ;)Belga
Hi @DanielAlder, you replied to a comment from 2016, and I have no idea what is this about. But I agree with you. Have a good one.Archiepiscopal
@Archiepiscopal It's really easy to remember what this is about, just check the title of the original question and the answer these comments are under. Btw, it didn't work for you because your bash hell did not have the option shopt -s extglob. I updated the answer.Aetolia
That formulation for fewer lines is wrong. You can't do ${${f}} in bash.Onomatopoeia
The second part is flat wrong. You cannot combine multiple substitutions like ${${...}...}; the only thing you can have following ${ is an optional ! and then the name of a variable. (It is also illogical, since the intuitive interpretation of ${${var}} is to expand the variable var to get the name of another variable, which in turn is expanded to get the text that you want.Poultry
S
13
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

OR

# Strip leading white space (new line inclusive).
ltrim(){
    [[ "$1" =~ [^[:space:]].* ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    [[ "$1" =~ .*[^[:space:]] ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

OR

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

OR

# Strip leading specified characters.  ex: str=$(ltrim "$str" $'\n a')
ltrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"]) ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip trailing specified characters.  ex: str=$(rtrim "$str" $'\n a')
rtrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1" "$2")" "$2")"
}

OR

Building upon moskit's expr soulution...

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$"`"
}

OR

# Strip leading white space (new line inclusive).
ltrim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)"`"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    printf "%s" "`expr "$1" : "^\(.*[^[:space:]]\)[[:space:]]*$"`"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}
Shakiashaking answered 15/12, 2008 at 21:24 Comment(0)
I
12

There are a few different options purely in BASH:

line=${line##+([[:space:]])}    # strip leading whitespace;  no quote expansion!
line=${line%%+([[:space:]])}   # strip trailing whitespace; no quote expansion!
line=${line//[[:space:]]/}   # strip all whitespace
line=${line//[[:space:]]/}   # strip all whitespace

line=${line//[[:blank:]]/}   # strip all blank space

The former two require extglob be set/enabled a priori:

shopt -s extglob  # bash only

NOTE: variable expansion inside quotation marks breaks the top two examples!

The pattern matching behaviour of POSIX bracket expressions are detailed here. If you are using a more modern/hackable shell such as Fish, there are built-in functions for string trimming.

Ingenuous answered 15/12, 2008 at 21:24 Comment(0)
B
12

Use AWK:

echo $var | awk '{gsub(/^ +| +$/,"")}1'
Bedspring answered 15/12, 2008 at 21:24 Comment(2)
Sweet that seems to work (ex:) $stripped_version=echo $var | awk '{gsub(/^ +| +$/,"")}1'``Moderato
except awk isn't doing anything: echo'ing an unquoted variable has already stripped out whitespaceMidlothian
I
11

You can use old-school tr. For example, this returns the number of modified files in a git repository, whitespaces stripped.

MYVAR=`git ls-files -m|wc -l|tr -d ' '`
Improvident answered 15/12, 2008 at 21:24 Comment(1)
This doesn't trim whitespace from the front and back - it removes all whitespace from the string.Testerman
O
10

This will remove all the whitespaces from your String,

 VAR2="${VAR2//[[:space:]]/}"

/ replaces the first occurrence and // all occurrences of whitespaces in the string. I.e. all white spaces get replaced by – nothing

Orthman answered 15/12, 2008 at 21:24 Comment(0)
N
8

I would simply use sed:

function trim
{
    echo "$1" | sed -n '1h;1!H;${;g;s/^[ \t]*//g;s/[ \t]*$//g;p;}'
}

a) Example of usage on single-line string

string='    wordA wordB  wordC   wordD    '
trimmed=$( trim "$string" )

echo "GIVEN STRING: |$string|"
echo "TRIMMED STRING: |$trimmed|"

Output:

GIVEN STRING: |    wordA wordB  wordC   wordD    |
TRIMMED STRING: |wordA wordB  wordC   wordD|

b) Example of usage on multi-line string

string='    wordA
   >wordB<
wordC    '
trimmed=$( trim "$string" )

echo -e "GIVEN STRING: |$string|\n"
echo "TRIMMED STRING: |$trimmed|"

Output:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c) Final note:
If you don't like to use a function, for single-line string you can simply use a "easier to remember" command like:

echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Example:

echo "   wordA wordB wordC   " | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Output:

wordA wordB wordC

Using the above on multi-line strings will work as well, but please note that it will cut any trailing/leading internal multiple space as well, as GuruM noticed in the comments

string='    wordAA
    >four spaces before<
 >one space before<    '
echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

Output:

wordAA
>four spaces before<
>one space before<

So if you do mind to keep those spaces, please use the function at the beginning of my answer!

d) EXPLANATION of the sed syntax "find and replace" on multi-line strings used inside the function trim:

sed -n '
# If the first line, copy the pattern to the hold buffer
1h
# If not the first line, then append the pattern to the hold buffer
1!H
# If the last line then ...
$ {
    # Copy from the hold to the pattern buffer
    g
    # Do the search and replace
    s/^[ \t]*//g
    s/[ \t]*$//g
    # print
    p
}'
Nuremberg answered 15/12, 2008 at 21:24 Comment(6)
Note: As suggested by @mkelement it will not work for multi-line string though it should work for single-line strings.Cilice
You are wrong: it does work on multi-line strings too. Just test it out!:)Nuremberg
+1 for the usage - made it easy for me to test out the code. However the code still won't work for multi-line strings. If you look carefully at the output, you'll notice that any leading/trailing internal spaces are also getting removed e.g. the space in front of " multi-line" is replaced by "multi-line". Just try increasing the number of leading/trailing spaces on each line.Cilice
Now I see what you mean! Thank you for the head up, I edited my answer.Nuremberg
@"Luca Borrione" - welcome :-) Would you explain the sed syntax you're using in trim()? It might also help any user of your code to tweak it to other uses. Also it might even help find edge-cases for the regular-expression.Cilice
let us continue this discussion in chatCilice
R
6

A simple answer is:

sed 's/^\s*\|\s*$//g'

An example:

$ before=$( echo -e " \t a  b \t ")
$ echo "(${before})"
(    a  b    )

$ after=$( echo "${before}"  |  sed 's/^\s*\|\s*$//g' )
$ echo "(${after})"
(a  b)
Rouen answered 15/12, 2008 at 21:24 Comment(0)
E
6

This is the simplest method I've seen. It only uses Bash, it's only a few lines, the regexp is simple, and it matches all forms of whitespace:

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

Here is a sample script to test it with:

test=$(echo -e "\n \t Spaces and tabs and newlines be gone! \t  \n ")

echo "Let's see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let's fix that..."

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

echo
echo "----------"
echo -e "Testing:${test}:Tested"  # "Testing:Spaces and tabs and newlines be gone!"
echo "----------"
echo
echo "Ah, much better."
Employ answered 15/12, 2008 at 21:24 Comment(1)
Surely preferable to, for example (ye gods!), shelling out to Python. Except I think it's simpler and more general to correctly handle string that contains only spaces.Slightly simplified expression would be: ^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$Graduate
P
6
var='   a b c   '
trimmed=$(echo $var)
Piperpiperaceous answered 15/12, 2008 at 21:24 Comment(1)
That won't work if there are more than one space in between any two words. Try: echo $(echo "1 2 3") (with two spaces between 1, 2, and 3).Embolden
P
6

To remove spaces and tabs from left to first word, enter:

echo "     This is a test" | sed "s/^[ \t]*//"

cyberciti.biz/tips/delete-leading-spaces-from-front-of-each-word.html

Policewoman answered 15/12, 2008 at 21:24 Comment(0)
S
6

This does not have the problem with unwanted globbing, also, interior white-space is unmodified (assuming that $IFS is set to the default, which is ' \t\n').

It reads up to the first newline (and doesn't include it) or the end of string, whichever comes first, and strips away any mix of leading and trailing space and \t characters. If you want to preserve multiple lines (and also strip leading and trailing newlines), use read -r -d '' var << eof instead; note, however, that if your input happens to contain \neof, it will be cut off just before. (Other forms of white space, namely \r, \f, and \v, are not stripped, even if you add them to $IFS.)

read -r var << eof
$var
eof
Sensation answered 15/12, 2008 at 21:24 Comment(0)
G
6

Here's a trim() function that trims and normalizes whitespace

#!/bin/bash
function trim {
    echo $*
}

echo "'$(trim "  one   two    three  ")'"
# 'one two three'

And another variant that uses regular expressions.

#!/bin/bash
function trim {
    local trimmed="$@"
    if [[ "$trimmed" =~ " *([^ ].*[^ ]) *" ]]
    then 
        trimmed=${BASH_REMATCH[1]}
    fi
    echo "$trimmed"
}

echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'
Girl answered 15/12, 2008 at 21:24 Comment(3)
The first approach is tricky in that it not only normalizes interior whitespace (replaces all interior spans of whitespace with a single space each) , but is also subject to globbing (pathname expansion) so that, for instance, a * character in the input string would expand to all files and folders in the current working folder. Finally, if $IFS is set to a non-default value, trimming may not work (though that is easily remedied by adding local IFS=$' \t\n'). Trimming is limited to the following forms of whitespace: spaces, \t and \n characters.Tights
The second, regular expression-based approach is great and side effect-free, but in its present form is problematic: (a) on bash v3.2+, matching will by default NOT work, because the regular expression must be UNquoted in order to work and (b) the regular expression itself doesn't handle the case where the input string is a single, non-space character surrounded by spaces. To fix these problems, replace the if line with: if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]. Finally, the approach only deals with spaces, not other forms of whitespace (see my next comment).Tights
The function that utilizes regular expresssions only deals with spaces and not other forms of whitespace, but it's easy to generalize: Replace the if line with: [[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]Tights
S
6

Assignments ignore leading and trailing whitespace and as such can be used to trim:

$ var=`echo '   hello'`; echo $var
hello
Sparteine answered 15/12, 2008 at 21:24 Comment(2)
That's not true. It's "echo" that removes whitespace, not the assignment. In your example, do echo "$var" to see the value with spaces.Girl
@NicholasSushkin One could do var=$(echo $var) but I do not recommend it. Other solutions presented here are preferred.Postpaid
S
6

I've seen scripts just use variable assignment to do the job:

$ xyz=`echo -e 'foo \n bar'`
$ echo $xyz
foo bar

Whitespace is automatically coalesced and trimmed. One has to be careful of shell metacharacters (potential injection risk).

I would also recommend always double-quoting variable substitutions in shell conditionals:

if [ -n "$var" ]; then

since something like a -o or other content in the variable could amend your test arguments.

Sapphire answered 15/12, 2008 at 21:24 Comment(2)
It is the unquoted use of $xyz with echo that does the whitespace coalescing, not the variable assignment. To store the trimmed value in the variable in your example, you'd have to use xyz=$(echo -n $xyz). Also, this approach is subject to potentially unwanted pathname expansion (globbing).Tights
this is jut wrong, the value in the xyz variable is NOT trimmed.Mice
T
5

Removing spaces to one space:

(text) | fmt -su
Typecase answered 15/12, 2008 at 21:24 Comment(0)
C
4

I had to test the result (numeric) from a command but it seemed the variable with the result was containing spaces and some non printable characters. Therefore even after a "trim" the comparison was erroneous. I solved it by extracting the numerical part from the variable:

numerical_var=$(echo ${var_with_result_from_command} | grep -o "[0-9]*")
Crossstitch answered 15/12, 2008 at 21:24 Comment(0)
D
4

Use:

trim() {
    local orig="$1"
    local trmd=""
    while true;
    do
        trmd="${orig#[[:space:]]}"
        trmd="${trmd%[[:space:]]}"
        test "$trmd" = "$orig" && break
        orig="$trmd"
    done
    printf -- '%s\n' "$trmd"
}
  • It works on all kinds of whitespace, including newline,
  • It does not need to modify shopt.
  • It preserves inside whitespace, including newline.

Unit test (for manual review):

#!/bin/bash

. trim.sh

enum() {
    echo "   a b c"
    echo "a b c   "
    echo "  a b c "
    echo " a b c  "
    echo " a  b c  "
    echo " a  b  c  "
    echo " a      b  c  "
    echo "     a      b  c  "
    echo "     a  b  c  "
    echo " a  b  c      "
    echo " a  b  c      "
    echo " a N b  c  "
    echo "N a N b  c  "
    echo " Na  b  c  "
    echo " a  b  c N "
    echo " a  b  c  N"
}

xcheck() {
    local testln result
    while IFS='' read testln;
    do
        testln=$(tr N '\n' <<<"$testln")
        echo ": ~~~~~~~~~~~~~~~~~~~~~~~~~ :" >&2
        result="$(trim "$testln")"
        echo "testln='$testln'" >&2
        echo "result='$result'" >&2
    done
}

enum | xcheck
Denouement answered 15/12, 2008 at 21:24 Comment(0)
S
4

Python has a function strip() that works identically to PHP's trim(), so we can just do a little inline Python to make an easily understandable utility for this:

alias trim='python -c "import sys; sys.stdout.write(sys.stdin.read().strip())"'

This will trim leading and trailing whitespace (including newlines).

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi
Sulfathiazole answered 15/12, 2008 at 21:24 Comment(1)
while that works, you may want to consider offering a solution that does not involve launching a full python interpreter just to trim a string. It's just wasteful.Cauchy
N
4

trim() removes whitespaces (and tabs, non-printable characters; I am considering just whitespaces for simplicity). My version of a solution:

var="$(hg st -R "$path")" # I often like to enclose shell output in double quotes
var="$(echo "${var}" | sed "s/\(^ *\| *\$\)//g")" # This is my suggestion
if [ -n "$var" ]; then
 echo "[${var}]"
fi

The 'sed' command trims only leading and trailing whitespaces, but it can be piped to the first command as well resulting in:

var="$(hg st -R "$path" | sed "s/\(^ *\| *\$\)//g")"
if [ -n "$var" ]; then
 echo "[${var}]"
fi
Newburg answered 15/12, 2008 at 21:24 Comment(0)
L
4

I needed to trim whitespace from a script when the IFS variable was set to something else. Relying on Perl did the trick:

# trim() { echo $1; } # This doesn't seem to work, as it's affected by IFS

trim() { echo "$1" | perl -p -e 's/^\s+|\s+$//g'; }

strings="after --> , <-- before,  <-- both -->  "

OLD_IFS=$IFS
IFS=","
for str in ${strings}; do
  str=$(trim "${str}")
  echo "str= '${str}'"
done
IFS=$OLD_IFS
Lennielenno answered 15/12, 2008 at 21:24 Comment(2)
You can easily avoid problems with non-default $IFS values by creating a local copy (which will go out of scope upon exiting the function): trim() { local IFS=$' \t\n'; echo $1; }Tights
Related to @Tights coment: mywiki.wooledge.org/IFS. The default value of IFS is space, tab, newline. (A three-character string.)...Deification
J
3

The simplest and cheapest way to do this is to take advantage of echo ignoring spaces. So, just use

dest=$(echo $source)

for instance:

> VAR="   Hello    World   "
> echo "x${VAR}x"
x   Hello    World   x
> TRIMD=$(echo $VAR)
> echo "x${TRIMD}x"
xHello Worldx

Note that this also collapses multiple whitespaces into a single one.

Javelin answered 15/12, 2008 at 21:24 Comment(2)
Really great cheap and simple solution.Worrywart
The lack of quoting is a bug; for example, if var contains a wildcard character, the shell will expand it. See also When to wrap quotes around a shell variable?Galenic
S
3

Use this simple Bash parameter expansion:

$ x=" a z     e r ty "
$ echo "START[${x// /}]END"
START[azerty]END
Shush answered 15/12, 2008 at 21:24 Comment(3)
This approach (a) also removes interior spaces and (b) only removes spaces, not other forms of white space.Tights
@Tights Sometimes that's exactly what is needed, though! :-)Olivarez
@Ville: Yes, and with the help of my behavior-clarifying comment future readers can decide if this solution is the right one for them.Tights
F
3

I found that I needed to add some code from a messy sdiff output in order to clean it up:

sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt 
sed -n 1'p' c12diff.txt | sed 's/ *$//g' | tr -d '\n' | tr -d '\t'

This removes the trailing spaces and other invisible characters.

Fresco answered 15/12, 2008 at 21:24 Comment(0)
R
3

I created the following functions. I am not sure how portable printf is, but the beauty of this solution is you can specify exactly what is "white space" by adding more character codes.

    iswhitespace()
    {
        n=`printf "%d\n" "'$1'"`
        if (( $n != "13" )) && (( $n != "10" )) && (( $n != "32" )) && (( $n != "92" )) && (( $n != "110" )) && (( $n != "114" )); then
            return 0
        fi
        return 1
    }

    trim()
    {
        i=0
        str="$1"
        while (( i < ${#1} ))
        do
            char=${1:$i:1}
            iswhitespace "$char"
            if [ "$?" -eq "0" ]; then
                str="${str:$i}"
                i=${#1}
            fi
            (( i += 1 ))
        done
        i=${#str}
        while (( i > "0" ))
        do
            (( i -= 1 ))
            char=${str:$i:1}
            iswhitespace "$char"
            if [ "$?" -eq "0" ]; then
                (( i += 1 ))
                str="${str:0:$i}"
                i=0
            fi
        done
        echo "$str"
    }

#Call it like so
mystring=`trim "$mystring"`
Rubdown answered 15/12, 2008 at 21:24 Comment(0)
D
3

This trims multiple spaces of the front and end

whatever=${whatever%% *}

whatever=${whatever#* }

Don answered 15/12, 2008 at 21:24 Comment(3)
Your second command should presumably have ## not just #. But in fact these don't work; the pattern you're giving matches a space followed by any sequence of other characters, not a sequence of 0 or more spaces. That * is the shell globbing *, not the usual "0-or-more" regexp *.Cameleer
These are useful if you know there will be no spaces in the string. They work great for converting " foo " to "foo". For " hello world ", not so much.Warford
t=" "" a "" "; echo "=${t#* }=" output = a = leading multiple spaces are not stripped. t=" "" a "" "; echo "=${t%% *}=" output == trailing spaces are stripped with string content. (Double quoting because of SE.)Valais
J
2

Array assignment expands its parameter splitting on the Internal Field Separator (space/tab/newline by default).

words=($var)
var="${words[@]}"
Johnajohnath answered 15/12, 2008 at 21:24 Comment(0)
E
2

Use:

var=`expr "$var" : "^\ *\(.*[^ ]\)\ *$"`

It removes leading and trailing spaces and is the most basic solution, I believe. Not Bash built-in, but 'expr' is a part of coreutils, so at least no standalone utilities are needed like sed or AWK.

Earleneearley answered 15/12, 2008 at 21:24 Comment(0)
L
2
var="  a b  "
echo "$(set -f; echo $var)"

>a b
Lest answered 15/12, 2008 at 21:24 Comment(0)
E
2
#!/bin/bash

function trim
{
    typeset trimVar
    eval trimVar="\${$1}"
    read trimVar << EOTtrim
    $trimVar
EOTtrim
    eval $1=\$trimVar
}

# Note that the parameter to the function is the NAME of the variable to trim, 
# not the variable contents.  However, the contents are trimmed.


# Example of use:
while read aLine
do
    trim aline
    echo "[${aline}]"
done < info.txt



# File info.txt contents:
# ------------------------------
# ok  hello there    $
#    another  line   here     $
#and yet another   $
#  only at the front$
#$



# Output:
#[ok  hello there]
#[another  line   here]
#[and yet another]
#[only at the front]
#[]
Enchase answered 15/12, 2008 at 21:24 Comment(0)
H
1

Yet another solution with unit tests which trims $IFS from stdin, and works with any input separator (even $'\0'):

ltrim()
{
    # Left-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local trimmed
    while IFS= read -r -d "${1-}" -u 9
    do
        if [ -n "${trimmed+defined}" ]
        then
            printf %s "$REPLY"
        else
            printf %s "${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
        printf "${1-\x00}"
        trimmed=true
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        if [ -n "${trimmed+defined}" ]
        then
            printf %s "$REPLY"
        else
            printf %s "${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
    fi
}

rtrim()
{
    # Right-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local previous last
    while IFS= read -r -d "${1-}" -u 9
    do
        if [ -n "${previous+defined}" ]
        then
            printf %s "$previous"
            printf "${1-\x00}"
        fi
        previous="$REPLY"
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        last="$REPLY"
        printf %s "$previous"
        if [ -n "${previous+defined}" ]
        then
            printf "${1-\x00}"
        fi
    else
        last="$previous"
    fi

    right_whitespace="${last##*[!$IFS]}"
    printf %s "${last%$right_whitespace}"
}

trim()
{
    # Trim $IFS from individual lines
    # $1: Line separator (default NUL)
    ltrim ${1+"$@"} | rtrim ${1+"$@"}
}
Hurling answered 15/12, 2008 at 21:24 Comment(0)
C
0
var = '  a b  '
# remove all white spaces
new=$(echo $var |  tr -d ' ')
# remove leading and trailing whitespaces
new=$(echo $var)

ab
a b
Cusack answered 15/12, 2008 at 21:24 Comment(2)
The lack of quoting is a bug; for example, if var contains a wildcard character, the shell will expand it. See also When to wrap quotes around a shell variable?Galenic
Fifty answers, one with nearly1,500 votes. What is your answer adding that's new? Why do you prefer your approach over the other answers?Driggers
A
0

Create an array instead of variable this will trim all space, tab and newline characters:

arr=( $(hg st -R "$path") )
if [[ -n "${arr[@]}" ]]; then
    printf -- '%s\n' "${arr[@]}"
fi
Alcalde answered 15/12, 2008 at 21:24 Comment(0)
E
0

The simplest way for the single-line use cases I know of:

echo "  ABC  " | sed -e 's# \+\(.\+\) \+#\1#'

How it works:

  • -e enables advanced regex
  • I use # with sed as I don't like the "messy library" patterns like /\////\/\\\/\/
  • sed wants most regex control chars escaped, hence all the \
  • Otherwise it's just ^ +(.+) +$, i.e. spaces at the beginning, a group no.1, and spaces at the end.
  • All this is replaced with just "group no.1".

Therefore, ABC becomes ABC.

This should be supported on most recent systems with sed.


For tabs, that would be

echo "  ABC  " | sed -e 's#[\t ]\+\(.\+\)[\t ]\+#\1#'

For multi-line content, that already needs character classes like [:space:] as described in other answers, and may not be supported by all sed implementations.

Reference: Sed manual

Em answered 15/12, 2008 at 21:24 Comment(1)
-e does not enable advanced regex. Did you mean -E (with which you wouldn't need any of those backslashes)?Bate
E
0

read already trims whitespace, so in bash you can do this:

$ read foo <<< "   foo  bar   two spaces follow   "
$ echo ".$foo."
.foo  bar   two spaces follow.

The POSIX compliant version is a bit longer

$ read foo << END
   foo  bar   two spaces follow   
END
$ echo ".$foo."
.foo  bar   two spaces follow.
Epigenesis answered 15/12, 2008 at 21:24 Comment(0)
B
0

The "trim" function removes all horizontal whitespace:

ltrim () {
    if [[ $# -eq 0 ]]; then cat; else printf -- '%s\n' "$@"; fi | perl -pe 's/^\h+//g'
    return $?
}

rtrim () {
    if [[ $# -eq 0 ]]; then cat; else printf -- '%s\n' "$@"; fi | perl -pe 's/\h+$//g'
    return $?
}

trim () {
    ltrim "$@" | rtrim
    return $?
}
Brainstorming answered 15/12, 2008 at 21:24 Comment(0)
A
-3

While it's not strictly Bash this will do what you want and more:

php -r '$x = trim("  hi there  "); echo $x;'

If you want to make it lowercase too, do:

php -r '$x = trim("  Hi There  "); $x = strtolower($x) ; echo $x;'
Augend answered 15/12, 2008 at 21:24 Comment(0)
L
-4
#Execute this script with the string argument passed in double quotes !! 
#var2 gives the string without spaces.
#$1 is the string passed in double quotes
#!/bin/bash
var2=`echo $1 | sed 's/ \+//g'`
echo $var2
Linctus answered 15/12, 2008 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.