Redirecting appcmd output to a variable - shell

I am trying to run the following:
FOR /F "tokens=* delims=" %A IN ('C:\windows\system32\inetsrv\appcmd.exe list app /site.name:"car" /xml | C:\windows\system32\inetsrv\appcmd.exe list vdir /vdir.name:"car/" /text:physicalPath') DO SET Variable=%A
But get the following error:
| was unxepected at this time

If the data must be piped from a process to the other, you need to escape the pipe character. It should be ^|
If what you need to do is execute both commands, replace the pipe character with ^&, the command concatenation operator, also escaped

For anyone reaches this, following is working example of batch file to get physical path of iis site , stored in a variable named 'physicalPath'.
This is based on #StuHarper explanations in comments.
#echo off
set site=stackoverflow.com
set site=%site:https://=%
set site=%site:http://=%
set site=%site: =%
set appcmd=%systemroot%\system32\inetsrv\AppCmd.exe
set xmlOutput=%appcmd% list app /site.name:%site% /path:"/" /xml
for /f "tokens=*" %%A in ('%xmlOutput% ^| %appcmd% list vdir /in /text:physicalPath') DO SET physcalPath=%%A
echo physcalPath: %physcalPath%
#echo on

Related

get path from the command line using windows batch

I would like to get the path after -h option.
Are there some commands that can be helpful for this case?
Input (which is actually in my case the output of the following command - wmic process where "name like '%%w3wp%%'" get processid,commandline):
c:\windows\system32\inetsrv\w3wp.exe -ap "SharePoint Central Administration v4" -v "v4.0" -l "webengine4.dll" -a \\.\pipe\iisipm23e2eb4d-e657-4192-980c-9ac9147d2f75 -h "C:\inetpub\temp\apppools\SharePoint Central Administration v4\SharePoint Central Administration v4.config" -w "" -m 0 5144
Desired output:
"C:\inetpub\temp\apppools\SharePoint Central Administration v4\SharePoint Central Administration v4.config"
Given that the target command line does not contain wildcards * and ? as well as < and >, the following approach should do it:
At first, wrap a for /F loop around to capture the output of wmic (without ProcessID since it is anyway not used, and using option /VALUE to avoid trailing spaces), then nest another for /F that processes that text strings again (this is necessary to correctly deal with the Unicode output of wmic).
Then use a standard for loop to walk through the parts/arguments of the command line, check against -h and retrieve the subsequent (quoted or unquoted) item.
Eventually skip the remaining command line portion.
This is a possible implementation:
#echo off
rem // Capture the output of `wmic` using two `for /F` loops for proper Unicode-to-ASCII/ANSI conversion:
for /F "delims=" %%I in ('
wmic Process where "Name like '%%w3wp%%'" get CommandLine /VALUE
') do for /F "tokens=1* delims==" %%J in ("%%I") do (
rem // Reset flag, iterate through the parts of the retrieved command line:
set "FLAG=" & for %%L in (%%K) do (
rem // Check against option string `-h` (remove `/I` to become case-sensitive):
if /I "%%~K"=="-h" (
rem // Set a flag to indicate that next item is the target path:
set "FLAG=#"
) else if defined FLAG (
rem // Flag is set, hence extract unquoted target path and terminate loop:
set "TARGET=%%~K"
goto :NEXT
)
)
)
:NEXT
echo Extracted path: "%TARGET%"
Assuming this command line is in variable input:
This works on the command line, to test the approach:
for /f delims^=^"^ tokens^=1 %a in ('echo %input:*-h =%') do #echo %a
And this is your code for a batch file (reading from %input% and saving into %result%):
for /f delims^=^"^ tokens^=1 %%a in ('echo %input:*-h =%') do #set result=%%a
echo %result%
This works by first cutting everything until the -h in a variable string replacement expression and then using for /f with the " as delimiter. In order to be able to specify the " as delimiter, we can't enclose the delims=... tokens=... part in quotes as we'd normally do, instead we have to escape every individual character with special meaning (including the space) with ^.
Note: This assumes that the path will always be quoted in the command line. If it isn't, you'd need to first check if the part after cutting the left side starts with a quote and then either use the quote or the space as delimiter accordingly:
set x=%input:*-h =%
if ^%x:~0,1% equ ^" (
for /f delims^=^"^ tokens^=1 %%a in ('echo %x%') do #set result=%%a
) else (
for /f tokens^=1 %%a in ('echo %x%') do #set result=%%a
)
echo %result%
...but having to resort to this sort of unreadable trickery seriously makes me wonder whether it even makes sense to use batch files with decades of legacy for this. Did you consider using PowerShell or Python or some other proper scripting language instead?
This method assumes that the desired string ends in a quote and a space (in this case, before the -w switch).
#echo off
setlocal
set "input=c:\windows\system32\inetsrv\w3wp.exe -ap "SharePoint Central Administration v4" -v "v4.0" -l "webengine4.dll" -a \\.\pipe\iisipm23e2eb4d-e657-4192-980c-9ac9147d2f75 -h "C:\inetpub\temp\apppools\SharePoint Central Administration v4\SharePoint Central Administration v4.config" -w "" -m 0 5144"
set "x=%input:-h =" & set "part=%"
set "output=%part:" =" & REM %"
echo %output%
You may read a further explanations of the method used at this or this answers...

