Remove quotes from named environment variables in Windows scripts - windows

I want to store a URL prefix in an Windows environment variable. The ampersands in the query string makes this troublesome though.
For example: I have a URL prefix of http://example.com?foo=1&bar= and want to create a full URL by providing a value for the bar parameter. I then want to launch that URL using the "start" command.
Adding quotes around the value for the SET operation is easy enough:
set myvar="http://example.com?foo=1&bar="
Windows includes the quotes in the actual value though (thanks Windows!):
echo %myvar%
"http://example.com?foo=1&bar=true"
I know that I can strip quotes away from batch file arguments by using tilde:
echo %~1
However, I can't seem to do it to named variables:
echo %~myvar%
%~myvar%
What's the syntax for accomplishing this?

echo %myvar:"=%

This is not a limitation of the environment variable, but rather the command shell.
Enclose the entire assignment in quotes:
set "myvar=http://example.com?foo=1&bar="
Though if you try to echo this, it will complain as the shell will see a break in there.
You can echo it by enclosing the var name in quotes:
echo "%myvar%"
Or better, just use the set command to view the contents:
set myvar

While there are several good answers already, another way to remove quotes is to use a simple subroutine:
:unquote
set %1=%~2
goto :EOF
Here's a complete usage example:
#echo off
setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS
set words="Two words"
call :unquote words %words%
echo %words%
set quoted="Now is the time"
call :unquote unquoted %quoted%
echo %unquoted%
set word=NoQuoteTest
call :unquote word %word%
echo %word%
goto :EOF
:unquote
set %1=%~2
goto :EOF

This works
for %a in (%myvar%) do set myvar=%~a
I would also use this if I wanted to print a variable that contained and ampersand without the quotes.
for %a in ("fish & chips") do echo %~a

To remove only beginning and ending quotes from a variable:
SET myvar=###%myvar%###
SET myvar=%myvar:"###=%
SET myvar=%myvar:###"=%
SET myvar=%myvar:###=%
This assumes you don't have a ###" or "### inside your value, and does not work if the variable is NULL.
Credit goes to http://ss64.com/nt/syntax-esc.html for this method.

Use delayed environment variable expansion and use !var:~1,-1! to remove the quotes:
#echo off
setlocal enabledelayedexpansion
set myvar="http://example.com?foo=1&bar="
set myvarWithoutQuotes=!myvar:~1,-1!
echo !myvarWithoutQuotes!

Use multiple variables to do it:
set myvar="http://example.com?foo=1&bar="
set bar=true
set launch=%testvar:,-1%%bar%"
start iexplore %launch%

#echo off
set "myvar=http://example.com?foo=1&bar="
setlocal EnableDelayedExpansion
echo !myvar!
This is because the variable contains special shell characters.

I think this should do it:
for /f "tokens=*" %i in (%myvar%) do set %myvar%=%~i
But you do not need this,
set myvar="http://example.com?foo=1&bar="
start "" %myvar%
Will work too, you just need to supply a title to the start command.

Related

Trying to concatenate the last 10 lines of a log file to a batch variable using powershell

I'm new to Windows scripting, but have quite a lot of experience in bash and python.
Here's the issue. Whenever I run this, (and this is the best result I've gotten so far) it makes it most of the way through and then errors with "The filename, directory name, or volume label syntax is incorrect."
Ignore the code designed for newlines, I'm still fighting with that as well.
setlocal EnableDelayedExpansion
set LF=^
set LAST_TEN=Here are the last 10 lines of the download log:
for /f "tokens=* usebackq" %%x in (`powershell -command "& {Get-Content download.log | Select-Object -last 10 | ForEach-Object {$_.substring(2)}}"`) do (
set LAST_TEN=!LAST_TEN!%%x
)
echo %LAST_TEN%
The reason I'm taking the substring is because some of the lines in the logfile start with < and > . I thought that was my only issue, but that is not the case. Please let me know if any more info is needed. Thank you!
Note: Your own answer shows the effective solution, but I thought I'd provide some background information.
Squashman has provided the crucial pointer:
Switching from echo %LAST_TEN% to echo !LAST_TEN! avoids problems with metacharacters (special characters such as < and >) in the variable value, which are what caused your error message.
The alternative would be to double-quote the variable reference - echo "%LAST_TEN%" - but, sadly, the double quotes are then included in the output.
In other words: If you need to echo the value of a variable that (potentially) contains metacharacters unquoted:
Place setlocal EnableDelayedExpansion at the start of your batch file.
Then reference the variable of interest as !VAR! instead of %VAR%: the delayed expansion this results in prevents the value from becoming part of the source-code line that cmd.exe parses (due to the macro-style up-front expansion that happens with %VAR%).
As an aside: Loop variables - such as %%x in your code - despite using % rather than ! as the delimiter, are of necessity always expanded in a delayed fashion, which is the reason that set LAST_TEN=!LAST_TEN!%%x worked even without the double-quoting around enclosing both the variable name and value that is normally required for literals and values of non-delayed variable references containing metacharacters (e.g.
set "LAST_TEN=a < b")
A simplified example:
#echo off
setlocal enableDelayedExpansion
:: Define a sample variable
:: Note the "..." enclosing both the name and the value.
set "var=a value with metacharacters: < > & |"
:: Thanks to using !var!, echoing the value *unquoted* works
echo !var!
Scoping setlocal enableDelayedExpansion:
One pitfall of delayed expansion is that that all ! characters are then considered part of delayed variable references, typically resulting in their quiet removal; e.g., echo hi! outputs just hi.
To escape ! characters in literal strings that should be used verbatim, you need ^^! (sic) in unquoted strings, and ^! inside "...".
The escaping is also needed for %...% variable references (e.g., echo %var:!=^^!%), but is again avoided for !...! ones.
To avoid such escaping headaches you can enable setlocal enableDelayedExpansion on demand, for a given line or block of lines, and disable it again with endlocal:
#echo off
:: Define a sample variable
:: Note the "..." enclosing both the name and the value.
set "var=a value with metacharacters: < > & |"
:: Because setlocal enableDelayedExpansion is NOT (yet)
:: in effect, the use of "!" is not a problem.
echo hi!
:: Localize the use of delayed expansion
setlocal enableDelayedExpansion
echo !var!
endlocal
:: Use of "!" is again fine.
echo hi again!
Caveat: Since setlocal creates a copy of the environment variables, which endlocal then discards, do not try to set variables between setlocal and endlocal if you need later code to see these changes.
As you're already using PowerShell, why not let it do the donkey work?
Grab the last ten lines and concatenate them within parentheses, for example:
For /F Delims^=^ EOL^= %%G In ('%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -NoProfile -Command "(Get-Content 'download.log' | Select-Object -Last 10) -Join ''"') Do Set "LAST_TEN=%%G"
Changed
echo %LAST_TEN%
to
echo !LAST_TEN!

