capturing CMD batch file parameter list; write to file for later processing - variables

I have written a batch file that is launched as a post processing utility by a program. The batch file reads ~24 parameters supplied by the calling program, stores them into variables, and then writes them to various text files.
Since the max input variable in CMD is %9, it's necessary to use the 'shift' command to repeatedly read and store these individually to named variables. Because the program outputs several similar batch files, the result is opening several CMD windows sequentially, assigning variables and writing data files. This ties up the calling program for too long.
It occurs to me that I could free up the calling program much faster if maybe there's a way to write a very simple batch file that can write all the command parameters to a text file, where I can process them later. Basically, just grab the parameter list, write it and done.
Q: Is there some way to treat an entire series of parameter data as one big text string and write it to one big variable... and then echo the whole big thing to one text file? Then later read the string into %n variables when there's no program waiting to resume?
Parameter list is something like 25 - 30 words, less than 200 characters.
Sample parameter list:
"First Name" "Lastname" "123 Steet Name Way" "Cityname" ST 12345 1004968 06/01/2010 "Firstname+Lastname" 101738 "On Account" 20.67 xy-1z 1 8.95 3.00 1.39 0 0 239 8.95
Items in quotes are processed as string variables. List is space delimited.
Any suggestions?

echo %* 1>args.txt
%* references all arguments: %1 %2 %3...
It also works with subroutines.
call :test 1 2 3
goto :eof
:test
echo 1: %1
echo 2: %2
echo 3: %3
echo *: %*
exit /b
output:
1: 1
2: 2
3: 3
*: 1 2 3
See the following website for more information:
http://ss64.com/nt/syntax-args.html

Interesting Post. It sparked my interest.
I too am needing something that could accept parameters and although this probably isn't useful to you now I thought it might be useful at some later date.
My solution is less simple - because there just isn't an elegant way to do it.
Basically, in this example the "-" can be used to identify a parameter, and the next space is assumed to be set to a value.
Legal Stuff:
So this is all my code and I don't really care how or where you choose to use it. No need to cite me it's just an example anyway.
Like this:
Microsoft Batch:Begin Copy below and save as filename.bat
#ECHO OFF
REM USAGE: this-batch-name.bat -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
SET __CURRENT_WORKING_DIRECTORY__=%~dp1
ECHO.__CURRENT_WORKING_DIRECTORY__=%__CURRENT_WORKING_DIRECTORY__%
REM # Clear Previous Variables
SET PACKAGING_BUILD_NUMBER=
SET PACKAGING_JOB_NAME=
SET GO_DEEPER=
SET RUN_COMMAND=
REM ## In order to read variables set while in a "FOR" loop
REM ## you have to set the 'ENABLEDELAYEDEXPANSION' with 'SETLOCAL'.
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
REM ## Capture Command line parameters here with a %*
FOR %%A IN (%*) DO (
REM ## If we found something with a '-' in previous pass run GO_DEEPER will be defined and thus set to the command line argument.
IF DEFINED GO_DEEPER (
REM ## When ENABLEDELAYEDEXPANSION is Set with setlocal command you have to use exclamation: i.e. '^!'
IF /I "-BUILD"=="!GO_DEEPER!" SET PACKAGING_BUILD_NUMBER=%%A
IF /I "-JOB"=="!GO_DEEPER!" SET PACKAGING_JOB_NAME=%%A
IF /I "-RUN"=="!GO_DEEPER!" SET RUN_COMMAND=%%A
SET SET GO_DEEPER=
)
IF /I "%%A" GEQ "-" (
REM ## Wow we found your command line argument that started with a '-' so set the GO_DEEPER Var
SET GO_DEEPER=%%A
) ELSE (
SET SET GO_DEEPER=
)
)
REM ## Time to grab the variables set while in delayed expansion mode
ENDLOCAL && SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER% && SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME% && SET RUN_COMMAND=%RUN_COMMAND%
REM ## Sucks, but you have to clear the '"' and "'" if it exists.
IF DEFINED RUN_COMMAND (
SET RUN_COMMAND=%RUN_COMMAND:"=%
SET RUN_COMMAND=%RUN_COMMAND:'=%
)
IF DEFINED PACKAGING_JOB_NAME (
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:"=%
SET PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME:'=%
)
IF DEFINED PACKAGING_BUILD_NUMBER (
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:"=%
SET PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER:'=%
)
REM ## Now we can try to run the command function if the -run was used...
IF DEFINED RUN_COMMAND (
CALL:--%RUN_COMMAND% "'%PACKAGING_JOB_NAME%'","'%PACKAGING_BUILD_NUMBER%'"
) ELSE (
ECHO Try running:
ECHO %0 -BUILD "1.2.3 build 405" -JOB "Running This Job" -run RUN_FUNCTION
)
GOTO DONE
:--RUN_FUNCTION
ECHO running... %~0
SET VARPASSED1=%~1
SET VARPASSED2=%~2
IF DEFINED VARPASSED1 ECHO VARPASSED1 was %VARPASSED1%
IF DEFINED VARPASSED2 ECHO VARPASSED2 was %VARPASSED2%
ECHO Add your code to process here...
GOTO:EOF
:DONE
ECHO We got the following results...
IF DEFINED PACKAGING_JOB_NAME ECHO PACKAGING_JOB_NAME=%PACKAGING_JOB_NAME%
IF DEFINED PACKAGING_BUILD_NUMBER ECHO PACKAGING_BUILD_NUMBER=%PACKAGING_BUILD_NUMBER%
IF DEFINED RUN_COMMAND ECHO RUN_COMMAND=%RUN_COMMAND%
</pre> </code>
Microsoft Batch END Copy
RESULTS:
__CURRENT_WORKING_DIRECTORY__=C:\dev\a\win\sysprep\
running... :--RUN_FUNCTION
VARPASSED1 was "'Running...'"
VARPASSED2 was "'This...'"
We got the following results...
PACKAGING_JOB_NAME="Running This Job"
PACKAGING_BUILD_NUMBER="1.2.3 build 405"
RUN_COMMAND=RUN_FUNCTION

