simple xmlstarlet example to copy files - terminal

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 . . .

Related

Building string in batch script on Windows

I'm trying to build a string in my batch script but I couldn't get it to working.
Here's what I'm trying:
#echo off
set str="wt -p "
for /r "C:\Documents\Files\" %%f in (*) do (
set str=%str% "Command Prompt" cmd /k "echo %%f";new-tab -p
)
echo %str%
pause
wt -p command is supposed to open it in the new Windows Terminal instead of regular Command Prompt.
I want to specify that %%f which is the full path of the file does include blank spaces inside it. So the command should be accepting it.
I've always been struggling with creating batch scripts, it is overwhelming after some point too. What am I doing wrong here?
When you want to use a variable that you defined/changed within a code block, you need delayed expansion.
#echo off
setlocal enabledelayedexpansion
set "str=wt -p "
or /r "C:\Documents\Files\" %%f in (*) do
set "str=!str!"Command Prompt" cmd /k "echo %%f";new-tab -p "
)
echo %str%
goto :eof

.bat curl with for complaining about a ( was unexpected at this time

I have the following code.
#echo off
SetLocal EnableDelayedExpansion & REM All variables are set local to this run & expanded at execution time rather than at parse time (tip: echo !output!)
REM Get full path to run.bat file (excluding filename)
set wrapper_dir=%~dp1dist
set arch=amd64.exe
set system=windows
mkdir %wrapper_dir%
REM get_my_version - Find the latest version available for download.
(for /f %%i in ('curl -f -s https://prysmaticlabs.com/releases/latest') do set my_version=%%i) || (echo [31mERROR: require an internet connection. ESC[0m && exit /b 1)
echo ESC[37mLatest release is %my_version%.ESC[0m
IF defined USE_MY_VERSION (
echo [33mdetected variable USE_MY_VERSION=%USE_MY_VERSION%[0m
set reason=as specified in USE_MY_VERSION
set my_version=%USE_MY_VERSION%
) else (
set reason=automatically selected latest available release
)
echo Using version %my_version%.
set PROGRAM_REAL=%wrapper_dir%\program-%my_version%-%system%-%arch%
if [%1]==[program-real] (
if exist %PROGRAM_REAL% (
echo ESC[32mBeacon chain is up to date.[0m
) else (
echo ESC[35mDownloading beacon chain %my_version% to %PROGRAM_REAL% %reason%[0m
for /f "delims=" %%i in ('curl --silent -w "%%{http_code}" -L "https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%" -o "%PROGRAM_REAL%" ') do set http=%%i
if %http%=="400" (
echo ESC[35mNo program real found for %my_version%ESC[0m
exit \b 1
)
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sha256 -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sha256
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sig -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sig
)
)
At the line of the second for loop inside the IF statement(second IF statement) it complains ( was unexpected at this time.
The way to work around it , is to "grease" the batch file by running this command (on the prompt directly)
>for /f "delims=" %i in ('curl --silent -w "%{http_code}" -L https://prysmaticlabs.com/releases/beacon-chain-%prysm_version%-%system%-%arch% -o gg.txt') do set http=%i
ONLY then does it work
>prysm1.bat program-real version
A subdirectory or file C:\Users\HP\Documents\Investments\ethbox\prysm\dist already exists.
Latest release is v1.4.2.
detected variable USE_MY_VERSION=fake
Using version fake.
Downloading beacon chain fake to C:\Users\HP\Documents\Investments\ethbox\prysm\dist\program-fake-windows-amd64.exe as specified in USE_MY_VERSION
I know it has to do with the way the variables are expanded at execution vs parse time and that maybe a bracket, _ or other special characters are causing this. But i tried a couple of things and the problem persists. (all did not work)
quote the entire for with double quotes "
use brakets around the for
not using the _ in my_version.
Any ideas?
thanks
UPDATES after comments below
The script now does not complain after #aschipfl 's suggestions. However the last if stat is not evaluating to true. Made the for echo the result to ensure value in if stat.
for /f "delims=" %%i in ('curl --silent -o nul -w "%%{http_code}" https://prysmaticlabs.com/releases/beacon-chain-%version%-%system%-%arch% ') do set "http=%%i" && echo %%i
Based on all the constructive feedback, here is the final working version the summary of which :
Replacing [] with ""( quotes) in all if statements in order to protect spaces and other special characters.
Using !! vs %% for http var since it is in a block with delayed expansion
Check if download exists (404 error) then proceed and download.
#echo off
SetLocal EnableDelayedExpansion & REM All variables are set local to this run & expanded at execution time rather than at parse time (tip: echo !output!)
.... same code as above...
if "%1"=="program-real" (
if exist "%PROGRAM_REAL%" (
echo ESC[32mBeacon chain is up to date.[0m
) else (
echo ESC[35mDownloading beacon chain %my_version% to %PROGRAM_REAL% %reason%[0m
for /f "delims=" %%i in ('curl --silent -o nul -w "%%{http_code}" -L "https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%" ') do set "http=%%i" && echo %%i
if "!http!"=="404" (
echo ESC[35mNo program real found for %my_version%ESC[0m
exit /b 1
)
curl -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch% -o %PROGRAM_REAL%
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sha256 -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sha256
curl --silent -L https://prysmaticlabs.com/releases/beacon-chain-%my_version%-%system%-%arch%.sig -o %wrapper_dir%\beacon-chain-%my_version%-%system%-%arch%.sig
)
)

batch equivalent of shell for output of command

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"

Pass variables and loop FTP script for different servers in Windows command line

I would like to build a script that runs from the Windows command line to update an existing file to multiple web domains using FTP. I would like to know how to build a loop that runs the ftp commands and passes variables to it.
The file that passes the arguments to the FTP script will be in the format of:
ftp.domain1.com username1 password1
ftp.domain2.com username2 password2
and so forth.
The FTP script will have the following commands:
open "$variable_for_domain_name"
$variable_for_username
$variable_for_password
cd /public_html
bin
hash
put "test.txt"
close
bye
Using this example, the first iteration of the loop would execute the following FTP commands:
open "ftp.domain1.com"
username1
password1
cd /public_html
bin
hash
put "test.txt"
close
bye
and the second iteration of the loop would execute the following:
open "ftp.domain2.com"
username2
password2
cd /public_html
bin
hash
put "test.txt"
close
bye
I understand to run the script once I would execute:
c:\windows\system32\ftp.exe -i <ftp_script.txt
Where ftp_script.txt contains the above FTP commands with the values stated explicitly. How can the loop be done so that I can execute one command from the windows command line but update files on multiple domains?
Also, I would like to add a command to the FTP script to verify the new contents of test.txt.
The current version of the batch file I'm running is:
`
#echo off
setlocal EnableDelayedExpansion
FOR /F "tokens=1,2,3 delims= " %%a in (ftp_config.txt) do (
echo %%a
echo %%b
echo %%c
call :SUB_ftp_cmd %%a %%b %%c
)
::exit
:SUB_ftp_cmd
echo open %1>ftp.txt
echo %2>>ftp.txt
echo %3>>ftp.txt
echo cd /public_html>>ftp.txt
echo bin>>ftp.txt
echo hash>>ftp.txt
echo put "test.txt">>ftp.txt
echo close>>ftp.txt
echo bye>>ftp.txt
::c:\windows\system32\ftp.exe -i <ftp.txt
::del ftp.txt
::exit /b
`
The resulting file ftp.txt contains the following:
`
open
ECHO is off.
ECHO is off.
cd /public_html
bin
hash
put "test.txt"
close
bye
`
I added the setlocal EnableDelayedExpansion to try and resolve the fact that the variables are not correctly being passed to the subroutine, but it did not change the result.
The simplest way is to create a sub-function in a batch file and call it multiple times with different arguments. This way you do not need any external configuration file and hence no loop:
#echo off
call :download ftp.domain1.com username1 password1
call :download ftp.domain2.com username2 password2
exit
:download
echo open %1>ftp.txt
echo %2>>ftp.txt
echo %3>>ftp.txt
echo bye>>ftp.txt
ftp.exe -s:ftp.txt
del ftp.txt
exit /b
If you really need to configure the parameters in an external file use for command to read the file:
#echo off
FOR /F "tokens=1,2,3" %%i in (config.txt) do call :download %%i %%j %%k
exit
:download
echo open %1>ftp.txt
echo %2>>ftp.txt
echo %3>>ftp.txt
echo cd /public_html>>ftp.txt
echo bin>>ftp.txt
echo hash>>ftp.txt
echo put "test.txt">>ftp.txt
echo close>>ftp.txt
echo bye>>ftp.txt
ftp.exe -s:ftp.txt
del ftp.txt
exit /b
Where the config.txt has the format your wanted:
ftp.domain1.com username1 password1
ftp.domain2.com username2 password2

Redirect command as well as output to text file in Windows CLI?

I need to redirect both the command as well as its output to a text file in Windows CLI. For instance, I am running the nslookup command on a subnet using a FOR loop,
for /L %i IN (1,1,254) DO nslookup 192.168.1.%i >> nslookup.txt
However, this only redirects the output of the command.
Is there a way to redirect both the command as well as the output to a text file? Please do not tell me about clip and select all/copy commands.
You can proceed the command with "cmd /c" to start a new command prompt, and redirect the output of the command prompt:
cmd /c for /L %i IN (1,1,254) DO nslookup 192.168.1.%i > nslookup.txt
Note that you only need to use a single greater than (>) since the output of cmd is going to nslookup.txt. Sadly, this misses the error output, so you are not seeing the ***Request to UnKnown timed-out for each failed address.
Your FOR loop is right on and it sounds like you are already getting the output you want, so all you need to do is ECHO the command before running it:
for /L %i IN (1,1,254) DO ECHO nslookup 192.168.1.%i&nslookup 192.168.1.%i >> nslookup.txt
The & chains the commands together so the ECHO is run before the nslookup.
If you want to use a batch file, it becomes a bit more clear:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET Outfile=nslookup.txt
REM Log the date/time.
ECHO %DATE% - %TIME%>%Outfile%
FOR /L %%i IN (1,1,254) DO (
SET Command=nslookup 192.168.1.%%i
REM Print the command being run.
ECHO !Command!>>%Outfile%
REM Run the command.
!Command!>>%Outfile%
)
ENDLOCAL
for /L %i IN (1,1,254) DO (#echo nslookup 192.168.1.%i & nslookup 192.168.1.%i) >> nslookup.txt
This works. Still I'm sure there are smarter ways to do this.

Resources