43

I know how to do this when the variable is pre-defined. However, when asking for the user to enter in some kind of input, how do I trim leading and trailing whitespace? This is what I have so far:

@echo off

set /p input=:
echo. The input is %input% before

::trim left whitespace
for /f "tokens=* delims= " %%a in ("%input%") do set input=%%a
::trim right whitespace (up to 100 spaces at the end)
for /l %%a in (1,1,100) do if "!input:~-1!"==" " set input=!input:~0,-1! 

echo. The input is %input% after

pause
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
OopOop
  • 433
  • 1
  • 4
  • 4

17 Answers17

32

The solution below works very well for me.

Only 4 lines and works with most (all?) characters.


Solution:

:Trim
SetLocal EnableDelayedExpansion
set Params=%*
for /f "tokens=1*" %%a in ("!Params!") do EndLocal & set %1=%%b
exit /b

Test:

@echo off

call :Test1 & call :Test2 & call :Test3 & exit /b

:Trim
SetLocal EnableDelayedExpansion
set Params=%*
for /f "tokens=1*" %%a in ("!Params!") do EndLocal & set %1=%%b
exit /b

:Test1
set Value=   a b c   
set Expected=a b c
call :Trim Actual %Value%
if "%Expected%" == "%Actual%" (echo Test1 passed) else (echo Test1 failed)
exit /b

:Test2
SetLocal EnableDelayedExpansion
set Value=   a \ / : * ? " ' < > | ` ~ @ # $ [ ] & ( ) + - _ = z    
set Expected=a \ / : * ? " ' < > | ` ~ @ # $ [ ] & ( ) + - _ = z
call :Trim Actual !Value!
if !Expected! == !Actual! (echo Test2 passed) else (echo Test2 failed)
exit /b

:Test3
set /p Value="Enter string to trim: " %=%
echo Before: [%Value%]
call :Trim Value %Value%
echo After : [%Value%]
exit /b
Community
  • 1
  • 1
skataben
  • 2,023
  • 1
  • 22
  • 25
  • 2
    can u please explain how does it work, i am interested in line `for /f "tokens=1*" %%a in ("!Params!") do EndLocal & set %1=%%b` especially the part -- `%1=%%b` – Subham Tripathi Apr 09 '15 at 08:19
  • 3
    @SubhamTripathi `tokens=1*`, along with the default delimiter of tab/space, means that if `Params` is for example `var a b c d`, it gets split into token 1 which is `var` and the rest (denoted by the `*`) which is `a b c d`. Token 1 gets assigned to `%%a` whereas * gets assigned to `%%b`. `set %1=%%b` is just assigning * to argument 1 we passed to `:Trim`. – doubleDown Sep 11 '17 at 11:01
  • This helped me immensely. I am saving various data in files: paths, numbers etc. but I always occupy only the first line. When I want to read these, save them into variable and then compare with other values, I need to trim the collected data. I don't know why I need to do that, but your way of trimming values solved my problem :) P.S. For curious ones - I encounter such problem when I read from the file and I need to use delayed expansion at the same time. – ashrasmun Dec 04 '18 at 11:49
  • Great solution, but the syntactical drawback is, you have to give a variable name for the return value to the function. – Mehrdad Mirreza Sep 04 '19 at 12:50
  • Fails for `amp ^& pause`, `"Caret ^"` or `Caret ^^ and exclam !`. It's always a bad idea to call a functions with content, better transport it in variables (call by ref) like `call :trim result var_test1`, but even then it doesn't work for carets combined with exclamation marks – jeb Jan 15 '20 at 08:16
28

You need to enable delayed expansion. Try this:

@echo off
setlocal enabledelayedexpansion
:blah
set /p input=:
echo."%input%"
for /f "tokens=* delims= " %%a in ("%input%") do set input=%%a
for /l %%a in (1,1,100) do if "!input:~-1!"==" " set input=!input:~0,-1!
echo."%input%"
pause
goto blah
Bradley Mountford
  • 8,195
  • 4
  • 41
  • 41
