How To Use A Command Output As An Argument of Another Command? - windows

This is an update for an old question of mine, because I wasn't so clear in it.
Honestly I didn't even have the Win_CLI experience to ask the right question. So I'm sorry, and I wish this time is better.
Like I said in my question, I need to use a command output as an argument of another command.
For Example:-
CLITool -Switch (Value_OF_a_Command_OutPut)
I think what I need here is the Set command:-
Set Value=
Command
CLITool -Switch %Value%
But, the problem is that the output value will be printed with a Line_Break!
OutPut_Value
CLITool -Switch (Nothing!)
and because of this, the command won't be executed correctly.
So.. my question now, is how to fix that Set Command problem,
and If possible, how to do the same thing in another technique?
BTW..As most of Linux users know, it's possible to use a command output in the same commandline of the other command as follow:
CLITool -Switch $(Command)
I wish if there is something similar in windows CLI.
Please tell me if it's better to post my actual command.
Thanks Guys,
Appreciate Your Help :)

You can use a for /f the command. For each line in the output of the executed command, the code in the do clause will be executed, with the content of the line stored in the for replaceable parameter
for /f "delims=" %%a in ('command') do CLITool -Switch %%a
Also, you can execute the command, send the output to a temporary file and retrieve its contents with a input redirection and a set /p command
command > "tempfile"
<"tempFile" set /p "var="
CLITool -Switch %var%
In both cases, under the assumption that command will echo only one line. If it generates more lines, the for will execute CLITool for each of the lines, while the redirection option will only retrieve the first line from the temp file.
The third way to solve it is to use a pipe. The left side of the pipe generates the content and the right side reads this content.
command | ( set /p "var=" && #call CLITool -Switch %%var%% )
Why the call and the %%var%% syntax? We are setting a variable inside a line and in the same line we need to retrieve the variable and for this to work, delayed expansion is needed. But by default delayed expansion is disabled and enabling it does not solve anything as each side of the pipe is executed inside its own cmd instance that is started without delayed expansion (default behaviour) so, the trick is to escape the reference to the variable (the reason for the doubled percent sign) and force a double parse phase in the line (the reason for the call)
The alternative could be something as
command | cmd /e /v /q /c "set /p "var=" && CLITool -Switch !var!"
This way we are running in the right side of the pipt a cmd instance with delayed expansion enabled (/v), extensions enabled (/e), with echo off (/q) that will execute the needed command.
But the pipe code (in both cases) has a drawback: the variable is set in a separate cmd instance, so, your main batch file will no have access to its value.

If You have two lines being returned from an executable and wish to assign each to a variable, then
set "line1="&set "line2="
for /f "delims=" %%a in ('yourcommand') do if defined line1 (set "line2=%%a") else (set "line1=%%a")
set line
should save and display the results.

Related

Why does this batch file close immediately when called from a shortcut, despite it working correctly when called from command line?

Edit 3 (SOLUTION):
As michael_heath described in his answer, the issue came down to two things: how exactly windows builds commands when executing something through a shortcut, and the very specific (and frankly ridiculous) consequences of the \C switch on cmd.exe. For future reference, if any other poor soul stumbles into this StackOverflow question, the problem was fixed by changing the "Target" property in the shortcut to a slightly edited version of Michael's answer, specifically C:\Windows\System32\cmd.exe /C #"C:\{path-to-script}\link.bat". Here's a screenshot too, if necessary, although you unfortunately can't see the whole Target line.
Huge thanks again to Michael.
I am attempting to make a personal batch utility to create a symlink on the desktop, much in the same way "Send to... > Desktop" works with shortcuts. I use symbolic links frequently to allow things like my bash configuration files (.bashrc and .bash_profile, etc) to be version controlled elsewhere for portability, and for several other things on my computer.
For ease of use, my idea was to create a simple batch file to do this for me, and place the symlink on the desktop. Then, I would put a shortcut to this file in the Send To folder so it appears in Send To in the context menu (I am aware that mklink requires admin privileges, so the shortcut is set to run as administrator also).
The following is the file I have written:
#echo off
set f=%~1
set switch=
if exist "%f%\*" set switch=/D
for /F "delims=" %%i in ("%f%") do set name=%%~nxi
mklink %switch% "%USERPROFILE%\Desktop\%name%" "%f%"
if not %ERRORLEVEL%==0 pause
Here is the general idea of what I'm trying to do:
Strip the quotes on the input, if any (%~1)
Check if the input is a directory
Get the base name and file extension of the input (%%~nxi)
Make the link
If an error occurred, pause so it can be seen rather than exiting (because the batch file is called from the shortcut)
It works perfectly fine until I give it an input that contains spaces in the name of the file or directory. I actually haven't tested what happens if there is a directory with a space in the path, but not in the base name of the actual file or directory, but I assume the same problem will be present.
I have made several changes to attempt to get it to work with files with spaces, including stripping the quotes on the input in that first line so that the quotes aren't doubled later, and that "delims=" thing on the for loop. Those two solutions I found here, actually.
But despite my best efforts, no matter what I do, the file closes immediately when given an input with a space. I have littered every line with pauses, run the script from the command line with a manually entered input so it would not exit, and run each individual command (where possible) from the command line.
Infuriatingly, when I run it from the command line or run the individual commands, it all works perfectly even with spaces in the input. I even created another batch file that does nothing but output the input it receives and ran that from Send To, and confirmed that the input is the same as I entered from the command line.
What on Earth is going wrong then when called from that shortcut in Send To?
Edit 1: The properties of the shortcut itself are as follows:
Target: C:\Users{username}\vc\git\util-scripts\bat\link.bat
Start in: C:\Users{username}\vc\git\util-scripts\bat
Here is a screenshot as well:
Because I just made my account here I can't embed the picture but here it is
Edit 2: This is the current code I am using, as suggested by Gerhard Barnard, however the problem still persists:
#echo off
set "fname=%~1"
set switch=
if exist "%fname%\.*" set "switch=/D"
for /F "delims=" %%i in ("%fname%") do (
mklink %switch% "%USERPROFILE%\Desktop\%%~nxi" "%fname%"
if errorlevel 1 pause
)
#echo off
setlocal
rem Set the path for the created symlink.
set "linkdir=%USERPROFILE%\Desktop"
for %%A in (%*) do call :link "%%~A"
rem Check results.
echo: & dir /A:L "%linkdir%" & pause
exit /b
:link
set "switch="
if exist "%~1\*" set "switch=/D"
for /F "delims=" %%A in ("%~1") do set "name=%%~nxA"
if not defined name echo Variable "name" not defined.& exit /b 1
mklink %switch% "%linkdir%\%name%" "%~1"
exit /b 0
Your batch-file code is working.
This code does multiple file or folders.
This code also helped to test all at once,
files, folders, symlinked files and symlinked
folders as targets.
If you only want 1 target to be processed,
then just change the %* to "%~1".
The main issue is the command string in the shortcut.
C:\Users\{username}\vc\git\util-scripts\bat\link.bat
The file type of .bat is going to build a command such as:
C:\Windows\System32\cmd.exe /C "C:\Users\{username}\vc\git\util-scripts\bat\link.bat" %*
%* is substituted with the passed arguments.
If you have the command with an argument with double quotes,
it may look like:
C:\Windows\System32\cmd.exe /C "C:\Users\{username}\vc\git\util-scripts\bat\link.bat" "C:\Users\a file.txt"
The command string after /C has 4 double quotes
and double quotes are at both ends.
The behavior changes due to the double quotes.
A quote from cmd /?:
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
1. If all of the following conditions are met, then quote characters
on the command line are preserved:
- no /S switch
- exactly two quote characters
- no special characters between the two quote characters,
where special is one of: &<>()#^|
- there are one or more whitespace characters between the
two quote characters
- the string between the two quote characters is the name
of an executable file.
2. Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
In section 1, "no /S switch" is true, then the next is
"exactly two quote characters" which is false.
This now applies section 2, which can make the
command string after /C with stripped double quotes:
C:\Users\{username}\vc\git\util-scripts\bat\link.bat" "C:\Users\a file.txt
The space is quoted though the rest is exposed,
which is an invalid command string in this case.
Note that C:\Users\a file.txt is an example
passed argument that was double quoted as
"C:\Users\a file.txt".
A change in the command string in the shortcut to:
C:\Windows\System32\cmd.exe /C echo: & "C:\Users\{username}\vc\git\util-scripts\bat\link.cmd"
The command is now C:\Windows\System32\cmd.exe which
gives some more control with the command string.
After the /C, echo: is used to avoid the command
string beginning with a double quote, which helps to
prevent double quotes being stripped at both ends because
the command string no longer starts with a double quote.
After the & is the command that is important and will
now work even if the arguments end with a double quote.
You can replace the echo: & with another initial command.
You can also use # before the command string, so the shortcut
command string would be:
C:\Windows\System32\cmd.exe /C #"C:\Users\{username}\vc\git\util-scripts\bat\link.cmd"
So whatever works the best for your use case.
Try without the /F option which is not needed here, we also do not need to set a variable as it will work perfectly fine with the expanded meta variable:
#echo off
set "fname=%~1"
set switch=
if exist "%fname%\.*" set "switch=/D"
for %%i in ("%fname%") do (
echo mklink %switch% "%USERPROFILE%\Desktop\%%~nxi" "%fname%"
if errorlevel 1 pause
)

wget: batch download with increment

I have this situation: I want to download a bunch of files named like this:
683482, 684483, 685484, 686485, 687486, 688487, 689488, 690489, 691490, 692491, ...
As you can see, the files are numbered with an increment of 1001. So, what's the easiest way to do a batch download?
Please try this:
#!/bin/bash
for i in {683482..1000000..1001}
do
wget $i
done
ECHO OFF
CLS
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /L %%A IN (683482,1001, 692491) DO (
SET stringWget=wget %%A
!stringWget!
)
I'll go step by step:
ECHO OFF prevents windows command line from displaying every command on the command-prompt (this is optional; But, looks clean).
CLS clear screen; This clears command prompt's console display. It does not clear the temporary environment variables or command history.
SETLOCAL ENABLEDELAYEDEXPANSION is used for non-blocking statements and is Windows specific; When we use %ENVIRONMENTVARIABLE% , we are implying blocking statement and when we use !ENVIRONMENTVARIABLE! we are implying non-blocking statement (meaning, in a loop, we see updated values of %A instead of repeating what %A had when entering loop). We use %% instead of %in batch files.
FOR loop's syntax can be found in reference.
Reference: https://stackoverflow.com/a/3541415

Please explain following outputs in Windows command prompt

I tried to customize windows command prompt with the following batch file.
#echo off
cls
:cmd
set /p "cmd=%cd%>"
%cmd%
goto cmd
So, when I open the batch file, it just takes my command into cmd variable and executes it and again prompts for a new command.
But the following command echo %cd% outputs only %cd%
Then I enabled delayedexpansion and used echo !cd! and got the desired output.
I think, because of the delayed expansion, cmd variable now holds echo c:\Users\Sourav\Desktop (am I correct?)
But I got confused when I tried to open the command prompt (not the batch file) and tried the following commands.
I thought, I will get c:\Users\Sourav\Desktop but I got !cd!. This contradicts my understanding of how echo !cd! is working in first case.
Why am I getting different output in the second case?
Can anyone suggest any improvement to the batch file, so that I can get desired output just using echo %cd% in first case?
you need another level of parsing. You can use call to do so:
#echo off
cls
:cmd
set /p "cmd=%cd%>>"
call %cmd%
goto cmd

cmd for loop mass renaming again oneliner

I'm over my head with this - spent too much time searching already - evidently I don't understand the basics of CMD variables etc. - and it always gives me such a headache
why wouldn't this work?
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
the above code outputs the value of %tmpx% in some other scope - and it is always constant
yes, i run setlocal ENABLEDELAYEDEXPANSION
basically i need to do a simple rename of all files in folder from constantstring_somenameXX.tif to somenameXX.tif, where i.e. constantstring=0000000005
i had to use set because other posts rightly suggested that %a in a for loop has a special behaviour, and the substitutions wouldn't work for it as it is.
i would prefer not to use scripts and/or powershell - unless not using them is impossible
thank you
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
The problem with the previous code is delayed expansion. Yes, you enabled it, but you have not used it, and depending on how you enabled it, it will not work
In cmd, when a line or block of lines (code inside parenthesis) is reached, it is first parsed and then executed. During the parse phase, variable read operations are removed from the command, replaced with the value in the variable before the command starts to execute. So, if you change the value of a variable inside a line/block you can not retrieve the changed value inside the same line/block as there are no variable reads (they were replaced)
setlocal enabledelayedexpansion allows to replace (where needed) the variable read syntax from %var% to !var!, indicating to the parser that the read operation will be delayed until the execution phase.
So, in your case, your code should have been something like
setlocal enabledelayedexpansion & for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )
BUT this will not work (in default configured environments).
cmd has two execution modes: batch file and command line. In your case, you are using command line (no escaped percent sign in for loop) and in command line mode the setlocal enabledelayedexpansion will not work. It is intended for batch files (see setlocal /?)
How to make it work from the command line? By default cmd is started with delayed expansion disabled and you can not enable it if not inside a batch file. But you can start cmd with delayed expansion enabled and run your command in this started instance (see cmd /?)
cmd /v:on /c "for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )"
Anyway, to solve your rename problem, delayed expansion is not needed
for %a in (*_*.tif) do for /f "tokens=1,* delims=_" %b in ("%~nxa") do echo ren "%a" "%c"
That is, for each tif file with an underscore, take the name and extension of the file (%~nxa) as a string, and using the underscore as a delimiter between tokens, retrieve the first token (the text on the left of the first underscore) in %b and the rest of the text (to the right of the underscore) into %c. Now, just rename the original file name (stored in %a) to the contents of %c (the text on the right of the underscore)
In this code rename operations are only echoed to console. If the output is correct, remove the echo command.
! is the character to use rather than % when wanting execution time value. % does when it's read value.
CMD was written by IBM engineers and they were trying to make MSDos a programming language while making sure Dos commands ran the same. So we get a hodge podge.
& seperates commands on a line.
&& executes this command only if previous command's errorlevel is 0.
|| (not used above) executes this command only if previous command's errorlevel is NOT 0
> output to a file
>> append output to a file
< input from a file
| output of one command into the input of another command
^ escapes any of the above, including itself, if needed to be passed to a program
" parameters with spaces must be enclosed in quotes
+ used with copy to concatinate files. E.G. copy file1+file2 newfile
, used with copy to indicate missing parameters. This updates the files modified date. E.G. copy /b file1,,
%variablename% a inbuilt or user set environmental variable
!variablename! a user set environmental variable expanded at execution time, turned with SelLocal EnableDelayedExpansion command
%<number> (%1) the nth command line parameter passed to a batch file. %0 is the batchfile's name.
%* (%*) the entire command line.
%<a letter> or %%<a letter> (%A or %%A) the variable in a for loop. Single % sign at command prompt and double % sign in a batch file.

