Echoing output of command from variable - windows

I'm trying to create a simple batch script that stores the output of a command to tmp file, stores the content of the tmp file to a variable, and outputs the variable:
#setlocal enableextensions enabledelayedexpansion
#echo off
ping -n 1 10.1.0.2 > tmp
SET #var= < tmp
ECHO %#var%
del tmp
I would expect the above to work, but it outpus:
C:\Documents and Settings\Administrator\Desktop>pinger.bat
ECHO is off.
(Nb: taking the #echo off just outputs ECHO is on, including echoing all the lines of code)

The question pointed by #rostok (my answer here), shows the reason to not use the received packets to determine if the host is or not online. On the same subnet, over ipv4, with an offline host, you will get a "unreachable" error, but the packets are received.
For a simple test in ipv4, the linked answer can handle your problem. For a more robust test over ipv4 or ipv6 (that show different behaviour), this could help.
Anyway, the code to get the required data directly from the ping command,
set "received="
for /f "skip=6 tokens=5 delims==, " %%a in ('ping -n 1 10.1.0.2') do (
if not defined received set "received=%%a"
)
or from the file
set "received="
for /f "usebackq skip=6 tokens=5 delims==, " %%a in ("tmpFile") do (
if not defined received set "received=%%a"
)
Where the skip clause indicates that the first six lines should be skipped, and the tokens and delims select the required element inside the line
Packets: Send = 1, Received = 1,
^ ^^^ ^^ ^^^ ^ Delimiter position
1 2 3 4 5 Token number
But as said, this is not a reliable way to solve the problem

Related

Batch file variable filenames: make timestamp filename within a for loop?

I am trying to run a batch of Lighthouse audits from the command line and output the results to JSON files. To ensure they don't overwrite one another, each file name needs to be unique. I would like to use the timestamp as the filename ideally. I have been through many other Stack questions on a similar topic, but I cannot get this to work. I am currently using the below code, written within a batch file and called from Windows command line:
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "delims=" %%a in (C:\Users\User\Desktop\Folder\Lighthouse_Project\urls.txt) DO (
set dte=!DATE:/=-%!
set tme=!TIME::=-%!
set tme=!%tme:.=-%!
ECHO Line is: %%a
lighthouse %%a --quiet --chrome-flags=" --headless" --output=json --output-path=C:\Users\User\Desktop\Folder\Lighthouse_Project\json_logs\!dte!!tme!.json
)
The first file produced has the correct name all subsequent JSONs have the literal name: '!dte!!tme!', and do overwrite one another. If you run the Lighthouse command outside of a for loop it executes perfectly, I'm aware that variables don't update within a for loop but I thought !var! could be used to counter this. The for loop iterates through a list of URLs stored in urls.txt, auditing each of them in turn and returning a JSON file.
Current outcome: !dte!!tme!.json
Desired oucome: 08-10-201913-55-12-07.json, 08-10-201913-56-14-00.json etc.
If anybody knows how to amend this to achieve the correct outcome I would be very grateful.
The urls.txt contains a list of URLs, each on a new line:
https://www.bbc.co.uk/weather/2643743
https://www.bbc.com/mundo
https://regex101.com/
https://www.bbc.co.uk/news
Example:
#Echo Off
SETLOCAL ENABLEDELAYEDEXPANSION
for /f "delims=" %%a in (C:\Users\Owner\Desktop\Folder\Lighthouse_Project\urls.txt) DO (
set dte=!DATE:/=-!
set tme=!TIME::=-!
set tme=!tme:.=-!
ECHO Line is: %%a
echo lighthouse "%%a" --quiet --chrome-flags="--headless" --output=json --output-path="C:\Users\Owner\Desktop\Folder\Lighthouse_Project\json_logs\!dte!!tme!.json"
Timeout 2 >NUL
)
Timeout -1 >NUL
This uses the exact content from your urls.txt example. I have added #Echo Off at the top to reduce screen clutter, and an echo in front of your lighthouse command so that it displays instead of runs. Additionally I included a couple of timeouts to both simulate a short timespan, (as the command is being echo'ed not ran), and to give you an opportunity to read the output at the end.
Here's the output from the above to prove my case:
Line is: https://www.bbc.co.uk/weather/2643743
lighthouse "https://www.bbc.co.uk/weather/2643743" --quiet --chrome-flags="--hea
dless" --output=json --output-path="C:\Users\Owner\Desktop\Folder\Lighthouse_Pro
ject\json_logs\08-10-201916-12-42-93.json"
Line is: https://www.bbc.com/mundo
lighthouse "https://www.bbc.com/mundo" --quiet --chrome-flags="--headless" --out
put=json --output-path="C:\Users\Owner\Desktop\Folder\Lighthouse_Project\json_lo
gs\08-10-201916-12-44-14.json"
Line is: https://regex101.com/
lighthouse "https://regex101.com/" --quiet --chrome-flags="--headless" --output=
json --output-path="C:\Users\Owner\Desktop\Folder\Lighthouse_Project\json_logs\0
8-10-201916-12-46-12.json"
Line is: https://www.bbc.co.uk/news
lighthouse "https://www.bbc.co.uk/news" --quiet --chrome-flags="--headless" --ou
tput=json --output-path="C:\Users\Owner\Desktop\Folder\Lighthouse_Project\json_l
ogs\08-10-201916-12-48-21.json"
Press any key to continue . . .
Your issue, as originally stated, was the inclusion of % characters in your initial set syntax, as you can see from the provided output.
By keeping the expansion without exiting the loop as you intended.. Note I have added a timeout of millisecond here to ensure we have a different time each time. ping 127.0.0.1 -n 1 -w 500> nul:
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in (%userprofile%\Desktop\Croud_DS_1\Lighthouse_Project\urls.txt) DO (
set dte=!date:/=-!
set tme=!time::=-!
set tme=!tme:.=-!
set tme=!tme:,=-!
echo Line is: %%a
lighthouse "%%a" --quiet --chrome-flags=" --headless" --output=json --output-path=%userprofile%\Desktop\Folder\Lighthouse_Project\json_logs\!dte!_!tme!.json
ping 127.0.0.1 -n 1 -w 500> nul
)
Please take note, I took the liberty of adding an underscore to make sure the date and time format is readable, so you'll have YYYY-MM-dd_HH-mm-ss-ms

cmd script for ping to find average, loss and IP

I look through stackoverflow, found several similar topics bot no one from them wasn't about cmd
The question is:
I have a website, for example route4me.com and I need to ping it and save into file only: IP, %loss and average time
Can anybody explain how to use tockens and delims since I'm total noob.
Found an article, that explains how to:
Let's have a closer look at the output of the PING command:
we want the (unknown) second word from the first line (actually, the
second line, because the first line is blank) that first line contains
the (known) IP address enclosed in square brackets [10.100.0.14] none
of the other lines contain the IP address enclosed in square brackets,
nor any other string in square brackets First let's mark (yellow
highlights) the boundaries of the requested word REMOTE_PC:
Pinging REMOTE_PC [10.100.0.14] with 32 bytes of data:
Reply from 10.100.0.14: bytes=32 time<10ms TTL=128
Ping statistics for 10.100.0.14: Packets: Sent = 4, Received = 4,
Lost = 0 (0% loss), Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms This makes our choice
for the delimiters, delims, quite obvious: a space. If we mark (yellow
highlights) all spaces, we can easily see which tokens are available:
Pinging REMOTE_PC [10.100.0.14] with 32 bytes of data:
token=1 token=2 token=3 4 5 6 7 8 In this case,
we're only interested in tokens 2 and 3:
token 2 is the requested computer name token 3 can be used to check if
we're dealing with the correct line: it should equal our original IP
address enclosed in square brackets So we're only interested in the
tokens 2 and 3:
Pinging REMOTE_PC [10.100.0.14] with 32 bytes of data:
token=2 token=3 This leads us to the following command line:
FOR /F "tokens=2,3 delims= " %%A IN ('PING -a %1') DO IF "%%B"=="[%1]"
SET PC=%%A %1 is the value of the first command line argument passed
to our batch file. In our case, the IP address to be investigated.
IF "%%B"=="[%1]" checks if the third word (token=3) equals the
original IP address (%1) enclosed in square brackets ([%1]). If we
were to skip this test, the end result for token 2 would be the equal
sign (=) from the last line (just try it). If the test matches, the
second word (token=2) is stored in a variable named PC.
Note that the first token specified (token 2) is stored in the
variable specified (%%A), and the following token specified (token 3)
in the following variable (in this case: %%B).
Our batch file thus far:
#ECHO OFF FOR /F "tokens=2,3 delims= " %%A IN ('PING -a %1') DO IF
"%%B"=="[%1]" SET PC=%%A SET PC The last line, SET PC, displays the
actual value of the variable PC. I added it for debugging purposes.
(Actually, SET PC will display all variables whose names begin with
"PC".)
But for me this is all totally new.
Let's break this down.
Basically, tokens are parts of a string and delims(delimiter) are the text to separate the tokens. Consider this example:
set str=This.String.Is.Separated.By.Fullstops
for /f "tokens=1,2 delims=." %%a in ("%str%") do echo %%a %%b
The for loops will separate the text by ., and now the text are formatted like so:
Tokens: 1 2 3 4 5 6
Text : This String Is Separated By Fullstops
and we only need the first two tokens, which is This and String. echo %%a will return This, and echo %%b will return the String. But echo %%c will not return Is, as the for loop only process the first two tokens as stated in "tokens=1,2.
Back to the for loop.
FOR /F "tokens=2,3 delims= " %%A IN ('PING -a %1') DO IF "%%B"=="[%1]" SET PC=%%A
This time, the for loops only get the result of the command PING -a %1. The result will be:
Tokens: 1 2 3 4 5 6 7 8
Text : Pinging REMOTE_PC [10.100.0.14] with 32 bytes of data:
and we only take the 2nd token. Remember, even though token 2 does not seems to be %%A, but since in the tokens settings, we've set the first outputted variable to be the text with the 2nd token.
The IF statement checks if the 2nd outputted variable with token 3, which is:
[10.100.0.14]
and this equal to the first command-line argument. If so, it set PC to the variable with token 1, which is REMOTE_PC, your PC name.
To get the information you need, we would introduce another setting called skip.Skip defines how many lines to skip, and read the line after it.
for /f "tokens=11 skip=8 delims= " %%a in ('ping -a google.com') do echo %%a
This should get you the loss percentage(maybe not because I don't use the Russian language on my PC.) Feel free to adjust it and make it work for you.
#echo off
setlocal
call :get route4me.com
call :get microsoft.com
call :get google.com
goto :eof
:get
REM set a default value for 'avrg':
set "avrg= ---"
REM do the ping one time...:
>temp ping %1
REM ... and process the output several times to collect the data:
for /f "tokens=2 delims=[]" %%a in (temp) do set "ip=%%a"
for /f "tokens=1 delims=( " %%a in ('find "(" temp') do set "loss=%%a"
for /f "tokens=4 delims==" %%a in ('find "ms, " temp') do set "avrg=%%a"
echo %1,%ip%,%loss%,%avrg:~1%
The first for loop gets the IP.
There will be only one line containing [ and ], so we can safely process the whole line without another line overwriting our result (no other line will have a token2).
The second for loop gets the loss percentage.
It's the only line containing ( and ), but this time, we have to use token 1. so the next line would destroy our token. So we use find to filter only this line (every line will have a first token).
The third for loop gets the average value.
Again, we need a filter the line (I'm using ms,, hoping it's international; the other two should work in every language)
If there is no such line, the tokens keeps empty, so the set will not execute and the default value is kept.
(trying to keep it language independend is also the reason for the delimiter = only in the last forloop and removing the space afterwards with %avrg:~1%)
Working with a temporary file doesn't only make it faster (one ping instead of three), but also ensures, the data is consistent (get all values from the same ping command).
I prefer filtering over skipping, because you can never be sure, if the lines to skip are the same in every case (responsive address vs. unresponsive address)

Batchfile: cmd.exe closes if variable has semicolon?

Here is a part of my batch...
for /f "delims=" %%i in ('C:\pathto\dig.exe www.example.com #8.8.8.8 +short') do set RIP=%%i
If %RIP%==93.184.216.119 (
ECHO - PASS >> results-file.txt
) else (
ECHO - FAIL >> results-file.txt
)
...this works fine - except, let's say the DNS server 8.8.8.8 is unavailable or broken (so for testing replace it with a bad IP) and then, normally from the command-line dig.exe will timeout and eventually return...
;; connection timed out; no servers could be reached
...followed by a return to the command prompt. So I would expect that string to get set in %RIP% and thus FAIL would be printed to the file, but what happens instead is dig.exe runs, timeout, and then cmd.exe just closes, and nothing gets written to the file, and the rest of the batch does not continue.
Any idea where I've gone wrong?
There is at least one reason why the RIP variable is not set - the default FOR /F EOL value is semicolon, meaning all lines that begin with ; are ignored. You need to set EOL to something you know the line cannot begin with, or disable it entirely (tricky).
It is also possible that the error message is sent to stderr instead of stdout, and FOR /F only captures stdout. If this is happening, then you must also redirect stderr to stdout using 2>&1. The special characters must be escaped (or quoted) when in the FOR /F IN() clause.
There is an awkward syntax to disable both DELIMS and EOL that can solve your problem:
for /f delims^=^ eol^= %%i in ('C:\pathto\dig.exe www.example.com #8.8.8.8 +short 2^>^&1') do set RIP=%%i
The reason your IF fails if RIP is not set is the left side is completely empty, causing the parser to fail. You can fix this by adding quotes (or any character) to both sides. But you should also explicitly clear the RIP value before the loop, just in case a prior command previously set RIP.
set "RIP="
for /f "delims=" %%i in ('C:\pathto\dig.exe www.example.com #8.8.8.8 +short') do set RIP=%%i
If "%RIP%"=="93.184.216.119" (
ECHO - PASS >> results-file.txt
) else (
ECHO - FAIL >> results-file.txt
)

CMD return specific lines from piped input

Bored in class trying to figure this out.
On windows command prompt:
ipconfig /all return all the loopback, tunnel, etc.
if I run ipconfig /all | find /n "Internal" it will return [11]Ethernet Adapter Internal. What I want to do is substring the 11 off the beginning and then pipe this to something else which will allow me to return lines 11-19 or whatever. Can you do this in a single line similar to jquery and chaining?
It is simple to parse out the 11 using a FOR /F loop. But making use of that value is difficult in a one liner from the command line.
Here is the best I could come up with for a one liner to run from the command line.
set "stop="&for /f "delims=[]" %N in ('ipconfig /all^|find /n "Internal"') do #for /f "tokens=1* delims=:" %A in ('ipconfig /all^|findstr /n "^"') do #if %A geq %N if not defined stop echo(%B&>nul 2>nul set /a 1/(%A-%N-8)||set stop=1
The shortest one liner I could think of uses a different strategy.
set n=&for /f "delims=" %A in ('ipconfig/all') do #if defined n (set/a"1/n,n-=1">nul 2>&1&&echo %A) else echo %A|find "Internal"&&set n=8
But the above solution ignores blank lines.
If you want to preserve blank lines then the following works:
set n=&for /f "tokens=1* delims=:" %A in ('ipconfig/all^|findstr/n "^"') do #if defined n (set/a"1/n,n-=1">nul 2>&1&&echo(%B) else echo %B|find "Internal"&&set n=8
EDIT
I shortened and simplified the logic of the 2nd and 3rd solutions a bit.
I'll try to explain the 2nd option:
I first explicitly undefine a counter and then use FOR to read the output of the IPCONFIG/ALL commmand. Now begins the fun part within the DO clause.
At first the counter is undefined so the ELSE clause is executed. I pipe the current line to FIND looking for "Internal". If the string is not found then nothing happens and we progress to the next line. But if it is found then the line is printed. Then the code after && is executed since the string was found. There I set the counter to 8 because we want the next 8 lines printed.
The counter is defined for the remainder of the lines, so we now switch to the first part of the IF statement. I use SET /A to do some math. First I divide 1 by the current value of the counter. IF the counter is 0 then an error is raised and the rest of the statement is ignored. If the counter is not 0 then I next decrement the counter by 1. I redirect both stdout and stderr to nul because we don't want to see any messages from the SET /A command. If the SET command was successful, then I echo the line. Since I initially set the counter to 8 when the string was found, up to 8 lines will be printed before the counter reaches 0.

Batch Print - Batch Script - findstr condition in for loop

My problem at hand is to download pdf files and send all of them to the printer.
I download via ftp correctly and all the files go into my local directory:
File Name = FtpScript.ftp
open ftp.domain.com
username
password
!:--- FTP commands below here ---
lcd local/Directory
cd /remote/Directory
binary
mget "*.pdf"
prompt
disconnect
quit
This batch file then calls the ftp script.
File Name = retrieve_print.bat
#ftp -i -s:"C:\Scripts\FtpScript.ftp"
set mm=%date:~4,2%
set dd=%date:~7,2%
set yy=%date:~-4%
IF NOT EXIST {C:\Users\print_test2\print_%mm%_%yy%}( mkdir C:\Users\print_test2\print_%mm%_%yy% )
IF NOT EXIST C:\Users\print_test2\print_%mm%_%yy%\uploaded_%mm%_%dd%_%yy%.txt (
#echo Uploaded Text -- Date: %date% Time : %time% >> C:\Users\print_test2\print_%mm%_%yy%\uploaded_%mm%_%dd%_%yy%.txt
)
IF NOT EXIST C:\Users\print_test2\print_%mm%_%yy%\printed_%mm%_%dd%_%yy%.txt (
#echo Printed Text -- Date: %date% Time : %time% >> C:\Users\print_test2\print_%mm%_%yy%\printed_%mm%_%dd%_%yy%.txt
)
REM LOOP THROUGH PDFs THAT WERE UPLOADED AND INSERT THE NAMES INTO THE UPLOADED_*_*.txt TEXT FILE
FOR %%x in ( C:\Users\print_test2\print_%mm%_%yy%\*.pdf ) DO (
findstr "%%~nxx" C:\Users\print_test2\print_%mm%_%yy%\uploaded_%mm%_%dd%_%yy%.txt
#ECHO Error level = %errorlevel%
#ECHO %%~nxx
#pause
IF NOT %errorlevel% == 0 (
#echo %%~nxx >> C:\Users\print_test2\print_%mm%_%yy%\uploaded_%mm%_%dd%_%yy%.txt
)
)
REM LOOP THROUGH PDFs THAT WERE UPLOADED AND PRINT THEM, THEN INSERT THEM INTO THE PRINTED_*_*.txt TEXT FILE TO SUPPRESS DUPLICATE PRINTS
FOR %%x in ( C:\Users\print_test2\print_%mm%_%yy%\*.pdf ) DO (
findstr "%%~nxx" C:\Users\print_test2\print_%mm%_%yy%\printed_%mm%_%dd%_%yy%.txt
#ECHO Error level = %errorlevel%
#ECHO %%~nxx
IF NOT %errorlevel% == 0 (
rem PRINT FUNCTION
#echo %%~nxx >> C:\Users\print_test2\print_%mm%_%yy%\printed_%mm%_%dd%_%yy%.txt
)
)
The text files generate incorrectly. My thought is that I could loop through the files available in the print_test2/print_%mm%_%yy% directory for all the files that I received via ftp and log it into a text file.
The problem becomes apparent when I try to run the script a second time when the text files have file names in them. I expect the findstr function to give back an %errorlevel% of 0 , but it does not detect the string that is in the text file and appends all the file names again in both my uploaded and printed text files.
Is there a better way of logging the files received and printing the pdfs only once?
Thanks
Your problem is that the %errorlevel% value is taken inside a for, so it is replaced by the value errorlevel had before enter the for loop. To take the current value that errorlevel have in each for iteration you must use Delayed Variable Expansion, that is, enclose the value in exclamation points instead percents: !errorlevel! AND insert this line at begining of your program:
setlocal EnableDelayedExpansion
To make this problem clearer, try this:
set name=Value before FOR
for %%f in (*.pdf) do (
set name=%%f
echo %name%
)
and then try again changing echo %name% by echo !name!.
A few ideas to consider:
I'm not sure that the errorlevel after your FINDSTR command is going to be non-zero just because the string wasn't found.
Even if errorlevel is non-zero, I think that the moment you execute the next command, the new errorlevel from that command gets set.
In your IF statement, you might need to wrap the two sides of your equality check in delimiters, e.g. IF NOT "%errorlevel%" == "0"
You might consider just making your list distinct after you echo all file names into it. It'll save you some logic on the way in. Some code for making a list distinct in DOS is described here: http://www.dullsharpness.com/2010/10/01/create-a-distinct-ordered-list-from-an-unordered-duplicate-list-using-ms-dos/
If you use techniques from #4, you can then just do a directory listing into your file (as follows) and then make unique using techniques in #4.
dir/b C:\Users\print_test2\print_%mm%_%yy%\*.pdf >> C:\Users\print_test2\print_%mm%_%yy%\printed_%mm%_%dd%_%yy%.txt

Resources