18

A very short & easy solution i'm using is this:

@ECHO OFF

SET /p NAME=- NAME ? 
ECHO "%NAME%"
CALL :TRIM %NAME% NAME
ECHO "%NAME%"
PAUSE

:TRIM
SET %2=%1
GOTO :EOF

Results in:

- NAME ?  my_name
" my_name "
"my_name"
Foumpie
  • 1,465
  • 1
  • 14
  • 10
12

I'd like to present a compact solution using a call by reference (yes, "batch" has pointers too!) to a function, and a "subfunction":

  @ECHO OFF
  SETLOCAL ENABLEDELAYEDEXPANSION

  SET /p NAME=- NAME ? 
  ECHO "%NAME%"
  CALL :TRIM NAME
  ECHO "%NAME%"
  GOTO :EOF

  :TRIM
  SetLocal EnableDelayedExpansion
  Call :TRIMSUB %%%1%%
  EndLocal & set %1=%tempvar%
  GOTO :EOF

  :TRIMSUB
  set tempvar=%*
  GOTO :EOF
Mehrdad Mirreza
  • 984
  • 12
  • 20
6

To improve on Forumpie's answer, the trick is using %*(all params) in the sub:

Edit: Added echo of the TRIM subroutines params, to provide more insight

@ECHO OFF

SET /p NAME=- NAME ? 
ECHO "%NAME%"
CALL :TRIM %NAME%
SET NAME=%TRIMRESULT%
ECHO "%NAME%"

GOTO :EOF

:TRIM
  echo "%1"
  echo "%2"
  echo "%3"
  echo "%4"
  SET TRIMRESULT=%*
GOTO :EOF

This strips leading and trailing spaces, but keeps all spaces between.

"  1 2    3 4    "
"1 2    3 4"

Details of %*: Batch Parameters

Patrick
  • 1,089
  • 14
  • 17
  • 2
    ... and which benefit does you solution provide compared to [mine](http://stackoverflow.com/a/19686956/880076), which is posted two years ago? You avoid using the second function, but then you need to use a new variable to get the result, instead of changing the original variable directly! – Mehrdad Mirreza Nov 06 '15 at 13:24
  • Youre right, my answer is halfway between forumpies and yours. I must have got carried away playing with the code. – Patrick Nov 09 '15 at 23:13
  • 1
    This is very good and i prefer it to all other solutions. It's few lines of code, and clean looking. Can you please explain exactly what is happening at "CALL :TRIM %NAME%" and what is the %* as variable trimresult meaning? Thank you, it's nice to understand why things work :) – Rakha Oct 25 '17 at 12:54
  • @Rakha Hi mate, I update the answer. Please see the web link for an explination of %* – Patrick Oct 26 '17 at 23:19
  • 1
    It works for trivial content only. It fails for `Caret ^`, `amp^&`, `Exclams!`, ... – jeb Jan 15 '20 at 08:19
3

This was my quick-and-dirty solution. Note that it will have unexpected results if the input text has characters that are forbidden in file names.

@echo off
set /p input=Enter some text with spaces before and after^>
echo "%input%"
for /f "tokens=*" %%i in ('echo %input%') do set trimmed=%%~nxi
echo "%trimmed%"

EDIT

To provide a more robust text input character set and still remove the single trailing space that would normally be left by the for in ('echo...') approach, this is an alternative:

set /p input=Enter some text with spaces before and after^>
for /f "tokens=*" %%i in ('echo %input%') do set j=%%i
set trimmed=%j:~0,-1%
echo "%trimmed%"
Jamesfo
  • 524
  • 6
  • 11
  • This is effectivly the same answer like the one from [@Dr.belisarius 2010](https://stackoverflow.com/a/3002029/463115) – jeb Feb 02 '21 at 11:57
  • 1
    @jeb Uhh, well yeah, except that answer didn't address the question of user input, and enableextensions and enabledelayedexpansion are unnecessary, and delims is unnecessary, and it leaves a trailing space if the input contains a trailing space...which kind of fails the whole issue, doesn't it? Did you even test the code? – Jamesfo Feb 03 '21 at 05:53
  • Yes the original code could be improved a bit, but there isn't a problem with trailing spaces, and btw your code fails completely with input like ` c:\temp\foo ` – jeb Feb 03 '21 at 10:59
  • But the question is about removing leading/trailing white spaces. Neither `%%~f` nor `%%~ni` can do that for any content, they are for filename handling, but fail with ` ::\What is\..\this ` – jeb Feb 04 '21 at 07:29
  • The point I was trying to make is that leading and trailing spaces can be trimmed in one short `for /f` statement, like @seagull 's implementation. However, the final token will still contain a space which is retained in the output. The ~n fixes that, but if it's too quick-and-dirty it can be removed in a more robust way via variable substrings. I've edited to include that approach. – Jamesfo Feb 04 '21 at 08:32
  • Good try, but always trimming the last character fails, when there is not exactlty one trailing space (0 or >1). Btw `('echo %input%')` should be replaced by `("%input%")` or even better `("!input!")` that works also with content like `&<>"&` – jeb Feb 04 '21 at 09:16
2

You can also strip whitespace by passing in those variables with whitespace as parameters into a sub and then inside your sub simply set them again with the passed in parameters.

@echo off 
set "a=    apple      "
set "b=     banana    "
echo [%a%]
echo [%b%]
call :Strip %a% %b%
pause
goto :EOF

:Strip
set a=%1
set b=%2
echo [%a%]
echo [%b%]

----------------
Results....
[    apple      ]
[     banana    ]
[apple]
[banana]
Press any key to continue . . .
Marc Mustric
  • 109
  • 1
  • 2
1

The :trimAll subroutine below:

  • trims leading and trailing tabs and spaces from the string passed to it
  • can be safely CALLed with delayed expansion disabled or enabled
  • handles poison characters (eg, !, %, ^, etc.)
  • CRs and LFs are not supported

Read comments for references and info.

@echo off & setLocal enableExtensions disableDelayedExpansion
:: https://www.dostips.com/forum/viewtopic.php?t=4308
(call;) %= sets errorLevel to 0 =%
(set lf=^
%= BLANK LINE REQUIRED =%
)
:: kudos to Carlos for superior method of capturing CR
:: https://www.dostips.com/forum/viewtopic.php?p=40757#p40757
set "cr=" & if not defined cr for /f "skip=1" %%C in (
    'echo(^|replace ? . /w /u'
) do set "cr=%%C"

set ^"orig=  !random!  !  ^^!  ^^^^!  ^"^^  ^&^"^&  ^^^" %%os%%  ^"

call :trimAll res1 orig
setLocal enableDelayedExpansion
call :trimAll res2 orig
echo(orig: [!orig!]
echo(res1: [!res1!]
echo(res2: [!res2!]
endLocal

endLocal & goto :EOF

:trimAll result= original=
:: trims leading and trailing whitespace from a string
:: special thanks to Jeb for
:: https://stackoverflow.com/a/8257951
setLocal
set "ddx=!" %= is delayed expansion enabled or disabled? =%
setLocal enableDelayedExpansion
set "die=" & if not defined %2 (
    >&2 echo(  ERROR: var "%2" not defined & set "die=1"
) else set "str=!%2!" %= if =%

if not defined die for %%L in ("!lf!") ^
do if "!str!" neq "!str:%%~L=!" (
    >&2 echo(  ERROR: var "%2" contains linefeeds & set "die=1"
) %= if =%

if not defined die for %%C in ("!cr!") ^
do if "!str!" neq "!str:%%~C=!" (
    >&2 echo(  ERROR: var "%2" contains carriage returns
    set "die=1"
) %= if =%

if defined die goto die

(for /f eol^= %%A in ("!str!") do rem nop
) || (
    >&2 echo(WARNING: var "%2" consists entirely of whitespace
    endLocal & endLocal & set "%1=" & exit /b 0
) %= cond exec =%

:: prepare string for trimming...
:: double carets
set "str=!str:^=^^^^!"
:: double quotes
set "str=!str:"=""!"
:: escape exclaims
set "str=%str:!=^^^!%" !

:: act of CALLing subfunction with
:: expanded string trims trailing whitespace
call :_trimAll "%%str%%

:: prepare string to be passed over endLocal boundary...
:: double carets again if delayed expansion enabled
if not defined ddx set "str=!str:^=^^^^!"
:: escape exclaims again if delayed expansion enabled
if not defined ddx set "str=%str:!=^^^!%" !
:: restore quotes
set "str=!str:""="!"

:: pass string over endLocal boundary and trim leading whitespace
for /f tokens^=*^ eol^= %%a in ("!str!") do (
    endLocal & endLocal & set "%1=%%a" !
) %= for /f =%
exit /b 0

:die
endLocal & endLocal & set "%1=" & exit /b 1

:_trimAll
:: subfunction
:: trailing exclaim is required as explained by Jeb at
:: https://www.dostips.com/forum/viewtopic.php?p=6933#p6933
set "str=%~1" !
exit /b 0

HTH and HNY! ;)

Sponge Belly
  • 121
  • 1
  • 4
1

Just came up with this:

set sString=" hello|123(4) "
call :trimMENew %sString%
echo "%sString%"
exit /b 0

:trimMeNew
set "sString=%~1"
if "%sString:~0,1%" == " " set "sString=%sString:~1%"
if "%sString:~-1%" == " " set "sString=%sString:~0,-1%"
exit /b 0
Tadaaaaa
  • 11
  • 1
  • 1
    the question implies, there could be more than one leading or trailing spaces. – Stephan Jul 16 '20 at 20:07
  • Ah good point ... I didnt catch that, yeah for dynamic spacing amounts it becomes more of a challenge =) I guess for that you could always loop front and back and assign "" till you come across non whitespace =) – Tadaaaaa Jul 16 '20 at 20:32
1

I've found that all solution introduced here are not complete enough for me and does not work in one or another case.

CAUTION:

Seems the stackoverflow incorrectly handles tabulation characters (and loses other characters like \x01) in the copy-pasted code, so the below code might not work if you copy it directly by CTRL+C. Use the link at the end to directly download the scripts.

trim_var.bat:

@echo off

rem drop the output variable value
if not "%~2" == "" if not "%~1" == "%~2" set "%~2="

if not defined %~1 exit /b 0

setlocal DISABLEDELAYEDEXPANSION

rem Load and replace a value quote characters by the \x01 character.
call set "RETURN_VALUE=%%%~1:"=%%"

:TRIM_LEFT_LOOP
if not defined RETURN_VALUE exit /b 0
if not ^%RETURN_VALUE:~0,1%/ == ^ / if not ^%RETURN_VALUE:~0,1%/ == ^   / goto TRIM_RIGHT_LOOP
set "RETURN_VALUE=%RETURN_VALUE:~1%"
if not defined RETURN_VALUE exit /b 0
goto TRIM_LEFT_LOOP

:TRIM_RIGHT_LOOP
if not defined RETURN_VALUE exit /b 0
if not ^%RETURN_VALUE:~-1%/ == ^ / if not ^%RETURN_VALUE:~-1%/ == ^ / goto TRIM_RIGHT_LOOP_END
set "RETURN_VALUE=%RETURN_VALUE:~0,-1%"
goto TRIM_RIGHT_LOOP

:TRIM_RIGHT_LOOP_END
rem recode quote and exclamation characters
set "__ESC__=^"
set __QUOT__=^"
set "__EXCL__=!"
set "RETURN_VALUE=%RETURN_VALUE:!=!__EXCL__!%"
set "RETURN_VALUE=%RETURN_VALUE:^=!__ESC__!%"
set "RETURN_VALUE=%RETURN_VALUE:=!__QUOT__!%"

rem safe set
setlocal ENABLEDELAYEDEXPANSION
for /F "tokens=* delims=" %%i in ("!RETURN_VALUE!") do for /F "tokens=* delims=" %%j in ("%%i") do (
  endlocal
  endlocal
  if not "%~2" == "" (
    set "%~2=%%j"
  ) else (
    set "%~1=%%j"
  )
)

exit /b 0

echo_var.bat:

@echo off

if not defined %~1 (
  echo.%~2%~3
  exit /b 0
)

setlocal DISABLEDELAYEDEXPANSION

rem Load and replace a value quote characters by the \x01 character.
call set "RETURN_VALUE=%%%~1:"=%%"

rem recode quote and exclamation characters
set "__ESC__=^"
set __QUOT__=^"
set "__EXCL__=!"
set "RETURN_VALUE=%RETURN_VALUE:!=!__EXCL__!%"
set "RETURN_VALUE=%RETURN_VALUE:^=!__ESC__!%"
set "RETURN_VALUE=%RETURN_VALUE:=!__QUOT__!%"

rem safe echo
setlocal ENABLEDELAYEDEXPANSION
for /F "tokens=* delims=" %%i in ("!RETURN_VALUE!") do for /F "tokens=* delims=" %%j in ("%%i") do (
  endlocal
  endlocal
  echo.%~2%%j%~3
)

exit /b 0

test_trim_var.bat:

@echo off

setlocal DISABLEDELAYEDEXPANSION

set myvar1=              1 ! 2 ^| 3 ^& 4 ^^ 5 = 6 , 7 ; 8 * 9 # 0 %% 1 / 2 \ 3 ? 4 ^> 5 ^< 6 " 7             

call "trim_var.bat" myvar1 myvar2

call "echo_var.bat" myvar1 - -
call "echo_var.bat" myvar2 - -

Output:

-                        1 ! 2 | 3 & 4 ^ 5 = 6 , 7 ; 8 * 9 # 0 % 1 / 2 \ 3 ? 4 > 5 < 6 " 7               -
-1 ! 2 | 3 & 4 ^ 5 = 6 , 7 ; 8 * 9 # 0 % 1 / 2 \ 3 ? 4 > 5 < 6 " 7-

The latest implementation: https://github.com/andry81/contools/tree/HEAD/Scripts/Tools/std/trim_var.bat

Pros:

  • Safely handles almost all specific characters like !, %, ^, ".

Cons:

  • The " character replaces by the \x01 character and might be affected by current code page (not tested).
Andry
  • 2,273
  • 29
  • 28
0
@echo off
setlocal EnableDelayedExpansion
set S=  This  is  a  test
echo %S%.
for /f "tokens=* delims= " %%a in ('echo %S%') do (set b=%%a & set b=!b: =! & echo !b!)
endlocal & goto :EOF

or

@echo off
setlocal EnableDelayedExpansion
set S=  This  is  a  test
echo %S%.
for /f "tokens=* delims= " %%a in ('echo %S%') do (set b=%%a & set b=!b: =_! & echo !b!)
endlocal & goto :EOF
catfood
  • 4,267
  • 5
  • 29
  • 55
0
  @echo off & setlocal enableextensions
  rem enabledelayedexpansion
  set S=  This  is  a  test
  echo %S%.
  for /f "tokens=* delims= " %%a in ('echo %S%') do set S=%%a
  echo %S%.
  endlocal & goto :EOF

from http://www.netikka.net/tsneti/info/tscmd079.htm

for removing leading spaces.

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
0

I did it like this (temporarily turning on delayed expansion):

      ...
sqlcmd -b -S %COMPUTERNAME% -E -d %DBNAME% -Q "SELECT label from document WHERE label = '%DOCID%';" -h-1 -o Result.txt
      if errorlevel 1 goto INVALID
    :: Read SQL result and trim trailing whitespace
    SET /P ITEM=<Result.txt
    @echo ITEM is %ITEM%.
    setlocal enabledelayedexpansion
    for /l %%a in (1,1,100) do if "!ITEM:~-1!"==" " set ITEM=!ITEM:~0,-1!
    setlocal disabledelayedexpansion
    @echo Var ITEM=%ITEM% now has trailing spaces trimmed.
....
djangofan
  • 28,471
  • 61
  • 196
  • 289
0
for /f "usebackq tokens=*" %%a in (`echo %StringWithLeadingSpaces%`) do set StringWithout=%%a

This is very simple. for without any parameters considers spaces to be delimiters; setting "*" as the tokens parameter causes the program to gather up all the parts of the string that are not spaces and place them into a new string into which it inserts gaps of its own.

seagull
  • 187
  • 11
0

I had to create a Function.

Use it as:

@echo off
SETLOCAL ENABLEDELAYEDEXPANSION


set "TextString=    Purple Cows are flying in the Air Tonight         "
echo REM ^^Notice there is whitespace at the start and end of the TextString
echo "!TextString!"
CALL:trimWhiteSpace "!TextString!" OutPutString
echo Resulting Trimmed Text: "!OutPutString!"
echo REM ^^Now there should be no White space at the start or end.

Exit /B

Add this to the bottom of your batch file:

:FUNCTIONS
@REM FUNCTIONS AREA
GOTO:EOF
EXIT /B


::TRIM FUNCTIONS AREA::
:: USAGE:
:: trimWhiteSpace "!InputText!" OutputText
:: Remember to use the "! on the input text, but NOT on the Output text.
:: The Following is Wrong: trimWhiteSpace "!InputText!" !OutputText!
:: ^^Because it has a ! around the OutPutText
:: Make Sure to add " around the InputText when running the call.
:: If you don't add the " then it will only accept the first word before a space.
::Example:
::  set "TextString=    Purple Cows are flying in the Air Tonight         "
::  echo REM ^^Notice there is whitespace at the start and end of the TextString
::  echo "!TextString!"
::  CALL:trimWhiteSpace "!TextString!" OutPutString
::  echo Resulting Trimmed Text: "!OutPutString!"
::  echo REM ^^Now there should be no White space at the start or end.

:trimWhiteSpace
set textToTrim=%~1
CALL:trimWhiteSpaceOnTheRight "!textToTrim!" OutPutString
SET textToTrim=!OutPutString!
CALL:trimWhiteSpaceOnTheLeft "!textToTrim!" OutPutString
SET %2=!OutPutString!
GOTO:EOF

:trimWhiteSpaceOnTheRight
set str=%~1
for /l %%a in (1,1,31) do if "!str:~-1!"==" " set str=!str:~0,-1!
SET %2=%str%
GOTO:EOF

:trimWhiteSpaceOnTheLeft
set str=%~1
for /f "tokens=* delims= " %%a in ("%str%") do set str=%%a
SET %2=%str%
GOTO:EOF

And remember you MUST add "SETLOCAL ENABLEDELAYEDEXPANSION" to the top of your batch file or else none of this will work properly.

SETLOCAL ENABLEDELAYEDEXPANSION
@REM # Remember to add this to the top of your batch file.
-2

set newVarNoSpaces=%someVarWithSpaces: =%

  • 2
    This is wrong because it removes all spaces in the variables and not just leading and trailing spaces which is what the asker wants. – doubleDown Sep 11 '17 at 11:03
-5

Thank you @Bradley Mountford

I'm using the "Trim Right Whitespace" exactly working on my "Show-Grp-of-UID.CMD". :) Other idea for improvement are welcome.. ^_^

Rhak Kahr
  • 740
  • 7
  • 10
  • 1
    Posting code as a screen grab makes it more difficult for others to reuse. It would also be easier to understand your solution if it wasn't part of an existing solution ;-). – rossmcm Oct 03 '21 at 21:33