Windows Batch file - strip leading characters

I have a batch file which copies some local files up to a google storage area using the gsutil tool. The gsutil tool produces a nice log file showing the details of the files that were uploaded and if it was OK or not.
Source,Destination,Start,End,Md5,UploadId,Source Size,Bytes Transferred,Result,Description
file://C:\TEMP\file_1.xlsx,gs://app1/backups/file_1.xlsx,2018-12-04T15:25:48.428000Z,2018-12-04T15:25:48.804000Z,CPHHZfdlt6AePAPz6JO2KQ==,,18753,18753,OK,
file://C:\TEMP\file_2.xlsx,gs://app1/backups/file_2.xlsx,2018-12-04T15:25:48.428000Z,2018-12-04T15:25:48.813000Z,aTKCOQSPVwDycM9+NGO28Q==,,18753,18753,OK,
What I would like to do is to
check the status result in column 8 (OK or FAIL)
If the status is OK then move the source file to another folder (so that it is not uploaded again).
The problem is that the source filename is appended with "file://" which I can't seem to remove, example
file://C:\TEMP\file_1.xlsx
needs to be changed into this
C:\TEMP\file_1.xlsx
I am using a for /f loop and I am not sure if the manipulation of the variables %%A is different within a for /f loop.
#echo off
rem copy the gsutil log file into a temp file and remove the header row using the 'more' command.
more +1 raw_results.log > .\upload_results.log
rem get the source file name (column 1) and the upload result (OK) from column 8
for /f "tokens=1,8 delims=," %%A in (.\upload_results.log) do (
echo The source file is %%A , the upload status was %%B
set line=%%A
set line=!line:file://:=! >> output2.txt echo !line!
echo !line!
)
The output is like this.
The source file is file://C:\TEMP\file_1.xlsx , the upload status was OK
The source file is file://C:\TEMP\file_2.xlsx , the upload status was OK
I'm expecting it to dump the altered values out into a new file but it is not producing anything at the moment.
Normally I would extract from a specific character to the end of the string with something like this but it doesn't work with my For/f loop.
%var:~7%
Any pointers or a different way of doing it greatly appreciated.
Since the part to remove seems fixed it is easier to use substrings.
Also using for /f "skip=1" evades he neccessity of the external command more +1 and another intermediate file.
#echo off & setlocal EnableDelayedExpansion
type NUL>output2.txt
for /f "skip=1 eol=| tokens=1,8 delims=," %%A in (.\upload_results.log) do (
echo The source file is %%A , the upload status was %%B
set "line=%%A"
set "line=!line:~7!"
echo(!line!>>output2.txt
echo(!line!
)
File names and paths can contain also one or more exclamation marks. The line set line=%%A is parsed by Windows command processor a second time before execution with enabled delayed expansion. See How does the Windows Command Interpreter (CMD.EXE) parse scripts? Every ! inside the string assigned to loop variable A is on this line interpreted as begin or end of a delayed expanded environment variable reference. So the string of loop variable A is assigned to environment variable line with an unwanted modification if file path/name contains one or more exclamation marks.
For that reason it is best to avoid usage of delayed expansion. The fastest solution is for this task using a second FOR to get file:// removed from string assigned to loop variable A.
#echo off
del output2.txt 2>nul
for /F "skip=1 tokens=1,8 delims=," %%A in (upload_results.log) do (
echo The source file is %%A , the upload status was %%B.
for /F "tokens=1* delims=/" %%C in ("%%~A") do echo %%D>>output2.txt
)
Even faster would be without the first echo command line inside the loop:
#echo off
(for /F "skip=1 delims=," %%A in (upload_results.log) do (
for /F "tokens=1* delims=/" %%B in ("%%~A") do echo %%C
))>output2.txt
The second solution can be written also as single command line:
#(for /F "skip=1 delims=," %%A in (upload_results.log) do #for /F "tokens=1* delims=/" %%B in ("%%~A") do #echo %%C)>output2.txt
All solutions do following:
The outer FOR processes ANSI (fixed one byte per character) or UTF-8 (one to four bytes per character) encoded text file upload_results.log line by line with skipping the first line and ignoring always empty lines and lines starting with a semicolon which do not occur here.
The line is split up on every occurrence of one or more commas into substrings (tokens) with assigning first comma delimited string to specified loop variable A. The first solution additionally assigns eighth comma delimited string to next loop variable B according to ASCII table.
The inner FOR processes the string assigned to loop variable A with using / as string delimiter to get assigned to specified loop variable file: and to next loop variable according to ASCII table the rest of the string after first sequence of forward slashes which is the full qualified file name.
The full qualified file name is output with command echo and appended either directly to file output2.txt (first solution) or first to a memory buffer which is finally at once written into file output2.txt overwriting a perhaps already existing file with that file name in current directory.
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.
del /?
echo /?
for /?
See also the Microsoft article about Using command redirection operators for an explanation of the redirections >, >> and 2>nul

How do I use a single quote in for /F command

I want to have .bat script to check if an IIS plugin exists, here is what I tried to assign a variable with the output of command,
set appcmd=%WINDIR%\System32\inetsrv\appcmd.exe
set cmd_list=%appcmd% list config -section:isapiFilters /text:[path='D:\iisplugins\F5XForwardedFor.dll'].path
for /f "usebackq" %%s in (`%cmd_list%`) do set filterPath=%%s
echo %filterPath%
But it didn't work, the output from command prompt:
set filterPath=ERROR
echo ERROR
It seems "=" needs to be escaped, so I tried again,
set cmd_list=%appcmd% list config -section:isapiFilters /text:[path^='D:\iisplugins\F5XForwardedFor.dll'].path
It still didn't work, "^" needs also be escaped? Tried one more time,
set cmd_list=%appcmd% list config -section:isapiFilters /text:[path^^='D:\iisplugins\F5XForwardedFor.dll'].path
Still did not work. Even the single quotes were escaped.
If executed the command alone without assigning to a variable, it worked well and returned the correct path of iis filter,
D:\iisplugins\F5XForwardedFor.dll
Found the solution, to add double quotes in the criteria,
set cmd_list=%appcmd% list config -section:isapiFilters /"text:[path='D:\iisplugins\F5XForwardedFor.dll'].path"
for /f "usebackq" %%s in (`%cmd_list%`) do set filterPath=%%s
echo %filterPath%
Instead of assigning the command result to a variable, it also does work for me to check "ERRORLEVEL".
%appcmd% list config -section:isapiFilter /text:[path='D:\iisplugins\F5XForwardedFor.dll'].path | findstr F5XForwardedFor
if %ERRORLEVEL% == 1 ( echo "Found" )
if %ERRORLEVEL% == 0 ( echo "Not Found")
You shouldn't need to make changes to a working command, just enclose it in doublequotes.
For /F "Delims=" %%A In ('"%cmd_list%"') Do Set "filterPath=%%A"
Full Example:
#Echo Off
Set "appcmd=%__APPDIR__%inetsrv\appcmd.exe"
Set "cmd_list="%appcmd%" list config -section:isapiFilters /text:[path='D:\iisplugins\F5XForwardedFor.dll'].path"
Set "filterPath="
For /F "Delims=" %%A In ('"%cmd_list%" 2^>Nul') Do Set "filterPath=%%A"
If Defined filterPath Echo %filterPath%
Pause

Batch file to remove first 10 chars from txt file

I'm trying to remove the first 10 characters from multiple lines inside a text file using a batch script, then output the results to a new file. I ran across this and it got me pointed in the right direction but the final output isn't working.
Here's what I've got so far:
setLocal EnableDelayedExpansion
CSCRIPT /nologo %windir%\System32\prnport.vbs -l > c:\IPPorts.txt
type c:\IPPorts.txt | findstr IP_ > c:\IPPorts2.txt
for /f "tokens=*" %%a in (c:\IPPorts2.txt) do (set line=%%a set chars=!line:~10! > c:\IPPorts3.txt)
for /f "delims=" %%x in (c:\IPPorts3.txt) do CSCRIPT /nologo %windir%\System32\prnport.vbs -d -r %%x
The 2nd line exports a list of printer ports to a file named IPPorts.txt. The 3rd finds the lines with "IP_" in them and exports to IPPorts2.txt. The 4th line is supposed to remove unneeded text (which it isn't doing) and export to IPPorts3.txt. And the last line will take the results from IPPorts3.txt and then delete those ports.
IPPorts.txt is as follows:
Server name
Port name IP_172.20.51.11
Host address 172.20.51.11
Protocol RAW
Port number 9100
SNMP Disabled
These lines are repeated for every port, of which there are several. Since I only need the line containing the port name, IPPorts2.txt looks like this:
Port name IP_172.20.51.11
Port name IP_172.20.52.58
Port name IP_172.20.53.16
Port name IP_172.20.54.19
Port name IP_172.20.55.15-1
Port name IP_172.20.55.15
Port name IP_172.20.55.11
Where I'm having trouble is removing the "Port name " portion of the lines (the first 10 characters). I want the output to read on each line as "IP_X.X.X.X". The problem is the 3rd file is always empty.
Where am I going wrong? Any help is greatly appreciated.
EDIT:
This is further down under Endoro's answer, but I thought it might be nice to post the answer here. Here's what I changed the 4th line to:
for /f "tokens=* delims=" %%c in ('type c:\IPPorts2.txt') do (
set LINE=%%c
>> c:\IPPorts3.txt echo !LINE:~10!
)
This has corrected my problems. Thanks everyone!
try this:
(for /f "tokens=3" %%i in (IPPorts2.txt) do #echo %%i)>IPPorts3.txt
Script to get directory name out of DIR command output :
...
20/09/2014 01:23 [DIR] some1
21/09/2014 02:34 [DIR] some2
22/09/2014 03:45 [DIR] some3
23/09/2014 11:22 [DIR] some4
...
We want it to be:
some1
some2
some3
some4
...
Code :
#FOR /f "tokens=4" %%D IN (i:\test.txt) DO #( echo %%D ) >> result.txt
In your case tokens=3, not perfect but does the job with few lines manually edited in the result.
(For /f "tokens=3delims= " %%i in (ipports2.txt) do echo %%i) >ipports3.txt
should do it for you.
The paretheses are important - ensure that the file is created anew. If omitted, will only generate the last line.
Simply uses the delimiter [space] to tokenise the string on each line into token1=Port, token2=Name and sets %%i to each token3 in turn.
The following isn't really a different solution but merely a suggestion to simplify your script by reducing the number of output files.
In fact, it is possible to exclude all of them from the script, unless you need to keep them for history.
Basically, the idea is first to apply FINDSTR directly to the output of prnport.vbs:
CSCRIPT /nologo %windir%\System32\prnport.vbs -l | FINDSTR "IP_"
then apply a loop directly to the output of FINDSTR (note the single quotation marks around the piped command line, as well as the escaped |):
FOR /F "tokens=3" %%A IN (
'CSCRIPT /nologo %windir%\System32\prnport.vbs -l ^| FINDSTR "IP_"'
) DO …
and call prnport.vbs with another set of arguments in that same loop:
FOR /F "tokens=3" %%A IN (
'CSCRIPT /nologo %windir%\System32\prnport.vbs -l ^| FINDSTR "IP_"'
) DO (
CSCRIPT /nologo %windir%\System32\prnport.vbs -d -r %%A
)
The tokens option of a FOR /F loop specifies which token (or field) to take based on a specific delimiter or set of delimiters. The default set of delimiters is a space, a comma, a tab. Your Port name IP_whatever lines conveniently consist of exactly three tokens and the third one is what you are after, hence "tokens=3" in the options.
So, as you can see, no output files, the necessary value is extracted and passed to the target command in the same iteration.

Win CLI: Get Tomcat drive

I am running the following command to get Tomcat location from the Registry.
for /f "tokens=2 delims=REG_SZ" %t in ('reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Apache Software Foundation\Tomcat\5.5" /v InstallPath | find "REG_SZ"') do set drive=%t
The output is set drive= C:\Tomcat 5.5
It looks like the the chars between = and C:\ are not spaces, since my command to substitute spaces for nothing: set drive=%drive: =% does not work.
Any suggestions?
It is a tab. Replacing works only in a batch file.
set drive=%drive: =%

Resources