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
Related
I have a batch script which part of it is clearing the Event logs of Windows 10.
I would like to see the output of the following wevtutil.exe command, but output just on a single line (overwriting each line) instead of many multiple lines.
I know about the ANSI Escape Sequences ESC[1FESC[0J and ESC[1A which can overwrite a previous line (The ESC char is ALT+027 and can be seen in Notepad++), but I haven't been able to figure out how to do that with the wevtutil.exe el command. It still outputs each line one after the other.
Here's what I tried (running in CMD with admin rights):
#echo off
SET OverwriteLine=ESC[1FESC[0J
echo Making sure the variable OverwriteLine actually works. This is line 1.
echo %OverwriteLine%If you see this line instead of line 1, OverwriteLine works.
echo Great, now let^'s see if it works for the "wevtutil.exe cl" command
pause
echo Clearing Event logs...
#echo on
for /F "tokens=*" %%E in ('wevtutil.exe el') DO echo %OverwriteLine% | wevtutil.exe cl "%%E"
pause
I know this can be done via Powershell's $host.ui.RawUI.CursorPosition, but I need a solution for CMD/BAT.
As we deal with a single specific issue per question, and your main one appears to be with the implementation of the VT100 sequences, here is a commented example using a completely different for loop just for demonstration of the technique.
#Echo Off
SetLocal EnableExtensions
Rem Create a variable to use as the escape character
For /F %%G In ('Echo Prompt $E ^| %SystemRoot%\System32\cmd.exe') Do Set "\x1b=%%G"
Rem This is a static line included to demonstrate that it is unaffected
Echo(Output
Rem Turns off the cursor and removes a stray new line caused by Echo
Echo(%\x1b%[?25l%\x1b%[1A
Rem For loop example
For /L %%G In (1,1,8) Do (
Rem Clears to the beginning of the line, prints the output, and moves up a line
Echo(%\x1b%[1KLine %%G%\x1b%[1A
Rem Creates a short delay between output lines to see the effect
%SystemRoot%\System32\PATHPING.EXE 127.0.0.1 -n -q 1 -p 650 1>NUL
)
Rem Turns on the cursor again
Echo(%\x1b%[?25h
Pause
OK, found the solution:
for /F "tokens=*" %%E in ('wevtutil.exe el') DO (wevtutil.exe cl "%%E" | echo <ESC>[1F<ESC>[0KClearing %%E)
Explanation/Notes:
< ESC> means the special ESC escape code sequence. You can generate this char by typing ALT+027 in Notepad++ if you're editing your code in there, or generate it at runtime using the FOR loop that Compo mentioned.
We move the cursor to the beginning of the previous line with ESC[1F.
We then clear from the cursor to the end of the line with ESC[0K.
Clearing the Windows event logs requires running the CMD script with Administrator rights.
Expect some event logs to fail. The failed ones will remain on the screen, each on a new line (which might become handy). If you don't want to see any failures, just add 2>nul : DO (wevtutil.exe cl "%%E" 2>nul | echo <ESC>[1F<ESC>[0KClearing %%E)
You can learn more about escape codes here.
I'm new to Windows cmd and .bat, and to Tesseract. But thanks to this list I've managed a couple of successes.
My first success was this cmd-window line:
tesseract.exe -l eng+lat+ita D:\TIFs\Convivio.tiff D:\TIFs\Convivio
My next success was the .bat file:
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set _SourcePath=D:\temp\TIFs\*.tif
Set _OutputPath=D:\temp\TIFs\
Set _Tesseract="D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in (%_SourcePath%) Do Echo Converting "%%A"...... &"D:\temp\Tesseract-OCR\tesseract.exe" "%%A" "%_OutputPath%%%~nA"
PAUSE
:End
Set "_SourcePath="
Set "_OutputPath="
Set "_Tesseract="
The problem now is how to include in the .bat file that"-l eng+lat+ita" bit from the cmd-window line.
I got the idea that this is possible from an explanation of the "For" command, which states that "do command" can be followed by "CommandLineOptions" (i.e., "-l eng+lat+ita").
Any help would be much appreciated... 'cause I've been banging my head on this one for hours now...
UPDATE: Found an alternative, but still would like an answer to my question.
I didn't know that "FOR" commands could be run from cmd. So, I pasted the folllowing line in the cmd window:
for %i in (*.tif) do "D:\temp\Tesseract-OCR\tesseract.exe" -l eng+lat+ita "%i" "D:\temp\%~ni"
And, it worked!
As I say, though, how to do this with a .bat file?
#ECHO OFF
SETLOCAL
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set "_SourcePath=D:\temp\TIFs\*.tif"
Set "_OutputPath=D:\temp\TIFs"
Set "_Tesseract=D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in ("%_SourcePath%") Do Echo Converting "%%A"...... &"%_Tesseract%" -l eng+lat+ita "%%A" "%_OutputPath%\%%~nA"
PAUSE
:End
rem Set "_SourcePath="
rem Set "_OutputPath="
rem Set "_Tesseract="
GOTO :EOF
Since I don't have the tesseract utility, I used another. The above worked for me as I expected with that other utility, so no guarantees with tesseract.
It's normal practice to start a batch with setlocal which makes the clean-up effort unnecessary (hence remmed-out) since an implicit endlocal is executed when the batch terminates, restoring the environment to its initial state.
Assigning values containing quotes is valid but awkward when combining elements. Ditto terminating a value with a backslash. I've converted your code to my preferred syntax. Note that the syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Will it work in your situation? Over to you to try.
I am new to writing batch scripts and was hoping for some help.
I have a folder of images. I want to cURL each of these images, one by one, to an image classifier I made on the web. This will then send me a result.
I then want to save the response of the server into a file - let's keep it simple a say a CSV, in this format:
File, Response
So I know to run the Batch on each folder, I can do the following -
for /f %%f in ('dir /b c:\') do echo curl -X POST -F "images_file=#%%f" "https://gateway-a.watsonplatform.net/visual-recognition/api/v3/classify?api_key=abcdefgh&version=2016-05-20"*
I know, I can somehow pipe the results into an output file using ">>".
I don't know how to put this together, and how to create the CSV as I am running the batch script. Can anyone help? I think I am 90% there just new to batch scripting.
You can do something like this in powershell :
Change the placeholders as per your requirement.
$dir_files=Get-ChildItem "C:\"
foreach($file in $dir_files)
{
$result=Invoke-WebRequest 'https://gateway-a.watsonplatform.net/visual-recognition/api/v3/classify?api_key=abcdefgh&version=2016-05-20' -Method Post -Headers 'images_file=#%%f'
$result | ConvertTo-Csv | Out-File "D:\outputfile.csv" -Append -Force
}
I just gave you the way to set off. I didn't go through the complete batch code.
Hope it helps you.
If you want to use batch, you can do:
#echo
SetLocal EnableDelayedExpansion
set imagedir=C:\
set csvfile="C:\results.csv"
FOR %%G IN ("%imagedir%*") DO (
set resultfile=%%G.out
echo curl -X POST -F "images_file=#%%G" "https://gateway-a.watsonplatform.net/visual-recognition/api/v3/classify?api_key=abcdefgh&version=2016-05-20"* -o "!resultfile!"
echo %%G, !resultfile! >> %csvfile%
)
EndLocal
exit /b 0
I am not that experienced with curl so I'm not able to verify the correctness of your curl command and let the echo before it. But I do know that you can actually specify a file to write the output to in Windows with the -o flag.
The FOR command (without any flag/option) is another way to loop through files in a directory. If you still want to use the for /f %%f in ('dir /b ...') do-style (perhaps because of the order in which you want to treat your images), you can simply replace the line FOR %%G IN ("%imagedir%*") DO ( with for /f %%f in ('dir /b "%imagedir%"') DO (. I saved the directory in the variable imagedir, it's then easier to change. But if you change it, don't forget the \ at the end (if you use my for-command) and don't add double quotes (I add them in the FOR command). Be aware that this will treat all FILES in the directory. You can specify extensions if you want by using "%imagedir%\*.jpg" "%imagedir%\*.gif" for example for only GIF and JPEG images.
As last I'll say that I added SetLocal EnableDelayedExpansion to use delayed expansion. It's actually only needed because I saved the name of the file where the result of the curl operation will be saved, in the variable resultfile. In batch, a FOR -block (FOR ... ( ... )), and other blocks delimited with ( ) (as IF-blocks), are parsed as one single command (as if they were written on line). It is not really possible to change the value of a variable and read that new value in the same command (or same line) without using some special "tricks". Delayed expansion is one of them. When you use delayed expansion on a variable, you surround it with ! instead of %. As resultfile will change in each iteration, I use !resultfile! instead of %resultfile%.
If you don't want to save the name of the outputfile in a variable, you can remove the use of the variable and the delayed expansion (remove the EndLocal at the end too).
Good luck!
I'm stuck with an idea that I just can't get off the ground and it's because I don't understand FOR loops very well.
Essentially though, what I am trying to do is a FOR loop that will go through its current directory and for every folder that is 7 random numbers long, it'll move another file into that directory and call it. I'll make a diagram here.
%cd%
\1234567\
\2345671\
\3456712\
\Release\
filetomove.bat
So the intent is that the file to move will end up only in the numbered directories.
%cd%
\1234567\filetomove.bat
\2345671\filetomove.bat
\3456712\filetomove.bat
\Release\
filetomove.bat
Then once it is in those directories, it will call the bat in each of them.
%cd%
CALL \1234567\filetomove.bat
CALL \2345671\filetomove.bat
CALL \3456712\filetomove.bat
\Release\
filetomove.bat
I would make it more convoluted with an IF statement to only move a certain .bat if certain files are or aren't present, but I want to be able to get down moving the file first. I think I might be good on the IF statement.
Does anyone know what the heck I'd want to do to make this possible? I thought for file directories I could have done like
FOR /D %%G in ("%cd%\[0-9][0-9][0-9][0-9][0-9][0-9][0-9]\") DO COPY...
but apparently something like this doesn't work the way I intend. The only way I could actually see that's finding each file is to do something like...
FOR /D %%G in ("%cd%\*") DO ECHO %%G
and have the code return to me the directories string. What am I doing wrong, just don't know, or just am not understanding?
I'm having trouble understanding your entire idea, but here's how you can iterate over all the folders that have 7 digit names and then run batch's equivalent of a function to handle the output. Note that to use the %% variable syntax this needs to be run from inside a batch file. The command line uses only a single % in front of the iterator variable.
for /f "tokens=*" %%a in ('dir /b /aD * ^|findstr /R "^[0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"') do call :junk "%%a"
REM stuff here runs after the for loop finishes
REM the goto :EOF here makes sure we aren't going to accidentally run :junk again when we didn't mean to.
goto :EOF
:junk
REM This runs each time 'for' spits out a new directory name.
set yourDirName=%~1
REM do other stuff here with this dir name, like copying or whatever.
REM If for instance you wanted to run a batch file c:\mybatfile.bat in
REM each dir, you could do that here:
pushd "%yourDirName%"
REM Now you're in the %yourDirName% directory and you can run your command:
c:\mybatfile.bat
popd
REM popd is the opposite of pushd.
goto :EOF
Here's an explanation of some of the syntax in the order it's used in the script.
for /f can parse command output. That's why it's always my favorite.
"tokens=*" causes the output from that iteration of the for-loop to all end up in one variable (%%a). Otherwise you would get the first delimited token in %%a, the next in %%b, etc.
Single-quote your command. I do it because it works, not because I know why.
For dir /b removes the columns of info you don't need and just gives the name. /aD gives you the files that have the Directory /attribute
You need to escape the | character with a ^ when it's part of a command you're giving to for
Use findstr /R so you can filter the names based on a regular expression.
call allows you to jump to a block of commands and pass arguments to that block (as %1, %2, etc.) so you can execute a set of commands without having to cram them into a set of parentheses since that gives you weird evaluation behavior.
When you use call, it's like running another script that just happens to be part of your current script. To return from that "other script"'s execution context, you use goto :EOF. It's distinct from exit because it doesn't completely terminate your program; it just does a goto on the meta-label :EOF which is short for End-Of-File. Here we want the script to act as if it has run out of commands (where it will keep executing a higher context if there was one) rather than exiting all the way out to the command line using exit.
The flow here is:
Start at the for loop
For each iteration of the for loop:
call :junk
Execute all of the commands under :junk until we hit the goto :EOF
goto :EOF at the bottom of :junk causes execution control to return to the for loop so it can iterate again.
When the for runs out of data to iterate over, it tries to execute more lines of the script. We don't want it to execute :junk again, so there's a goto :EOF above :junk.
Simple, right?
Assuming Windows, is there a way I can detect from within a batch file if it was launched from an open command prompt or by double-clicking? I'd like to add a pause to the end of the batch process if and only if it was double clicked, so that the window doesn't just disappear along with any useful output it may have produced.
Any clever ways to do this? I'm looking for solutions I could rely on to work on a machine that was configured more or less with default settings.
I just ran a quick test and noticed the following, which may help you:
When run from an open command prompt, the %0 variable does not have double quotes around the path. If the script resides in the current directory, the path isn't even given, just the batch file name.
When run from explorer, the %0 variable is always enclosed in double quotes and includes the full path to the batch file.
This script will not pause if run from the command console, but will if double-clicked in Explorer:
#echo off
setlocal enableextensions
set SCRIPT=%0
set DQUOTE="
#echo do something...
#echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
:EXIT
if defined PAUSE_ON_CLOSE pause
EDIT:
There was also some weird behavior when running from Explorer that I can't explain. Originally, rather than
#echo %SCRIPT:~0,1% | findstr /l %DQUOTE% > NUL
if %ERRORLEVEL% EQU 0 set PAUSE_ON_CLOSE=1
I tried using just an if:
if %SCRIPT:0,1% == ^" set PAUSE_ON_CLOSE=1
This would work when running from an open command prompt, but when run from Explorer it would complain that the if statement wasn't correct.
Yes. Patrick Cuff's final example almost worked, but you need to add one extra escape, '^', to make it work in all cases. This works great for me:
set zero=%0
if [^%zero:~0,1%] == [^"] pause
However, if the name of the batch file contains a space, it'll be double quoted in either case, so this solution won't work.
Don't overlook the solution of having two batch files:
abatfile.bat and abatfile-with-pause.bat
The second simply calling the first and adding a pause
Here's what I use :
rem if double clicked it will pause
for /f "tokens=2" %%# in ("%cmdcmdline%") do if /i "%%#" equ "/c" pause
I use a parameter "automode" when I run my batch files from scripts.
set automode=%7
(Here automode is the seventh parameter given.)
Some code follows and when the file should pause, I do this:
if #%automode%==# pause
One easy way to do it is described here:
http://steve-jansen.github.io/guides/windows-batch-scripting/part-10-advanced-tricks.html
There is little typo in the code mentioned in the link. Here is correct code:
#ECHO OFF
SET interactive=0
ECHO %CMDCMDLINE% | FINDSTR /L /I %COMSPEC% >NUL 2>&1
IF %ERRORLEVEL%==0 SET interactive=1
ECHO do work
IF "%interactive%"==1 PAUSE
EXIT /B 0
Similar to a second batch file you could also pause if a certain parameter is not given (called via clicking).
This would mean only one batch file but having to specify a -nopause parameter or something like that when calling from the console.
crazy idea: use tasklist and parse it's results.
I've wrote in a test batch file:
tasklist > test.out
and when I double-clicked it, there was an additional "cmd.exe" process just before the tasklist process, that wasn't there when the script was run from command line (but note that might not be enough if someone opens a command line shell and then double-click the batch file)
Just add pause regardless of how it was opened? If it was opened from command prompt no harm done apart from a harmless pause. (Not a solution but just thinking whether a pause would be so harmful / annoying )