Setting a variable from an executable

I am running an executable in a batch file with two parameters;
cmd /k ""executable" "param1" "param2""
This returns a string that I want to launch. I can't figure out how to set this return in a variable and subsequently launch it in IE.
Any ideas?
If the returned string contains a single line you may use FOR /F to set the value of an environment variable. For example:
s1.cmd
echo this is a one line string
s2.cmd
#SETLOCAL
#ECHO OFF
for /f "tokens=*" %%a in ('cmd /c s1.cmd') do set MY_VAR=%%a
echo got: %MY_VAR%
ENDLOCAL
Result
C:\> s2.cmd
got: this is a one line string
C:\>
You can use the following syntax to capture the output of your executable into a variable:
FOR /F "tokens=*" %%i in ('%~dp0YOUR_APP.exe') do SET TOOLOUTPUT=%%i
Source
then you can pass the value on to IE like so:
START "YOUR_WINDOW_NAME" /MAX /D"C:\Program Files\Internet Explorer\" iexplore %TOOLOUTPUT%
I take it that the application code that determines the url is too complicated to be reproduced in a batch file directly, or the source to the executable has been lost. If not I personally would prefer to have the logic visible in the batch file itself.
start %1 %2
Edit: Romulo A. Ceccon posted a much better solution which doesn't involve any file system access and dirty tricks. Left this here for reference (it works with command.com as well if you need 9x compatibility), but please prefer Romulo's solution.
Go through an environment variable you set by using an intermediate helper script you dynamically generate from a template. You will need write permissions somewhere, otherwise it cannot be done (the Windows command shell language is very, very limited.)
Let's call your helper script template helper.tpl with the following contents:
set INTERMEDVAR=
Make sure that helper.tpl has only a single line (no trailing CRLF!) and make sure you don't have any spaces after the equals sign there.
Now, in your main script, capture the output from your command into a temporary file (let's call it my_output_file.tmp):
cmd /k ""executable" "param1" "param2"" > my_output_file.tmp
Then copy the contents of the helper template and the output together into your helper script, let's call it my_helper_script.cmd:
copy /b helper.tpl + my_output_file.tmp my_helper_script.cmd
Then evaluate the helper script in the current context:
call my_helper_script.cmd
Now the INTERMEDVAR variable is set to the first line of the output from "executable" (if it outputs more than one line, you're on your own...) You can now invoke IE:
start iexplore.exe "%INTERMEDVAR%"
And don't forget to clean up the created files:
del /q /f my_output_file.tmp my_helper_script.cmd
This will obviously not work when invoked multiple times in parallel - you'll have to parametrize the temporary file and helper script names using the current cmd.exe's PID (for example) so that they won't overwrite each other's output, but the principle is the same.
However, if you can get a real shell, use that. cmd.exe is extremely cumbersome.

Resources