Related

Variable set in batch file won't show

When I do this in Notepad, the command prompt doesn't show ping %ip% -t -l %package%, but it shows ping %ip% -t -l and doesn't show the package variable.
#echo off
set data=0
set package = -600
IF %data% == 0 (
set /a package= %package% + 1600
#echo ping %ip% -t -l %package%
)
echo %package%
pause
What am I doing wrong?
Batch is sensitive to spaces in a SET statement. SET FLAG = N sets a variable named "FLAGSpace" to a value of "SpaceN"
The set "var=value" syntax ensures that any trailing spaces on the batch line are not included in the value assigned to var.
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
Two common ways to overcome this are 1) to use setlocal enabledelayedexpansion and use !var! in place of %var% to access the changed value of var or 2) to call a subroutine to perform further processing using the changed values.
Note therefore the use of CALL ECHO %%var%% which displays the changed value of var.
So:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
set /A data=0
set /A package=-600
IF %data% == 0 (
set /a package=!package!+1600
echo ping %ip% -t -l !package!
)
echo %package%
pause
Noting: The setlocal statement should normally be placed at the start of the code. Your posted code is evidently a snip, since you do not appear to be setting ip.
Spaces are irrelevant in a set /a but even so, removing them fosters a habit
Set /a uses the run-time value, not the parse-time value of var when the syntax set /a var=var+1 is used within a loop, so set /a var=var+1 and set /a var=!var!+1 are equivalent but set /a var=%var%+1 uses the value of var at the time the loop is parsed.
Since echoing is set to off by the initial statement, the leading # on the second echo is redundant.

Saving Number of Lines in File as a Variable in Batch File

