Shell command:
export My_VAL=`cd shell && my-cli preview | sed -n '2 p'`
Command above:
cd to shell folder
runs "my-cli preview" command
gets the second line of command line output
stores it in MY_VAL
What would be it's equivalent windows/batch command?
setlocal ENABLEDELAYEDEXPANSION
set "MY_VAL="
cd shell
for /f "skip=1 tokens=*" %%a in ('my_cli preview') do if "!MY_VAL!"=="" set "MY_VAL=%%a"
Related
In Windows Command Prompt (CMD), when my command1 is executed, the output is a full command (command2) with arguments.
My question is, is there a way to execute command2 directly just after command1 is executed?
Usually the commands could be piped like "command1 | command2". but here even the command name of command2 is a part of the output of command1. So I'm not sure if there's a way to use the pipe.
I understand your question is that command1's text output is another command name - command2. If so, there's a way though not so clean. Try
> for /F "tokens=*" %a in ('first command') do %a additionalSecondCommandArg
Example. My win-10 has notepad.exe in \Windows. If I want to open aaa.txt with it,
> for /F "tokens=*" %a in ('dir /B \windows\note*.exe') do %a aaa.txt
which runs notepad.exe and trys to open aaa.txt. You can check what "tokens=*" means by typing "for /?" in command prompt.
I have a git command to get the latest SHA of the current repo as follows:
git log --pretty=format:"%H" -n 1
I have a windows batch script I'd like to use this in as follows:
SET CURRENT_SHA=???
But I'm at a loss as to how to get the output from that call to git into the variable so that I can make use of it.
Edit
I've tried the following (which seems to be the general advice I've read here and elsewhere):
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /F "tokens=* USEBACKQ" %%i IN (`git log --pretty=format:"%H" -n 1`) DO (SET CURRENT_SHA=%%i)
ECHO Current Sha: %CURRENT_SHA%
..but I get:
fatal: failed to stat 'format:i) ECHO Current Sha: 48bce83e800b96607afb2a387c4fcd7b0b0f037e
So presumably there's a problem with the quotes?
I don't have a Windows system handy to test, but I think something along these lines:
FOR /F %i IN (`git log --pretty=format:"%%H" -n 1`) DO SET CURRENT_SHA=%i
Note that the "%H" needs to be escaped, but to use this line in a batch file you also need to double escape everything. You may also need to escape the double-quotes with ^. I think this ought to work:
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "tokens=* USEBACKQ" %%a in (`git log --pretty^=format:"%%H" -n 1`) do (SET CURRENT_SHA=%%a)
ECHO Current Sha: %CURRENT_SHA%
But really, if you want to do shell programming in Windows just use Powershell and then you can do:
$CURRENT_SHA=git log --pretty=format:"%H" -n 1
I know the normal behaviour when running an EXE in a batch script is for the batch script to wait for the EXE to exit before continuing, but is there any way to get the batch script to continue execution, but redirect its stdout to the stdin of the EXE?
Basically I'm trying to achieve this neat trick or something similar...
#ECHO OFF
echo This is a windows batch script...
dir /p C:\
C:\cygwin\bash.exe <--- Do some magic here
echo This is a bash shell script...
ls -la /cygdrive/c/
exit
echo We're back to the windows batch script again
REM Note: being able to return to the batch script isn't important
Any ideas on how to achieve this? Thanks.
This should work:
#ECHO OFF
echo This is a windows batch script...
dir /p C:\
(
ECHO echo This is a bash shell script...
ECHO ls -la /cygdrive/c/
ECHO exit
) | C:\cygwin\bash.exe
echo We're back to the windows batch script again
EDIT: Reply to the comment
If you want not to add ECHO to each bash line, you may use this method:
#ECHO OFF
echo This is a windows batch script...
dir /p C:\
rem Get the number of the first line in this file that start with "###"
for /F "delims=:" %%a in ('findstr /N "^###" "%~F0"') do set "line=%%a" & goto break
:break
rem Pass from that line to end of this file to bash.exe
more +%line% "%~F0" | C:\cygwin\bash.exe
echo We're back to the windows batch script again
goto :EOF
#################################################################
# The remainder of this file is a bash script running in cygwin #
#################################################################
echo This is a bash script!!
ls -la /cygdrive/c/
EDIT #2:
I borrowed SonicAtom's method and slightly modified it in order to make it simpler. Here it is:
#ECHO OFF
setlocal
rem This is a magic trick to run the bottom half of this script as a bash script
if not defined _restarted (
set _restarted=true
cmd.exe /Q /D < "%~F0"
rem Put any cleanup commands here
echo/
echo Bash script finished
pause
exit /B
)
cls
"C:\cygwin\bin\bash.exe"
#################################################################
# The remainder of this file is a bash script running in cygwin #
#################################################################
echo This is a bash script!!
ls -la /cygdrive/c/
No replies?
Well I found the answer myself:
test.bat:
#ECHO OFF
rem This is a magic trick to run the bottom half of this script as a bash script
if not exist _.bat (
echo #ECHO OFF >_.bat
echo cmd.exe -c ^< %~nx0 >>_.bat
_.bat
rem Put any cleanup commands here
del _.bat
echo.
echo Bash script finished
pause
exit /B
)
cls
"C:\cygwin\bin\bash.exe"
#################################################################
# The remainder of this file is a bash script running in cygwin #
#################################################################
echo This is a bash script!!
ls -la /cygdrive/c/
How it works:
The file starts off as a batch script. It writes another (temporary) batch script which runs another cmd.exe shell, directing the original batch file to cmd.exe as stdin. The original batch file then acts like a text file containing commands typed on the keyboard. It runs bash.exe, and continues to provide stdin to bash. The only disadvantage is that the #ECHO OFF doesn't take effect in the part of the batch file that runs before bash.exe is called. This is because there doesn't seem to be a way to turn off keyboard echoing in cmd.exe.
Windows Command Prompt. I want to do the following in a ONE-LINER COMMAND.
i want to set a variable with a simple xml structure:
<pathlist>
<path>C:\file.txt</path>
<path>C:\file2.txt</path>
</pathlist>
like this:
SET "_myvar=^<pathlist^>^<path^>C:\file.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pathlist^>"
then i want to echo this and pipe it to xmlstarlet:
echo !_myvar!|xmlstarlet sel -t -v "//path"
then the result should be put into another var sourcefiles, with a for loop?
for /f %i in ('call echo %^_myVar%^|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i
and finally pscp sourcefile to a remote Unix
pscp -l user-pw password %sourcefiles% openstack#remoteIP:/opt/testfolder
i cannot use temporary files for this taks, i tried this:
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pat
hlist^>"& for /f %i in ('cmd /v:on /c echo !_myVar!|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i &&cmd /v:on /
c pscp -l user -pw password %sourcefiles% openstack#remoteIp:/opt/testfolder
and get this error:
| was unexpected at this time.
the problem is when i want to echo the var content to xmlstarlet, i think. Anybody know how to solve this?
Edit
using call echo instead of cmd /v:on in a slighty simplified command i get this error:
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pat
hlist^>"& 'call echo ^^%^_myVar!%^^|xmlstarlet sel -t -v "/*"
error:
-:1.1: Document is empty
^
-:1.1: Start tag expected, '<' not found
^
SET "_myVar=^<pathlist^>^<path^>C:\file1.txt^</path^> ^<path^>C:\file2.txt^</path^>^</pathlist^>"& for /f %i in ('cmd /v:on /c echo !_myVar!^|xmlstarlet sel -t -v "/*" ') do set sourcefiles=%i &&cmd /v:on /c pscp -l user -pw password %sourcefiles% openstack#remoteIp:/opt/testfolder
should fix your | was unexpected at this time. error. The only change is to escape the pipe with ^, which tells cmd that the pipe is part of the command, not an instruction to cmd.
My focus was on the error-report about the pipe.
What I found was that echo doesn't line < as the first character of the string being echoed - so I toyed around with it a while.
And then I cut the grass and cleaned up the yard a bit.
And then I thought - well, how about sed? I use GNUSED, and since you're outputting to a *Nix system, the world of SED shouldn't be too fearsome.
How about - as a batch file,
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "uname=%~1"&shift
SET "pass=%~1"&shift
SET "sourcefiles="
SET "_myVar=*pathlist?"
:dirloop
IF "%~1" neq "" SET "_myvar=%_myvar% *path?%~1*/path?"&shift&GOTO dirloop
SET "_myvar=%_myvar%*/pathlist?"
for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do ECHO %%i&set "sourcefiles=!sourcefiles! %%i"
ECHO pscp -l %uname% -pw %pass% %sourcefiles% openstack#remoteIp:/opt/testfolder
Which you could execute as a single line
thisbatch user password path1 path2 path3...
(yeah - the pscp is just being echoed for verification...)
or, for an all-in-one-line version, which I'll spread over several for clarity's sake (and because I wrote it in a "DOS" session):
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"
SET "sourcefiles="
for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i"
ECHO pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
SInce * and ? can't be used as legitimate filename characters, including them in the string set into the environment, then using echo to send *? to sed which converts them to <> for input to xmlstarlet - now that might work (but I don't have xmlstarlet for testing - I conclude it simply outputs a list of some variety...)
Note the ^ to escape the ; in the sed command!
Here's the all-in-one-line version that may work
SETLOCAL ENABLEDELAYEDEXPANSION&SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"&SET "sourcefiles="&(for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i")&pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
but I've no way of testing this in your environment.
Here's the line I used:
SETLOCAL ENABLEDELAYEDEXPANSION&SET "_myvar=*pathlist?*path?C:\file.txt*/path? *path?C:\file2.txt*/path?*/pathlist?"&SET "sourcefiles="&(for /f %%i in ('echo %_myvar%^|sed s/\x2a/\x3c/g^;s/\x3f/\x3e/g^|xmlstarlet sel -t -v "/*" ') do set "sourcefiles=!sourcefiles! %%i")&pscp -l user -pw password !sourcefiles! openstack#remoteIp:/opt/testfolder
I used a batch file called xmlstarlet.bat
#ECHO OFF
SETLOCAL
ECHO phial1.txt
ECHO phial2.txt
ECHO phial3.txt
ECHO phial4.txt
ECHO phial5.txt
which should simply produce 5 lines of output when run
I used a batch file called pscp.bat
#ECHO OFF
SETLOCAL
ECHO running PSCP
ECHO %*
PAUSE
And the result was:
running PSCP
-l user -pw password phial1.txt phial2.txt phial3.txt phial4.txt phial5.txt openstack#remoteIp:/opt/testfolder
Press any key to continue . . .
I don't know how can i make this clear in a short sentence, so i give this example
Bash :
./foo $(ls -a)
First, "ls -a" is evaluated and converts to its output. So we 've got this line
./foo some_script Downloads
and then that's executed.
How can i achieve the same by using the windows command line?
P.S. : I need to use it when my IDE makes a build, so using PowerShell or CygWin is not an option
Assuming that the filenames contain no embedded spaces:
#echo off
setlocal enabledelayedexpansion
set "args="
for /f "delims=" %%i in ('dir /a /b') do set "args=!args!%%i "
.\foo %args%