windows batch command nested variable

I have a requirement to use nested variables for creating a folder based on a environment variables.
Assume I have variables listed below:
%ABC_ASIA_LOCATION%
%ABC_EUROPE_LOCATION%
%ABC_US_LOCATION%
and I want to pass the country as variable like %ABC_%COUNTRY%_LOCATION%.
How do I achieve this in Windows utilizing batch scripting?
you have to enclose each variable into %:
set "ABC=ABC"
set "COUNTRY=EUROPE
set "LOCATION=MUNICH
echo %ABC%_%COUNTRY%_%LOCATION%
Result: ABC_EUROPE_MUNICH
Or if you just want Country as a variable, keeping the rest fixed:
echo ABC_%COUNTRY%_LOCATION
Result: ABC_EUROPE_LOCATION
or if you want the whole thing to be a variable (a variable name containing another variable), you have to use delayed expansion:
setlocal enabledelayedexpansion
set country=EUROPE
set "ABC_EUROPE_LOCATION=a town in southern Germany"
echo !ABC_%country%_LOCATION!
which gives you: a town in southern Germany
Note: setlocal has no effect outside of batchfiles, so delayed expansion works only:
- in batchfiles
- when the command prompt was started with delayed expansion enabled (cmd /v:on) (by default, the command prompt runs with delayed expansion disabled)
There are times when you need the nested variable to work inside a for loop, which already requires the !varname! syntax for the variable expansion. When this is the case, !ABC_%country%LOCATION!, will not work (reference Stephan's post on 9/6/2017 at 7:24). Neither will !ABC!country!_LOCATION!.
The following batch file demonstrates this. This is a somewhat contrived example to demonstrate the issue. In the subroutine, we can also set a variable to the nested value if you didn't want to do the work in the subroutine.
#echo off
setlocal enabledelayedexpansion
setlocal
set var1=value1
set var2=value2
set var3=value3
set var4=value4
set var5=value5
for %%A in (var1 var2 var3 var4 var5) do (
set varname=%%A
echo 1.This will not work: !varname!=!!varname!!
echo 2.This will not work: !varname!=%!varname!%
echo 3.This will not work: !varname!=!%varname%!
echo 4.This will not work: %varname%=%varname%
echo 5.This will not work: %varname%=%%varname%%
call:NestedVar %%A
call:getNestedVar new%%A %%A
echo new%%A=!new%%A!
echo.
echo.
)
goto:eof
:NestedVar
echo This will work: %1=!%1! (but only if setlocal enabledelayedexpansion is used)
goto:eof
:getNestedVar
REM Use: getNestedVar newVariableName varName
REM Will set newVariableName to the value of varName
echo Setting variable, %1=!%2!
set %1=!%2!
goto:eof

How to delete quotation marks from a text file using batch file?

I am trying to edit out the quotation marks that have been inputed into a text file using .bat.
echo %name%>./User_Records/%username%.txt
in the text file it is saving as
"Firstname Lastname"
I am trying to add to the batch file so that it will edit the *.txt file and delete the quotation marks if they are saved in that text file.
Can anyone help me?
I have been trying to do this for weeks. I want the output to look like
Firstname Lastname
Try replacing all instances of " in the %name% variable by using Environment variable substitution (see set /? for more)
#echo off
set "name=%name:"=%"
echo %name%>./User_Records/%username%.txt
If you are trying to replace the quotation marks after the text file has been saved, then refer to this previous question
echo %name:"=%>.\User_Records\%username%.txt
should strip the quotes before they are recorded, if that's what your question is.
Note that path-separators in windows are \ not /.
But - if your question is about files that already exist then probably the easiest way is to use your editor. Depends a little on quite how many files you have to process - which you haven't specified.
The bat file you are using should really be altered, especially as the line you've provided from it has some issues, (mostly already mentioned).
SetLocal EnableDelayedExpansion
(Echo=!name:"=!)>"User_Records\%username%.txt"
EndLocal
If you have no control over that bat file then something like this should do what you want:
#For /F "UseBackQ Delims=" %%A In ("User_Records\%username%.txt") Do #Echo(%%~A
Alternatively:
#Set/P "FullName="<"User_Records\%username%.txt"
#Echo(%FullName:"=%
If the name read from text file is assigned to environment variable name using set name="..." syntax, then this is the cause of the double quotes in output string.
It makes a big difference if string assigned to environment variable is enclosed in double quotes or the entire parameter string of command SET. Read answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? to get knowledge about the big difference between using set "variable=string" versus set variable="string".
But it is of course possible to remove all double quotes from string assigned currently to environment variable name by using a string substitution:
set "name=%name:"=%"
All occurrences of " in string of name are replaced by an empty string and resulting string is assigned again to environment variable name.
But be aware that this command line
echo %name%>"./User_Records/%username%.txt"
could result in an unwanted behavior, for example if the name assigned to environment variable name is C&A. The ampersand found by Windows command interpreter after expanding environment variable name and before execution of command ECHO is interpreted now as AND operator and not anymore as literal character to output by ECHO.
One solution is using delayed expansion, for example:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Environment variable name is defined with delayed expansion disabled
rem making it possible to assign an exclamation mark as literal character
rem without the need to escape it as it would be necessary when delayed
rem expansion would be enabled already here at this time.
set "name=C&A!"
rem Enable delayed expansion which results in pushing on stack current
rem state of command extensions and of delayed expansion, the current
rem directory path and the pointer to current environment variables list
rem before creating a copy of all environment variables being used further.
setlocal EnableDelayedExpansion
rem Output the value of environment variable name using delayed expansion
rem with redirection into a text file being named like the currently used
rem user account name which of course can contain a space character or
rem other characters requiring enclosed file name with relative path in
rem double quotes.
echo !name!>".\User_Records\%username%.txt"
rem Delete all environment variables, restore pointer to previous set of
rem environment variables, restore current working directory from stack
rem restore states of command extension and delayed expansion from stack.
endlocal
rem Explicitly call ENDLOCAL once again for initial SETLOCAL command.
rem That would not be necessary because Windows command interpreter
rem runs implicitly ENDLOCAL for each local environment still being
rem pushed on stack.
endlocal
An alternate solution is using command FOR for an implicit delayed expansion:
#echo off
set "name=C&A!"
set "name=%name:"=%"
for /F "delims=" %%I in ("%name%") do echo %%I>".\User_Records\%username%.txt"
set "name=
See also the answers on Batch: Auto escape special characters.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators.

Batch scripting: Why does this condition evaluate to true?

Create a Batch file with the following contents:
#echo off
setlocal
echo %1
if [%1] == [] echo hi
Then run it from the command prompt like so:
script.cmd ==
It will output the following:
ECHO is off.
hi
Why is this, and how does it happen? Also, how can I more reliably check for empty strings in my batch scripts?
Thanks.
In this case, I suggest using the following:
#echo off
setlocal
echo(%~1
if "%~1"=="" echo hi
And instead of script.cmd == use script.cmd "=="
Note that I used echo( which will echo a newline if the variable is seen as empty, and that I used %~1 which removes surrounding quotes from the %1 argument.
You need the surrounding quotes because an equal-sign is treated as a delimeter in the arguments, unless inside quotes, just like spaces.
#ECHO OFF
SETLOCAL
SET "cmdtail=%*"
ECHO(%cmdtail%
IF "%cmdtail%"=="==" ECHO two "="
But back to the unstated original problem.
Note that this syntax will detect the == provided as the command tail whereas %~1 will not evaluate to ==.

How can I set a variable in a long one liner using a windows cmdline?

For example:
I want to set a variable and then have it output on the same line.
set /p MESSAGE= && echo %MESSAGE%
But it doesn't quite work like you expect. Is there a way to pipe it to echo or is there a better way to delimit separate commands?
Thanks.
This cannot work the way you try here since environment variables are expanded while parsing a line, not when executing it. Since %MESSAGE% is only meaningful after executing the first part, this cannot work.
This however will:
setlocal enabledelayedexpansion
set /p MESSAGE= && echo !MESSAGE!
See help set for a discussion of delayed expansion.

Resources