I have this really nice line in my batch file that tells me how many lines are in a file:
find /v /c "" C:\Users\c1921\mypath\myfolder\!$Unit!.txt
This is nice and gives me 31 for the particular file I'm working with. My problem is the file looks something like this:
Microsoft (R) Windows Script Host Version 5.8
Copyright (C) Microsoft Corporation. All rights reserved.
my_handled
219278
check
219276
control
219274
I want to be able to skip the first three lines entirely and then save the first value and then use the second value in my next command etc.
How do I save the number (e.g. 31) into a variable in my batch file?
On a hunch I tried setting a variable like so but it wasn't effective:
set "$testVar="""
echo !$testVar!
This command allows you to "save the number (e.g. 31) into a variable in my batch file":
for /F %%a in ('find /v /c "" ^< C:\Users\c1921\mypath\myfolder\!$Unit!.txt') do set numLines=%%a
This command allows you "to skip the first three lines entirely" and process the rest:
for /F "skip=3 delims=" %%a in (C:\Users\c1921\mypath\myfolder\!$Unit!.txt) do echo Processing: "%%a"
However, in my opinion this problem could be entirely avoided if the three first lines in the text file are supressed from the very beginning. I think this file is generated via a VBScript of JScript program that is executed this way:
cscript progname.vbs > C:\Users\c1921\mypath\myfolder\!$Unit!.txt
The first three lines in the text file may be avoided adding //nologo switch this way:
cscript //nologo progname.vbs > C:\Users\c1921\mypath\myfolder\!$Unit!.txt
#ECHO OFF
SETLOCAL
SET /a count=3
SET "first="
FOR /f "skip=3delims=" %%a IN (q25089468.txt) DO (
IF DEFINED first (CALL :show %%a) ELSE (SET "first=%%a")
)
ECHO count=%count%
GOTO :EOF
:show
ECHO first=%first% second=%1
SET /a count+=2
SET "first="
GOTO :eof
I used a file named q25089468.txt containing your data for my testing.
You appear to be asking two entirely different things - how to count the lines and how to skip the first 3, then deliver each succeeding pair to another process.

Identify running instances of a batch file

These are not working for me.
Any help to definitelly corret the four examples below ?
The EXAMPLE01 just echoes "continue", even if I have three CMD.exe opened.
---------- EXAMPLE 01 ------------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
SET ERRORLEVEL=value if "%value%" GTR 1 (
ECHO This batch is not first
ECHO quitting ...
)
if "%value%" LSS 2 ECHO continue
I am getting the "unexpected i error" message in the EXAMPLE 02!
----------- EXAMPLE 02 -------
#echo off
FOR /F "usebackq tokens=2" %i IN (`tasklist ^| findstr /r /b "cmd.exe"`)
DO taskkill /pid %%i
I am getting the "is first" message in the EXAMPLE 03, even with three CMD.exe opened!
----------- EXAMPLE 03 -------
#echo off
wmic process where name="cmd.exe" | find "cmd.exe" /c
if "%errorlevel%" LEQ 1 echo CMD is first
if "%errorlevel%" GTR 1 echo CMD is already running
It is also possible that I will not have access to the Wmic command at work, so, another possibility is found in the EXAMPLE 04 ... but to no avail.
----------- EXAMPLE 04 -------
#echo off
Tasklist /FI "IMAGENAME eq cmd.exe" 2>NUL | find /I /N "cmd.exe">NUL
if "%ERRORLEVEL%"==0 do (goto Use) else (goto Cont)
:Cont
ECHO Only one instance running
pause
:Use
echo Application running already. Close this window
Kind regards,
Maleck
wmz identified a number of errors in the OP's code, and also has an excellent suggestion to use a lock file for concurrency control.
Below is a robust batch solution that uses a lock file to prevent multiple instances of the batch file from running at the same time. It uses a temporary lock file for the concurrency control. The mere presence of the lock file does NOT stop the script from running. The script will only fail if another process has an exclusive lock on the lock file. This is important in case the script should crash or be killed without deleting the lock file. The next process to run the script will still succeed because the file is no longer locked.
This script assumes the script is installed on a local drive. It allows only one instance for the entire machine. There are lots of variations to control the amount of concurrency allowed. For example, incorporating the %USERNAME% into the lock file name would allow one instance per user in a network environment. Incorporating %COMPUTERNAME% in the name would allow one instance per machine in a network environment.
#echo off
setlocal
:: save parameters to variables here as needed
set "param1=%~1"
:: etc.
:: Redirect an unused file handle for an entire code block to a lock file.
:: The batch file will maintain a lock on the file until the code block
:: ends or until the process is killed. The main code is called from
:: within the block. The first process to run this script will succeed.
:: Subsequent attempts will fail as long as the original is still running.
set "started="
9>"%~f0.lock" (
set "started=1"
call :start
)
if defined started (
del "%~f0.lock" >nul 2>nul
) else (
echo Process aborted: "%~f0" is already running
)
exit /b
:start
:: The main program appears below
:: All this script does is PAUSE so we can see what happens if
:: the script is run multiple times simultaneously.
pause
exit /b
EDIT
The error message "The process cannot access the file because it is being used by another process." can be suppressed by redirecting stderr output to nul in an outer block.
2>nul (
9>"%~f0.lock" (
set "started=1"
call :start
)
)
The problem with the above is that all error messages for the main program will also be suppressed. That can be fixed by 1st saving the current definition of stderr to another unused file handle, and then adding yet another inner block that redirects stderr back to the saved file handle.
8>&2 2>nul (
9>"%~f0.lock" (
2>&8 (
set "started=1"
call :start
)
)
)
You do not set value anywhere. Even if you did it would not work as variables are expanded on parse. You would need to use delayed expansion if you need to both set and test variable against what was set. Quotes on comparison are not required (see 3). help set will show you info delayed expansion.
%i should be %%i. DO taskkill /pid %%i must be on same line as rest of for command. You also use findstr in regex mode, which means it will search for cmd[any character]exe
You use string (lexical) comparison: "1" leq 1 is true (as "4" leq 1 also is...). Use errorlevel 1 which is equivalent to errorlevel equal or greater than 1. help if shows syntax
Same as 3 plus do in do (goto Use) should be removed. %errorlevel%==0 would work, but it's normally advised to use errorlevel number.
How to check if there is only 1 cmd running:
#echo off
for /f %%i in ('Tasklist /FI "IMAGENAME eq cmd.exe" 2^>NUL' ^| find /I /c "cmd.exe"') do (
if %%i leq 2 (echo only one instance) else (echo More than one instance)
)
Note: it's just an example. I do not recommend this as a real method of concurrency control. I would rather use lock file for that.

