从批处理文件中的变量中删除双引号会导致 CMD 环境出现问题
Posted
技术标签:
【中文标题】从批处理文件中的变量中删除双引号会导致 CMD 环境出现问题【英文标题】:Removing double quotes from variables in batch file creates problems with CMD environment 【发布时间】:2010-12-30 04:03:07 【问题描述】:任何人都可以通过有效且安全的方式从批处理变量中删除引号吗?
我编写了一个批处理文件,它成功地导入了参数列表 %1、%2、%3 等,并将它们放入命名变量中。其中一些参数包含多个单词,因此用双引号括起来。
> "Susie Jo" (%1)
> "Smith Barnes" (%2)
> "123 E. Main St." (%3)
接下来将这些 %variables 放在命名变量中:
> set FirstName=%1
> set LastName=%2
> set ShipAddr=%3
变量的验证由 echo 完成。 > 回声。%FirstName% > 回声。%LastName% > echo.%ShipAddr%
结果显示为
"Susie Jo"
"Smith Barnes"
"123 E. Main St."
我需要消除所选变量中包含的引号。例如,FirstName 和 LastName 在其他地方使用,并且不能包含引号。
在一个测试批处理文件中,我成功地在变量中使用 ~tilde 字符消除了引号。
> set FirstName=%~1
> set LastName=%~2
我以为我有解决方案,但我很快就遇到了执行批处理文件的异常行为。突然,CMD 无法识别长路径语句。从完整路径正常执行批处理文件
> C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
返回
> 'C:\Documents' is not recognized as an internal or external command....
因此,在传入的 %1 %2...%n 变量中添加 ~波浪号似乎会引起一些变化。可能某些环境变量已被更改?
我还尝试使用 FOR 命令通过各种尝试从变量中清除引号。这看起来很尴尬,我无法通过创建一个变量列表来完成这项任务:
类似这样的:
for %%g in (%FirstName% %LastName%) do (
set %%g=%%~g
set %%h=%%~h
set FirstName=%%~g
set LastName=%%h
echo.%FirstName% %LastName%
)
我认为我有两个问题。
我在传入的 %1 %2 变量(%~1 等)中插入 ~ 波浪号的“简短而甜蜜”的想法似乎影响了某些设置并改变了 CMD 导航长路径名的方式。
我仍在寻找一种简洁的方法来消除选定命名变量中的引号。
对于那些更有经验的人的任何帮助将不胜感激。我的技能已经到了尽头...请指导一下!
编辑 2009 年 12 月 26 日 13:36 PST 整个批处理文件:
:: dataout.bat
:: revision 12/25/2009 add ~tilde to incoming %variables to eliminate embedded "quotation marks.
:: writes address list using command line parameters
:: writes data output list for QBooks IIF import
:: writes Merchant Order data for RUI
:: sample command line string for testing
:: listmail[firstname][lastname]["address string"]["city string"][state][zip][Order#][PurchDate][Regname]["FirstName LastName"][TransactionID][PaymentMethod][Total][ProductID][Qty][Price_Each][PackPrep] [Shipping] [CommissionPmt] [Invoice#]
:: example: dataout Bellewinkle Moose "123 Green Forest Way" "Vancouver" WA 98664 1004968 05/25/2009 "Bellewinkle Moose" "Olive Oyl" 101738 "On Account" 20.67 FK-1P 1 8.95 3.00 1.39 239
@echo off
cls
c:
cd\
cd documents and settings\administrator\my documents\txt\batchtest
echo processing %1 %2
:VARISET
:: Convert %n command line parameters to string variables
set ($FirstName)=%~1
set ($LastName)=%~2
set ($BillingAddress1)=%~3
set ($BillingCity)=%~4
set ($BillingState)=%~5
set ($BillingPostal)=%~6
set ($OrderNumber)=%~7
set ($Purch_Date)=%~8
set ($RegistrationName)=%~9
shift
set ($TransactionID)=%~9
shift
set ($PaymentMethod)=%~9
shift
set ($Total)=%~9
shift
set ($ProductIdentifier)=%~9
shift
set ($Quantity)=%~9
shift
set ($Price_Each)=%~9
shift
set ($Pack_Prep)=%~9
shift
set ($Shipping)=%~9
shift
set ($ServiceFee)=%~9
shift
set ($Discount)=%~9
shift
set ($Invoice)=%~9
shift
set ($UnitPrice)=%~9
set _ShipCombName=%($FirstName)% %($LastName)%
echo ship combo name is %_ShipCombName%
pause
:: write string varibables to logfile
echo FN %($FirstName)% LN %($LastName)% BA %($BillingAddress1)% %($BillingCity)% %($BillingState)% %($BillingPostal)% %($OrderNumber)% %($Purch_Date)% %($RegistrationName)% %($TransactionID)% %($PaymentMethod)% %($Total)% %($ProductIdentifier)% %($Quantity)% %($Price_Each)% %($Pack_Prep)% %($Shipping)% %($ServiceFee)% %($Discount)% %($Invoice)% %($UnitPrice)% %_ShipCombName% >> d_out_log.txt
:: Assign Account by Service Provider
IF /i %($PaymentMethod)%==Amazon Receivables SET _QBAcct=Amazon.com
:: 12-25-2009 added second Amazon pm't method for versatility
IF /i %($PaymentMethod)%==Amazon SET _QBAcct=Amazon.com
IF /i %($PaymentMethod)%==MAST SET _QBAcct=Auth/Net
IF /i %($PaymentMethod)%==MasterCard SET _QBAcct=Auth/Net
IF /i %($PaymentMethod)%==Visa SET _QBAcct=Auth/Net
IF /i %($PaymentMethod)%==PayPal SET _QBAcct=PayPalPmts
IF /i %($PaymentMethod)%==On Account SET _QBAcct=%($RegistrationName)%
IF /i %($PaymentMethod)%==Mail SET _QBAcct=%($RegistrationName)%
IF /i %($PaymentMethod)%==AMER SET _QBAcct=Auth/Net
IF /i %($PaymentMethod)%==DISC SET _QBAcct=Auth/Net
:: Assign Rep designator based on QBAccount
IF /i %($PaymentMethod)%==Amazon Receivables SET _Rep=Amazon
:: 12-25-2009 added second Amazon pm't method for versatility
IF /i %($PaymentMethod)%==Amazon SET _Rep=Amazon
IF /i %($PaymentMethod)%==MAST SET _Rep=BlueZap
IF /i %($PaymentMethod)%==MasterCard SET _Rep=BlueZap
IF /i %($PaymentMethod)%==Visa SET _Rep=BlueZap
IF /i %($PaymentMethod)%==PayPal SET _Rep=BlueZap
IF /i %($PaymentMethod)%==On Account SET _Rep=R B
IF /i %($PaymentMethod)%==Mail SET _Rep=R B
IF /i %($PaymentMethod)%==AMER SET _Rep=BlueZap
IF /i %($PaymentMethod)%==DISC SET _Rep=BlueZap
:: check for duplicate address data
findstr /i /s "%_ShipCombName%" addrlist.txt
echo errorlevel: %errorlevel%
if errorlevel 1 goto :ADDRWRITE
if errorlevel 0 goto :ADDRFOUND
:ADDRWRITE
echo %_ShipCombName% >> addrlist.txt
echo %($BillingAddress1)% >> addrlist.txt
echo %($BillingCity)% %($BillingState)% %($BillingPostal)% >> addrlist.txt
echo. >> addrlist.txt
echo Address File Written
:ADDRFOUND
echo selected rep is %_Rep%
echo selected account is: %_QBAcct%
pause
:: RUI OUT
:: write Merchant Order ID & RUI Order ID to RUI
:: check for duplicate RUI data in writeRUI.txt
cd..
cd RegKOut
find /i "%($OrderNumber)%" writeRUI.txt
echo errorlevel: %errorlevel%
if errorlevel 1 goto :RUIWRITE
if errorlevel 0 goto :IIFWRITE
:RUIWRITE
echo %($Invoice)% %($OrderNumber)% >> writeRUI.txt
:: end write RUI
:: IIF OUT
:IIFWRITE
:: Check for duplicate invoice data in writeIIF.txt
find /i "%($OrderNumber)%" writeIIF.txt
echo errorlevel: %errorlevel%
if errorlevel 1 goto :HEADWRITE
if errorlevel 0 goto :LINEWRITE
:HEADWRITE
:: write Header, Ship/Handling, discount, Rep & commission data to QB IIF import file
echo %($OrderNumber)% %($Purch_Date)% Invoice %($TransactionID)% %_QBAcct% Accounts Receivable %($Total)% %_Rep% >> writeIIF.txt
echo H/P %($Pack_Prep)% 1 ? >> writeIIF.txt
echo SHP %($Shipping)% 1 ? >> writeIIF.txt
echo DISC %($Discount)% 1 ? >> writeIIF.txt
echo Comm %($ServiceFee)% 1 ? >> writeIIF.txt
:LINEWRITE
IF /i %($ProductIdentifier)% equ PH-1 goto WRITE_DEFA ELSE goto WRITE_DISC
echo %($ProductIdentifier)%
:WRITE_DISC
::writes discounted prices parsed from custom variable:
echo %($ProductIdentifier)% %($Price_Each)% %($Quantity)% ? >> writeIIF.txt
goto :EOF
:WRITE_DEFA
:writes default prices parsed from Product data
echo %($ProductIdentifier)% %($UnitPrice)% %($Quantity)% ? >> writeIIF.txt
goto :EOF
:: 3-second delay
:: TYPE NUL | CHOICE.COM /N /CY /TY,3 >NUL
:EOF
【问题讨论】:
您可以使用 PowerShell 来执行此操作吗?我怀疑如果您使用 PowerShell,您将获得更多控制权。 我检查了你的批处理文件——它似乎工作正常(在我修复了“shift set ($ProductIdentifier)=%~9”这一行之后——我认为这是复制的产物- 并在此处粘贴)。 谢谢罗伯特·哈维;我一直在关注 PowerShell。我对编程不是很有经验..不确定学习曲线会是什么样子。我确实有一些批处理文件的经验,我选择这个的主要原因。我打算编译批处理,以便它运行得更快。感谢您的回复。 感谢 atzz:我已经对该批处理文件进行了更多测试,并且我有理由相信 ~tilde 和驱逐引号与带有长路径名的 CMD 问题无关。我通过在根目录附近设置一个短名称子目录来解决这个问题,以消除长路径名。 不需要显示完整的批处理文件。请缩短您的问题。 【参考方案1】:最后有一个额外的双引号,将它添加回字符串的末尾(在从字符串中删除两个引号之后)。
输入:
set widget="a very useful item"
set widget
set widget=%widget:"=%
set widget
输出:
widget="a very useful item"
widget=a very useful item
注意:要将双引号 " 替换为单引号 ',请执行以下操作:
set widget=%widget:"='%
注意:要将单词“World”(不区分大小写)替换为 BobB,请执行以下操作:
set widget="Hello World!"
set widget=%widget:world=BobB%
set widget
输出:
widget="Hello BobB!"
就您最初的问题而言(将以下代码保存到批处理文件 .cmd 或 .bat 并运行):
@ECHO OFF
ECHO %0
SET BathFileAndPath=%~0
ECHO %BathFileAndPath%
ECHO "%BathFileAndPath%"
ECHO %~0
ECHO %0
PAUSE
输出:
"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"
C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd
"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"
C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd
"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"
Press any key to continue . . .
%0
是脚本名称和路径。%1
是第一个命令行参数,依此类推。
【讨论】:
谢谢,这就是我在SET BathFileAndPath=%~0
之后的样子(实际上,我的参数数组以 1 开头,所以我使用了SET BathFileAndPath=%~1
你能解释一下为什么%widget:"=%
去掉引号吗?
@Imray - :old=new
是变量扩展中的模式匹配和替换。使用"
作为old
而没有作为new
,即使它们在中间,即使它们不平衡,也会删除引号。
set widget=%widget:"=% 在小部件为空时有一个奇怪的行为,它返回 "="
当字符串中有&
时,恐怕此解决方案不起作用,如set widget="hello&world"
。在这种情况下,您需要使用set "widget=%widget:"=%"
【参考方案2】:
您的结论 (1) 听起来是错误的。一定有其他因素在起作用。
批处理文件参数中的引号问题通常通过删除%~
的引号然后在适当的地方手动放回来解决。
例如:
set cmd=%~1
set params=%~2 %~3
"%cmd%" %params%
注意%cmd%
周围的引号。没有它们,带有空格的路径将不起作用。
如果您可以发布您的整个批处理代码,也许可以做出更具体的答案。
【讨论】:
我同意;在我看来, set var=%~1 应该可以正常工作。我会按要求发布整个批处理文件。 MS 参考:Using batch parameters。谁知道有这么多修饰符?供参考,MS官方文档将%~1
的描述为Expands %1 and removes any surrounding quotation marks ("").
@cod3monk3y 此外,您可以向cmd
询问这些修饰符:call /?
获取有关参数修饰符的帮助,set /?
获取变量修饰符(可能不是很直观)。很方便。
%~
仅适用于命令参数,不适用于变量;下面有一个正确答案提到set var=%var:"=%
技巧。【参考方案3】:
我通常只是从我的变量中删除所有引号:
set var=%var:"=%
然后在我需要的地方再次应用它们,例如:
echo "%var%"
【讨论】:
一开始我也遇到了麻烦,结果我看到:
是.
- 在弄清楚这一点之后,工作顺利。很棒的提示!【参考方案4】:
花了很多时间尝试以一种简单的方式做到这一点。 仔细查看 FOR 循环后,我意识到我只需一行代码就可以做到这一点:
FOR /F "delims=" %%I IN (%Quoted%) DO SET Unquoted=%%I
例子:
@ECHO OFF
SET Quoted="Test string"
FOR /F "delims=" %%I IN (%Quoted%) DO SET Unquoted=%%I
ECHO %Quoted%
ECHO %Unquoted%
输出:
"Test string"
Test string
【讨论】:
此方法与“SET Quoted=Test string”无关。下一个问题是如何区分“(双引号)是否存在。谁可能关心,谢谢你。【参考方案5】:简单的波浪号语法仅适用于删除传递到批处理文件的命令行参数周围的引号
SET xyz=%~1
上面的批处理文件代码会将 xyz 设置为作为第一个参数传递的任何值,去掉前导和尾随引号(如果存在)。
但是,这种简单的波浪号语法不适用于未作为参数传入的其他变量
对于所有其他变量,您需要使用扩展的替换语法,这需要您 指定要删除的前导和滞后字符。实际上,我们是在指示删除第一个和最后一个字符而不查看它实际上是什么。
@SET SomeFileName="Some Quoted file name"
@echo %SomeFileName% %SomeFileName:~1,-1%
如果我们想在删除它之前检查第一个和最后一个字符实际上是什么引号,我们需要一些额外的代码,如下所示
@SET VAR="Some Very Long Quoted String"
If aa%VAR:~0,1%%VAR:~-1%aa == aa""aa SET UNQUOTEDVAR=%VAR:~1,-1%
【讨论】:
【参考方案6】:这听起来像是一个简单的错误,您在不应该在的地方使用 %~。 if %~ 的使用不会从根本上改变批处理文件的工作方式,它只是在那种情况下从字符串中删除引号。
【讨论】:
【参考方案7】:我从这个link 了解到,如果您使用的是 XP 或更高版本,这将简单地自行工作:
SET params = %~1
我无法在此处获得任何其他解决方案以在 Windows 7 上运行。
为了迭代它们,我这样做了:
FOR %%A IN (%params%) DO (
ECHO %%A
)
注意:如果您传递的参数通常用空格分隔,您只会得到双引号。
【讨论】:
【参考方案8】:-
set widget="一个非常有用的项目"
设置小部件
widget="一个非常有用的项目"
设置小部件=%widget:"=%"
设置小部件
设置小部件=一个非常有用的项目"
第 4 行中的尾随引号 "
是在字符串中添加引号 "
。它应该被删除。
第 4 行的语法以 %
结尾
【讨论】:
@user195488 - 将第 3 行读取为 观察它显示:widget="a very useful item"
并将第 6 行读取为 观察它显示:widget=a very useful item"
并了解第 7 行将是 set widget=%widget:"=%
和第 8 行将观察显示的行 widget=a very useful item
不再有尾随引号。【参考方案9】:
我以为我有解决方案,但我很快就遇到了执行批处理文件的异常行为。突然,CMD 无法识别长路径语句。从完整路径正常执行批处理文件
C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
返回
'C:\Documents' 未被识别为内部或外部命令....
这是你的全部问题。 CMD 不理解命令行中文件名中的空格,因此它认为您正在尝试传递
和 Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
作为参数
"C:\Documents"
程序。
您需要引用它来运行路径中带有空格的批处理文件:
> "C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat"
会起作用的。
【讨论】:
【参考方案10】:@echo off
Setlocal enabledelayedexpansion
Set 1=%1
Set 1=!1:"=!
Echo !1!
Echo "!1!"
Set 1=
不管原始参数是否有引号,都显示带或不带引号。
如果您想测试是否存在引号中的参数,请将此行放在上面的回声之前:
如果 '%1'=='' 转到您的子目录
但如果检查是否存在可能有引号或没有引号的文件,那么它是:
如果存在“!1!”转到其他子
注意单引号和双引号的使用是不同的。
【讨论】:
【参考方案11】:所有的答案都是完整的。但是想补充一件事,
设置名字=%~1
设置姓氏=%~2
这条线应该可以用,你需要做一点小改动。
设置“名字=%~1”
设置“姓氏=%~2”
在引号内包含完整的作业。它将毫无问题地删除引号。这是一种首选的赋值方式,可以修复参数中的引号引起的不想要的问题。
【讨论】:
【参考方案12】:Azure devops 有时使用双引号字符 ("
) 来指定字符串。 Powershell 可以使用单引号字符 ('
) 来指定字符串。当然,我希望能够灵活地指定参数,但我希望如此,因此可以通过批处理文件从命令行使用相同的参数,也可以作为 powershell 脚本使用任何参数,包括空值。
引用自然的想法是这样写的:
build.bat:
@echo off
setlocal enabledelayedexpansion
set args=%*
set args=%args:"='%
echo powershell -executionpolicy bypass "%~dpn0.ps1" %args%
endlocal
但正如你可以猜到的 - 这不是开箱即用的 - 如果没有为批处理文件提供参数,那么%*
== 空字符串。 args
扩展为无字符串,下一个替换注意到 args
未设置 - 而不是替换字符串 - 它将额外的 "='
垃圾附加到 args
参数。
解决这个问题的方法是在第一次分配中添加额外的空间。
build.bat:
@echo off
setlocal enabledelayedexpansion
set args=%*
set args=%args:"='%
echo powershell -executionpolicy bypass "%~dpn0.ps1" %args%
endlocal
这个字符翻译后应该是正确的:
C:\test>build
powershell -executionpolicy bypass "C:\test.ps1"
C:\test>build aa
powershell -executionpolicy bypass "C:\test\build.ps1" aa
C:\test>build "aa"
powershell -executionpolicy bypass "C:\test\build.ps1" 'aa'
C:\test>build 'aa'
powershell -executionpolicy bypass "C:\test\build.ps1" 'aa'
【讨论】:
以上是关于从批处理文件中的变量中删除双引号会导致 CMD 环境出现问题的主要内容,如果未能解决你的问题,请参考以下文章