How to trim whitespace from a Bash variable?

1,682,894

Solution 1

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.

Solution 2

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})"

Solution 3

There is a solution which only 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   "

A nice thing about this solution is that it will work with any POSIX-compliant shell.

Reference

Solution 4

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

Solution 5

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'
Share:
1,682,894
Theo Orphanos
Author by

Theo Orphanos

Updated on April 12, 2022

Comments

  • Theo Orphanos
    Theo Orphanos about 2 years

    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.

    • Volomike
      Volomike over 11 years
      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.
    • user.friendly
      user.friendly about 8 years
      "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 $? 1
    • Luis Alvarado
      Luis Alvarado almost 8 years
      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)
    • Mecki
      Mecki almost 7 years
      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.
    • Konstantin Burlachenko
      Konstantin Burlachenko almost 6 years
      A="123 4 5 6 "; B=echo $A | sed -r 's/( )+//g';
    • Jan Groth
      Jan Groth almost 3 years
      This question would benefit from one of the answers being excepted.
  • Theo Orphanos
    Theo Orphanos over 15 years
    I don't want to remove '\n' from the middle of the string, only from the beginning or end.
  • Paul Tomblin
    Paul Tomblin over 15 years
    Or rather, it works for spaces in the middle of a var, but not when I attempt to anchor it at the end.
  • Admin
    Admin over 15 years
    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."
  • Paul Tomblin
    Paul Tomblin over 15 years
    @Ant, so they're not really regular expressions, but something similar?
  • Admin
    Admin about 15 years
    They're regex, just a strange dialect.
  • Peter Lindqvist
    Peter Lindqvist over 14 years
    to replace all matches echo ${var// }
  • rogerdpack
    rogerdpack about 14 years
    Sweet that seems to work (ex:) $stripped_version=echo $var | awk '{gsub(/^ +| +$/,"")}1'``
  • dubiousjim
    dubiousjim over 13 years
    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 *.
  • dubiousjim
    dubiousjim over 13 years
    Terrific! I think this is the most lightweight and elegant solution.
  • glenn jackman
    glenn jackman almost 13 years
    except awk isn't doing anything: echo'ing an unquoted variable has already stripped out whitespace
  • Nicholas Sushkin
    Nicholas Sushkin about 12 years
    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.
  • mklement0
    mklement0 almost 12 years
    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; }
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' almost 12 years
    ${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.
  • mklement0
    mklement0 almost 12 years
    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.
  • mklement0
    mklement0 almost 12 years
    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).
  • mklement0
    mklement0 almost 12 years
    @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.
  • Paul Tomblin
    Paul Tomblin almost 12 years
    I think that was the point I was making - that flussence was calling them regular expressions, but they're not.
  • mklement0
    mklement0 almost 12 years
    This approach (a) also removes interior spaces and (b) only removes spaces, not other forms of white space.
  • mklement0
    mklement0 almost 12 years
    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.
  • mklement0
    mklement0 almost 12 years
    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).
  • mklement0
    mklement0 almost 12 years
    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:]])[[:spac‌​e:]]* ]]
  • mklement0
    mklement0 almost 12 years
    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:]])[[:spac‌​e:]]* ]]; echo -n "${BASH_REMATCH[1]}")
  • xebeche
    xebeche over 11 years
    @NicholasSushkin One could do var=$(echo $var) but I do not recommend it. Other solutions presented here are preferred.
  • xebeche
    xebeche over 11 years
    Amazing! Simple and effective! Clearly my favorite solution. Thank you!
  • GuruM
    GuruM over 11 years
    as you've observed trim() removes leading and trailing whitespace only.
  • Ярослав Рахматуллин
    Ярослав Рахматуллин over 11 years
    @luca borrione: local var="$1"
  • GuruM
    GuruM over 11 years
    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.
  • GuruM
    GuruM over 11 years
    Note: As suggested by @mkelement it will not work for multi-line string though it should work for single-line strings.
  • Luca Borrione
    Luca Borrione over 11 years
    You are wrong: it does work on multi-line strings too. Just test it out!:)
  • GuruM
    GuruM over 11 years
    +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.
  • GuruM
    GuruM over 11 years
    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.
  • Luca Borrione
    Luca Borrione over 11 years
    Now I see what you mean! Thank you for the head up, I edited my answer.
  • GuruM
    GuruM over 11 years
    @"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.
  • GuruM
    GuruM over 11 years
  • GuruM
    GuruM over 11 years
    @mkelement +1 for taking the trouble to rewrite my code snippet as a function. Thanks
  • Will
    Will about 11 years
    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.
  • instanceof me
    instanceof me about 11 years
    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.
  • joshlf
    joshlf over 10 years
    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).
  • n611x007
    n611x007 over 10 years
    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.
  • n611x007
    n611x007 over 10 years
    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.
  • n611x007
    n611x007 over 10 years
    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.
  • Asclepius
    Asclepius over 10 years
    Using grep . can further prevent the output of empty lines.
  • Jocelyn delalande
    Jocelyn delalande over 10 years
    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.
  • Nick
    Nick over 10 years
    This doesn't trim whitespace from the front and back - it removes all whitespace from the string.
  • Evgeni Sergeev
    Evgeni Sergeev over 10 years
    This collapses multiple contiguous spaces into one.
  • Victor Zamanian
    Victor Zamanian about 10 years
    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!
  • Torben
    Torben about 10 years
    @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.
  • Victor Zamanian
    Victor Zamanian about 10 years
    @Torben Fair point. I suppose the single expression could be made conditional, with |, so as to keep it as one single expression, rather than several.
  • gniourf_gniourf
    gniourf_gniourf about 10 years
    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.
  • Joe
    Joe almost 10 years
    This will trim only 1 space character. So the echo results in 'hello world ', 'foo bar', 'both sides '
  • Mark G.
    Mark G. over 9 years
    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!
  • Admin
    Admin over 9 years
    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.
  • bos
    bos over 9 years
    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.
  • Sasha
    Sasha about 9 years
    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.
  • Daniel Alder
    Daniel Alder about 9 years
    @makevoid Why did you remove the (very important) warning from this answer? This doesn't change the behaviour of xargs...
  • J.D.
    J.D. about 9 years
    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.
  • makevoid
    makevoid about 9 years
    @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 :)
  • Aquarius Power
    Aquarius Power about 9 years
    very ingenious, as it is also extremely an "one liner" helper read -rd '' str < <(echo "$str") thx!
  • Aquarius Power
    Aquarius Power about 9 years
    @CraigMcQueen it is the variable value, as read will store at variable by its name $1 a trimmed version of its value ${!1}
  • pdwalker
    pdwalker about 9 years
    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.
  • flabdablet
    flabdablet about 9 years
    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'.
  • Quinn Comendant
    Quinn Comendant almost 9 years
    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.
  • caesarsol
    caesarsol almost 9 years
    this is jut wrong, the value in the xyz variable is NOT trimmed.
  • farid99
    farid99 almost 9 years
    could you explain the syntax for sed?
  • Paul Tomblin
    Paul Tomblin almost 9 years
    The regular expression matches all trailing whitespace and replaces it with nothing.
  • Admin
    Admin almost 9 years
    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.
  • musicin3d
    musicin3d over 8 years
    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?
  • musicin3d
    musicin3d over 8 years
    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.
  • gMale
    gMale over 8 years
    @musicin3d : 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"
  • Qian Chen
    Qian Chen about 8 years
    How about leading whitespaces?
  • DrBeco
    DrBeco about 8 years
    Funny, it worked in the prompt, but not after transposing to the bash script file.
  • DrBeco
    DrBeco about 8 years
    Good reminder of the wildcards @gniourf_gniourf +1. Still an excelente solution, Vamp. +1 to you too.
  • gMale
    gMale about 8 years
    weird. Is it the same bash version in both instances?
  • F-3000
    F-3000 about 8 years
    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.
  • Craig
    Craig about 8 years
    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.
  • vinc17
    vinc17 about 8 years
    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:]].
  • vinc17
    vinc17 about 8 years
    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).
  • vinc17
    vinc17 about 8 years
    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.
  • Gene Pavlovsky
    Gene Pavlovsky almost 8 years
    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.
  • Gene Pavlovsky
    Gene Pavlovsky almost 8 years
    How about this little modification, to support trimming multiple vars in one call? trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
  • flabdablet
    flabdablet almost 8 years
    @AquariusPower there's no need to use echo in a subshell for the one-liner version, just read -rd '' str <<<"$str" will do.
  • Nils
    Nils over 7 years
    Bah in C this would be way simpler to implement!
  • Daniel Alder
    Daniel Alder over 7 years
    Sure. Unfortunately, this is not C and sometimes you want to avoid calling external tools
  • sehe
    sehe over 7 years
    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 substitutions
  • Leon S.
    Leon S. over 7 years
    To make the code both more readable and copy-past compatible, you could change the brackets to escaped characters: [\ \t]
  • Ohad Schneider
    Ohad Schneider over 7 years
    @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.htm‌​l, 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.
  • Mig82
    Mig82 about 7 years
    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.
  • Ville
    Ville about 7 years
    @mklement0 Sometimes that's exactly what is needed, though! :-)
  • mklement0
    mklement0 about 7 years
    @Ville: Yes, and with the help of my behavior-clarifying comment future readers can decide if this solution is the right one for them.
  • rkachach
    rkachach about 7 years
    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).
  • Ron Burk
    Ron Burk about 7 years
    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:]]*$
  • nwinkler
    nwinkler almost 7 years
    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 | xargs
  • makevoid
    makevoid almost 7 years
    @AlexanderMills thanks for the sincerity.... maybe try with this? A=$(echo " lol " | xargs); echo $A
  • Potherca
    Potherca over 6 years
    This is by far the cleanest (both short and readable) solution.
  • wjandrea
    wjandrea over 6 years
    @Joe I added a better option.
  • Clint Pachl
    Clint Pachl about 6 years
    Works with OpenBSD's default /bin/ksh as well. /bin/sh -o posix works too but I'm suspicious.
  • catpnosis
    catpnosis about 6 years
    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.)
  • Abhijit Sarkar
    Abhijit Sarkar about 6 years
    Not a bash wizard here; what's trimmed? Is it a built-in thing or the variable that is being trimmed?
  • Armali
    Armali almost 6 years
    @Abhijit Sarkar - It's the latter.
  • Cecil Curry
    Cecil Curry over 5 years
    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.
  • Cecil Curry
    Cecil Curry over 5 years
    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.
  • scai
    scai over 5 years
    Note that tr -d "[:space:]" removes both horizontal and vertical whitespace characters (=newlines). To remove just horizontal whitespace characters simply use tr -d " ".
  • bfontaine
    bfontaine about 5 years
    Why do you need echo -n at all? echo " my string " | xargs has the same output.
  • rkachach
    rkachach about 5 years
    echo -n removes the end of line as well
  • Maëlan
    Maëlan about 5 years
    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).
  • Maëlan
    Maëlan about 5 years
    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…).
  • Ken Ingram
    Ken Ingram about 5 years
    This was an excellent solution. Just what I was looking for. To use the built-ins. Now I just need the codebook to decipher all of it. Is this solution based on years of experience? I didn't know how BASH could do this, but I had a feeling it should be able to do it.
  • akhan
    akhan almost 5 years
    What is the need for nested double quotes? Seems to work without inner pair.
  • Sasha
    Sasha almost 5 years
    @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).
  • Isaac Freeman
    Isaac Freeman over 4 years
    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.
  • Isaac Freeman
    Isaac Freeman over 4 years
    Oh, and I guess the solution in my last comment requires shopt -s extglob.
  • Victor Sergienko
    Victor Sergienko over 4 years
    To continuously trim all stdin lines, run it through xargs -l.
  • vaab
    vaab over 4 years
    Here's a subtle bug with your current implementation using echo happening at least in bash: try to trim the following string: "-e", it'll output nothing and not the expected string "-e". Indeed, this will end launching echo -n -e which outputs nothing. I would suggest printf "%s" "$var" to replace your final instruction.
  • POMATu
    POMATu over 4 years
    this is bad, it removes backslash from string
  • Admin
    Admin over 4 years
    you are a hero. much easier then perl/sed/bashism
  • miguelmorin
    miguelmorin over 4 years
    @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?
  • Kaii
    Kaii about 4 years
    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 @Sasha already pointed out: it already has some weird behaviours you would not expect when trimming strings!
  • Harlin
    Harlin about 4 years
    Bumped you to 1000 since it had been sitting at 999 for some time :-D
  • Admin
    Admin about 4 years
    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.
  • Brent212
    Brent212 about 4 years
    In 's/ *$//', why are there 2 spaces before the asterisk, instead of a single space? Is that a typo?
  • Paul Tomblin
    Paul Tomblin about 4 years
    @Brent212 because you don't want to match unless there is at least one space. 's/ +$//' might work as well.
  • Brent212
    Brent212 about 4 years
    @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.
  • Paul Tomblin
    Paul Tomblin about 4 years
    You’re thinking of glob, not regex. * means 0 or more of the previous thing, so s/ *$// will match on zero spaces.
  • JPT
    JPT about 4 years
    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)
  • Dima Korobskiy
    Dima Korobskiy almost 4 years
    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.
  • Daniel Alder
    Daniel Alder almost 4 years
    @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, busybox
  • Leon S.
    Leon S. almost 4 years
    @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: stackoverflow.com/a/3352015/3968618
  • Michał Górny
    Michał Górny almost 4 years
    While using builtin functionality seems nice, extglob is very slow in my experience.
  • JL Peyret
    JL Peyret over 3 years
    FWIW, var="${var//[[:space:]]/}" works fine in re-assigning the trimmed contents back to the original variable.
  • Daniel Alder
    Daniel Alder over 3 years
    @DrBeco #!/bin/sh is not necessarily bash ;)
  • DrBeco
    DrBeco over 3 years
    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.
  • Kévin Berthommier
    Kévin Berthommier over 3 years
    xargs <<< " test teste grdgdft"
  • David Scott Kirby
    David Scott Kirby about 3 years
    It takes lots of examples because trimming a whitespace is about as easy as trimming your own hair.
  • David Scott Kirby
    David Scott Kirby about 3 years
    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.
  • Sumit Trehan
    Sumit Trehan about 3 years
    The cleanest solution
  • mc2
    mc2 about 3 years
    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?
  • Alek
    Alek about 3 years
    @akhan, Yes, quotes are NOT needed in variable assignment in BASH: stackoverflow.com/questions/3958681/… unix.stackexchange.com/questions/97560/…
  • Ярослав Рахматуллин
    Ярослав Рахматуллин almost 3 years
    @DrBeco 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.
  • andrew lorien
    andrew lorien almost 3 years
    This helped me, but the question specifically asked about a trailing newlin which this does not remove.
  • AngryDane
    AngryDane almost 3 years
    I tried two other solution from this question/answer page and only this one worked as expected, keep-it-simple-silly strikes again!!
  • Sridhar Sarnobat
    Sridhar Sarnobat almost 3 years
    You could even use process command substitution but I guess that won't work for Bourne Shell
  • Faither
    Faither almost 3 years
    Related to @mklement0 coment: mywiki.wooledge.org/IFS. The default value of IFS is space, tab, newline. (A three-character string.)...
  • Zane
    Zane almost 3 years
    Great. Thanks for the function - added to my local library of valuabe functions.
  • black.swordsman
    black.swordsman over 2 years
    This answer doesn't remove \r or CR from the end of a variable.
  • ardnew
    ardnew over 2 years
    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.
  • uli42
    uli42 about 2 years
    will not work if the_string contains wildcards, e.g. "the_string=" foo * bar "
  • bobbogo
    bobbogo about 2 years
    That formulation for fewer lines is wrong. You can't do ${${f}} in bash.