Removing double quotes from variables in batch file creates problems with CMD environment

Can anybody help with effective and safe way of removing quotes from batch variables?
I have written a batch file which successfully imports a list of parameters %1, %2, %3 etc. and places them into named variables. Some of these parameters contain multiple words, and therefor are enclosed in double quotes.
> "Susie Jo" (%1)
> "Smith Barnes" (%2)
> "123 E. Main St." (%3)
These %variables are next placed in named variables:
> set FirstName=%1
> set LastName=%2
> set ShipAddr=%3
verification of variables is done by echo.
> echo.%FirstName%
> echo.%LastName%
> echo.%ShipAddr%
results display as
"Susie Jo"
"Smith Barnes"
"123 E. Main St."
I need to eliminate the included quotes on selected variables. For instance, FirstName and LastName are used elsewhere and must not include quotes.
In a test batch file I was successful at eliminating quotes using the ~tilde character in variables.
> set FirstName=%~1
> set LastName=%~2
I thought I had the solution, but I soon experienced unusual behavior with execution of batch files. Suddenly CMD is no recognizing long path statments. Normal execution of batch file from full path
> C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
returns
> 'C:\Documents' is not recognized as an internal or external command....
So it would appear that the addition of the ~tilde character to the in-coming %1 %2...%n variables has caused some change. Possibly some environment variables have been altered?
I also tried clearing quotes from within variable with various attempts using the FOR command. That seems awkward and I have been unable to learn how to accomplish this by creating a list of variable to perform the task:
something like this:
for %%g in (%FirstName% %LastName%) do (
set %%g=%%~g
set %%h=%%~h
set FirstName=%%~g
set LastName=%%h
echo.%FirstName% %LastName%
)
I think I have two issues.
My 'short and sweet' idea of inserting ~tilde in the incoming %1 %2 variables (%~1, etc) seems to have affected some settings and altered how CMD navigates long pathnames.
I'm still in search of a clean and easy way to eliminate quotes from selected named variables.
Any help for those more experienced would be most appreciated. I'm at the end of my skills here... need some guidance please!
edit 12/26/2009 13:36 PST
entire batch file:
:: 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
You have an extra double quote at the end, which is adding it back to the end of the string (after removing both quotes from the string).
Input:
set widget="a very useful item"
set widget
set widget=%widget:"=%
set widget
Output:
widget="a very useful item"
widget=a very useful item
Note: To replace Double Quotes " with Single Quotes ' do the following:
set widget=%widget:"='%
Note: To replace the word "World" (not case sensitive) with BobB do the following:
set widget="Hello World!"
set widget=%widget:world=BobB%
set widget
Output:
widget="Hello BobB!"
As far as your initial question goes (save the following code to a batch file .cmd or .bat and run):
#ECHO OFF
ECHO %0
SET BathFileAndPath=%~0
ECHO %BathFileAndPath%
ECHO "%BathFileAndPath%"
ECHO %~0
ECHO %0
PAUSE
Output:
"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 is the Script Name and Path.
%1 is the first command line argument, and so on.
Your conclusion (1) sounds wrong. There must be some other factor at play.
The problem of quotes in batch file parameters is normally solved by removing the quotes with %~ and then putting them back manually where appropriate.
E.g.:
set cmd=%~1
set params=%~2 %~3
"%cmd%" %params%
Note the quotes around %cmd%. Without them, path with spaces won't work.
If you could post your entire batch code, maybe more specific answer could be made.
I usually just remove all quotes from my variables with:
set var=%var:"=%
And then apply them again wherever I need them e.g.:
echo "%var%"
Spent a lot of time trying to do this in a simple way.
After looking at FOR loop carefully, I realized I can do this with just one line of code:
FOR /F "delims=" %%I IN (%Quoted%) DO SET Unquoted=%%I
Example:
#ECHO OFF
SET Quoted="Test string"
FOR /F "delims=" %%I IN (%Quoted%) DO SET Unquoted=%%I
ECHO %Quoted%
ECHO %Unquoted%
Output:
"Test string"
Test string
The simple tilde syntax works only for removing quotation marks around the command line parameters being passed into the batch files
SET xyz=%~1
Above batch file code will set xyz to whatever value is being passed as first paramter stripping away the leading and trailing quotations (if present).
But, This simple tilde syntax will not work for other variables that were not passed in as parameters
For all other variable, you need to use expanded substitution syntax that requires you to
specify leading and lagging characters to be removed. Effectively we are instructing to remove strip away the first and the last character without looking at what it actually is.
#SET SomeFileName="Some Quoted file name"
#echo %SomeFileName% %SomeFileName:~1,-1%
If we wanted to check what the first and last character was actually quotation before removing it, we will need some extra code as follows
#SET VAR="Some Very Long Quoted String"
If aa%VAR:~0,1%%VAR:~-1%aa == aa""aa SET UNQUOTEDVAR=%VAR:~1,-1%
I learned from this link, if you are using XP or greater that this will simply work by itself:
SET params = %~1
I could not get any of the other solutions here to work on Windows 7.
To iterate over them, I did this:
FOR %%A IN (%params%) DO (
ECHO %%A
)
Note: You will only get double quotes if you pass in arguments separated by a space typically.
This sounds like a simple bug where you are using %~ somewhere where you shouldn't be. The use if %~ doesn't fundamentally change the way batch files work, it just removes quotes from the string in that single situation.
All the answers are complete. But Wanted to add one thing,
set FirstName=%~1
set LastName=%~2
This line should have worked, you needed a small change.
set "FirstName=%~1"
set "LastName=%~2"
Include the complete assignment within quotes. It will remove quotes without an issue. This is a prefered way of assignment which fixes unwanted issues with quotes in arguments.
set widget="a very useful item"
set widget
widget="a very useful item"
set widget=%widget:"=%"
set widget
set widget=a very useful item"
The trailing quote " in line 4 is adding a quote " to the string. It should be removed.
The syntax for line 4 ends with %
I thought I had the solution, but I soon experienced unusual behavior with execution of batch files. Suddenly CMD is no recognizing long path statments. Normal execution of batch file from full path
C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
returns
'C:\Documents' is not recognized as an internal or external command....
There's your whole problem. CMD doesn't understand spaces inside of filenames from the command line, so it thinks you're trying to pass
and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat
as parameters to the
"C:\Documents"
program.
You need to quote it to run a batch file with spaces in the path:
> "C:\Documents and Settings\Administrator\My Documents\Txt\batchtest\dataout.bat"
would have worked.
#echo off
Setlocal enabledelayedexpansion
Set 1=%1
Set 1=!1:"=!
Echo !1!
Echo "!1!"
Set 1=
Demonstrates with or without quotes reguardless of whether original parameter has quotes or not.
And if you want to test the existence of a parameter which may or may not be in quotes, put this line before the echos above:
If '%1'=='' goto yoursub
But if checking for existence of a file that may or may not have quotes then it's:
If EXIST "!1!" goto othersub
Note the use of single quotes and double quotes are different.
Azure devops sometimes uses double quoting character (") to specify string. Powershell can use single quote character (') to specify string. Naturally I wanted to have a flexibility to be able to specify parameters however I wish, so same parameter can be used from both - command line - via batch file, and as a powershell script, as any parameter, including empty value.
Quote natural thinking is to write something like this:
build.bat:
#echo off
setlocal enabledelayedexpansion
set args=%*
set args=%args:"='%
echo powershell -executionpolicy bypass "%~dpn0.ps1" %args%
endlocal
But like you can guess - this does not work out of box - if no arguments are provided to batch file so %* == empty string. args expands as no string, and next replacement notices that args is not set - and instead of replacing string - it would append extra "=' garbage to args parameter.
Solution to this was just to add extra space in first assignment.
build.bat:
#echo off
setlocal enabledelayedexpansion
set args=%*
set args=%args:"='%
echo powershell -executionpolicy bypass "%~dpn0.ps1" %args%
endlocal
After this character translation should be correct:
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'

How can I count how many calls of a cmd file?

How can I count how many calls of a cmd file?
I'm struggling with something like this but it didn't work:
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo %v% > Calls.log
)
If all you're trying to do is count how many times a cmd script is called, you can just append one character to a file every time it runs rather than fiddling around with expression evaluation every time the script is run. This also has the advantage of making the script quicker since the analysis of the count is moved elsewhere.
The counter file expands by one byte every time, so watch out if you're calling it a truly large number of times since the file will expand. But even, calling it once per second, a 1G file will accrue only after 30 years.
At the top of your script, just put:
set >>countfile.txt <nul: /p x=X
This simply adds the character X to the end of the countfile.txt file every time the script is called. It uses the "set/p" command with input/output redirection, which is the only way I'm aware of to get a character out without a CR/LF following it (like the UNIX "echo -n").
To get a count of the number of calls to date, you can use the file size environment variable modifier, as in the following script (I expect this will be done less often than running the script so it's better to put the grunt work here [in fact, it's not a lot of grunt work since it's not counting the characters, rather it gets the information directly from the directory entry]):
#echo off
goto :main
:getsize
echo %~z1
goto :eof
:main
setlocal enableextensions enabledelayedexpansion
call :getsize countfile.txt
endlocal
To reset the count, use the following extremely complicated command (I'm thinking of patenting this):
del countfile.txt
One other thing I'd suggest - you don't need to prefix every command with "#" to prevent echo, you can simply put "#echo off" at the top of your script for global no-echo. If you want to selectively echo some commands, just ignore this paragraph.
The complete if block is parsed at once and thus all environment variables in it are getting replaced by their values at the time before the if block gets executes. You need to enable delayed variable expansion and use !v!:
#setlocal enabledelayedexpansion
#IF NOT EXIST Calls.log echo. > Calls.log
#for %%i in (Calls.log) do set size=%%~zi
#IF %size% EQU 0 (
#ECHO 1 > Calls.log
) ELSE (
#set /p v=<Calls.log
#set /A v+=1
#echo !v! > Calls.log
)
And you can simplify the code as follows:
#echo off
setlocal enabledelayedexpansion
IF NOT EXIST Calls.log (
ECHO 1 > Calls.log
) ELSE (
set /p v=<Calls.log
set /A v+=1
echo !v! > Calls.log
)
There is no need to create the file beforehand (and even that I'd solve with copy nul Calls.log, since that ensures a file size of 0).
The following code works on my computer:
#if not exist Calls.log (
echo 0 > Calls.log
)
#set /p v=< Calls.log
#set /a v=v+1
echo %v% > Calls.log