IF was not expected this time - if-statement

#rem videodir webm -> mp4
#rem audio acc video h264 overwrite optional
set /p videodir="directory>>> "
echo %videodir%
FOR %%i IN (%videodir%) DO %%~di
cd %videodir%
pause
set /p option="turn on overwrite? [y/n]>>> "
echo %option%
pause
IF %option%==y (
FOR /R %videodir% %%i IN (*.webm) DO ( ffmpeg -i "%%i" -y -c:a aac -c:v libx264 "%videodir%\mp4\%%~ni.mp4" )
) IF %option%==n (
FOR /R %videodir% %%i IN (*.webm) DO ( ffmpeg -i "%%i" -n -c:a aac -c:v libx264 "%videodir%\mp4\%%~ni.mp4" )
) ELSE (
echo "invaild value" )
pause
If statement keeps making error. please help me sir.
I tried to convert both D:\school.webm C:\school3.webm file into mp4, but whether i turn on or off overwrite option, it just keeps raising error.

) IF %option%==n (
should be
) else IF %option%==n (
Additionally, better syntax is if /i "%option%"=="y" which provides some protection against an entry like y hello and /i allows a case-insensitive match.
It would also be a good idea to use the search facility to find out about choice and how to use it.

The if statements parenthesized blocks are an overkill, you have a single command so no need to enclose them in parenthesis, in fact no need for else either.
To directly fix you error, simply put IF %option%==n ( on a new line.. that's it.
I'll however encourage you to use choice for these... choice like scripts, that what it is built for:
#echo off
rem videodir webm -> mp4
rem audio acc video h264 overwrite optional
set /p videodir="directory>>> "
pushd "%videodir%">nul 2>&1 || (echo no such dir "%videodir%" & exit /b 1)
choice /M "Overwrite?"
if %errorlevel% equ 1 set "opt=y"
if %errorlevel% equ 2 set "opt=n"
for /R %videodir% %%i in (*.webm) do ffmpeg -i "%%i" -%opt% -c:a aac -c:v libx264 "%videodir%\mp4\%%~ni.mp4"
popd

Related

Batch file: evaluating command output with regex?

I have made a Windows batch file based on some examples I have found elsewhere.
What it does is it parses a folder for a specific file type (.mkv) and then runs mkvmerge.exe from the MKVToolNix folder.
The command produces an output, listing the different tracks in the container.
The core of the file is
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
for /f %%b in ('mkvmerge -i "%%a" ^| find /c /i "chapters"') do (
if [%%b]==[0] (
echo "%%a" has no chapters
) else (
echo Doing some interesting stuff!
)
)
)
The above example is just part of the file, rootfolder is set to the folder I want parsed, of course, and upon finding a file with chapters in it, it will run additional commands.
It all works beautifully but I also want to check for subtitles at the same time. The find command doesn't take regular expressions or I could just have added "chapters subtitles". My efforts using other commands, like findstr, haven't really worked.
How do I go about using RegEx here?
This is an example output, running mkvmerge.exe on an .mkv file
mkvmerge.exe -i "Snow White and the Seven Dwarfs (1937) [tt0029583].mkv"
File 'Snow White and the Seven Dwarfs (1937) [tt0029583].mkv': container: Matroska
Track ID 0: video (MPEG-4p10/AVC/h.264)
Track ID 1: audio (DTS)
Track ID 2: audio (AC-3)
Track ID 3: subtitles (HDMV PGS)
Track ID 4: subtitles (HDMV PGS)
Chapters: 26 entries
This example has both subtitle and chapter tracks and the batch file will find the keyword "chapters" (it's set to ignore case). I also want to catch the files that contain the keyword "subtitles" even when there are no chapters.
To clarify my intent here, I want the code to:
Parse through the given folder
For all .mkv files, do mkvmerge /i which will output (as text) the streams in that file
Look at that output and if it contains the word(s) "chapters" and/or "subtitles" trigger some action.
Since you appear to make no distinction between whether one string or the other (or both) is detected, then
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
mkvmerge -i "%%a" | findstr /i "^Chapters subtitles" >nul
if errorlevel 1 (
echo Neither Chapters nor subs found in "%%a"
) else (
echo Chapters or subs found in "%%a"
)
)
would likely be easier.
Thanks LotPings for your effort. I learned something about tokens that will be very useful. Your script also got me on the right track (learning a few more commands on the way.
Your script ended up looking like this:
:: Q:\Test\2018\05\27\SO_50555308.cmd
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
set "found="
for /f "tokens=1-4* delims=: " %%b in (
'mkvmerge -i "%%a" ^| findstr /i "^Chapters subtitles"'
) do (
if /i "%%b"=="Chapters" set found=1
if /i "%%e"=="subtitles" set found=1
)
if defined found (
echo Chapters or subs found in "%%a"
) else (
echo.
)
)
All I needed was to check whether one of my keywords were present in any of the tokens and then set a flag accordingly and after the loop do the appropriate action, resetting the flag for the next file.
Still unclear how a line with Subtitles would look like.
Presuming the values Chapters and Subtitles appear in 1st column
The for /f splits the lines at colon and space (adjacent delims count as one) into tokens 1=%%b and 2=%%c the possible unsplitted rest in token 3=%%d
The space separated (ORed) RegEx search words anchored ^ at line begin will only match chapters/subtitles.
:: Q:\Test\2018\05\27\SO_50555308.cmd
#Echo off
set rootfolder="Z:\Movies"
for /r %rootfolder% %%a in (*.mkv) do (
for /f "tokens=1-4* delims=: " %%b in (
'mkvmerge -i "%%a" ^| findstr /i "^Chapters subtitles"'
) do (
if /i "%%b"=="Chapters" if "%%c"=="0" (
echo "%%a" has no chapters
) else (
echo "%%a" has %%c chapters
echo Doing some interesting stuff!
)
if /i "%%e"=="subtitles" echo "%%a" %%b %%b %%d: %%e %%f
)
)

Alternative to grep for isolating specific text in Windows command line

My network changes rather dynamically and unfortunately I am forced to run Windows.
This script works because I have installed GNU tools for Windows and can use grep.
I would like to be able to run this script on any windows machine without having to install anything (grep etc). I used findstr in this at first but could not get it to show only what matches the regex string.
#echo off
set %IPLIST% = nul
Echo.
Echo "Pinging all local Gateways"
ipconfig | findstr /i "Gateway" | grep -o "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*" > %IPLIST%
For /F "Usebackq Delims=" %%# in ("%IPLIST%")
do (
Echo+
Echo [+] Pinging: %%#
Ping -n 1 "%%#" 1>nul && (
Echo [OK]) || (
Echo [FAILED])
)
Echo.
Echo.
Echo "now testing your internet connection"
Echo .
Echo ...
if %errorlevel% == 0 (
echo ....you're all good bro.
) else (
ping -n 3 "8.8.8.8" | findstr /r /c:"[0-9] *ms"
if %errorlevel% == 1 (
echo ....all is lost
)
)
Echo.
You have a number of problems with the script.
set %IPLIST% = nul
This would yield a syntax error because iplist is likely undefined, and you are attempting to set the variable contents of iplist to "nul"
The resolution of this would be
set = nul
Further, spaces on either side of the = are included in the variable/value set, so removing the %s would set the value of iplistSpace to Spacenul
Had iplist indeed been set up to nul then your ipconfig command would have simply dumped its eventual output into the bit-bucket as it would have been sent to nul.
For /F "Usebackq Delims=" %%# in ("%IPLIST%")
The do ( must be on the same physical line as the "for"
<opinion>Using non-alphas as metavariables is not a good idea as it is not officially supported <\opinion>
if %errorlevel% == 0 (
echo ....you're all good bro.
) else (
ping -n 3 "8.8.8.8" | findstr /r /c:"[0-9] *ms"
if %errorlevel% == 1 (
echo ....all is lost
)
)
Because of the delayed expansion trap (all %var% in a block (parenthesised sequence of statements) are replaced by their value at parse-time) the innermost %errorlevel% here would be replaced by whatever errorlevel was set to when the outermost if was encountered. To interpret the ping output, you'd need to either invoke delayedexpansion (many, many SO items on this subject) or use the old original if errorlevel n... construct, where the if condition is true if the current errorlevel is n or greater than n.
So - reconstructing using batch features,
#ECHO OFF
SETLOCAL
Echo.
Echo "Pinging all local Gateways"
For /F "tokens=2 Delims=:" %%a in ('ipconfig ^| findstr /i "Gateway"') DO IF "%%a" neq " " (
Echo+
Echo [+] Pinging: %%a
Ping -n 1 %%a 1>nul && (
Echo [OK]) || (
Echo [FAILED])
)
Echo.
Echo.
Echo "now testing your internet connection"
Echo .
Echo ...
if %errorlevel% == 0 (
echo ....you're all good bro.
) else (
ping -n 3 "8.8.8.8" | findstr /r /c:"[0-9] *ms"
if ERRORLEVEL 1 IF NOT ERRORLEVEL 2 (
echo ....all is lost
)
)
Echo.
GOTO :EOF
The 'for /ftokenises the output of the command in single-quotes using:as a delimiter and picking the second token to apply to%%a`.
The | needs to be escaped by a caret to tell cmd that it is part of the command to be executed, not part of the for.
The for processes the output of ipconfig... so %%a has the form Spacexxx.xxx.xxx.xxx
On my machine, xxx... was missing on one line, so I filtered out the single-space response.
Pinging %%a without the quotes adds an extra space into the ping line, which is harmless. With the quotes, it didn't like it.
Then the appendix...
I'm not sure what you're really attempting here. Perhaps it was a frustrated debug attempt.
errorlevel will be set to whatever the last ping result was. This is logically unpredictable.
I've shown a correct method of interpreting the findstr errorlevel result. I'm not sure that your regex is correct here...but the all is lost message would only be shown if findstr sets errorlevel to 1.

Windows batch script to search for specific files to delete

I need help converting the following Linux/Mac commands into a Windows batch script.
find / -regex ``^.*/Excel_[a-z]*.xls'' -delete 2>/dev/null
find / -regex ``^.*/presentation[0-9]*[a-z]*.ppt'' -delete 2>/dev/null
Basically using regular expressions, I need to find any of the .xls/.ppt files (in the format above) in a given Windows box and delete them.
Sorry, I'm new to Windows batch files.
You really don't explain what your hieroglypics mean.
In a batch file,
#echo off
setlocal
dir /s "c:\local root\Excel_*.xls"
would show all of the files matching starting Excel_ with extension .xls
(and if that's what you want, simply replacing dir with del would delete them; adding >nul would suppress messages; adding 2>nul suppresses error messages)
If you want files starting Excel_ then followed by any alphas-only, then
for /f "delims=" %%a in ('dir /b /s /a-d Excel_*.xls ^| findstr /E /R "\\Excel_[a-z]*\.xls" ') do echo "%%a"
The dir produces a directory list in /b (basic) form (ie. filename-only) /s - with subdirectories (which attaches the full directory name) and the /a-d suppresses directorynames. This is piped to findstr to filter out the required filenames. The result is assigned to %%a line-by-line, and the delims= means "don't tokenise the data"
should show you all the files matching that criterion - but it would be case-sensitive; add /i to the findstr switches to make it case-insensitive. (/r means "regex" within findstr's restricted implementation; /e means "ends" - I tend to use these over $) The \\ in intended to implement escaped \ to ensure the filename match is complete (ie do't find "some_prefixExcel_whatever.xls) - I'll leave what \. is intended to do to your imagination...
(again, change echo to del to delete and add in the redirection palaver if required)
And the other regex - well, follow the bouncing ball. It would appear you want .ppt files with names starting presentation followed by a possible series of numerics then by a series of alphabetics. I'd suggest
findstr /e /r "\\presentation[0-9]*[a-z]*\.ppt" for that task.
Use PowerShell.
get-childitem | where-object { $_.Name -match '<put a regex here>' } | remove-item
get-childitem returns file system objects, and the where-object filter selects only those file system objects whose name property matches a regular expression. These filtered items are then passed through the pipeline to remove-item.
There is good information about the PowerShell pipeline in the about_pipelines help topic, which you can read using the following command:
help about_pipelines
Plain Batch can't do this task. But you could make use of tools like findstr which come with Windows and support Regex.
This line can be executed from CMD and deletes all files in the current folder which match the RegEx:
for /f "eol=: delims=" %F in ('findstr /r "MY_REGEX_HERE"') do del "%F"
So try to get your expected results by playing around with this command. If your fine with the output/results, you can embed this line in your batch script. (Be careful, when embedding this line in batchscript you have to double the percentage signs!)
for /f "eol=: delims=" %%F in ('findstr /r "MY_REGEX_HERE"') do del "%%F"
Here a small batch to do what you are looking for:
#echo off
set /p dirpath=Where are your files ?
:: set dirpath=%~dp0 :: if you want to use the directory where the batch file is
set /p pattern=Which pattern do you wanna search (use regex: *.xml e.g.) :
:: combinason /s /b for fullpath+filename, /b for filename
for /f %%A in ('dir /s /b "%dirpath%\%pattern%" ^| find /v /c ""') do set cnt=%%A
echo File count = %cnt%
call :MsgBox "Do you want to delete all %pattern% in %dirpath%? %cnt% files found" "VBYesNo+VBQuestion" "Click yes to delete the %pattern%"
if errorlevel 7 (
echo NO - quit the batch file
) else if errorlevel 6 (
echo YES - delete the files
:: set you regex, %%i in batch file, % in cmd
for /f "delims=" %%a in ('dir /s /b "%dirpath%\%pattern%"') do del "%%a"
)
:: avoid little window to popup
exit /b
:: VBS code for the yesNo popup
:MsgBox prompt type title
setlocal enableextensions
set "tempFile=%temp%\%~nx0.%random%%random%%random%vbs.tmp"
>"%tempFile%" echo(WScript.Quit msgBox("%~1",%~2,"%~3") & cscript //nologo //e:vbscript "%tempFile%"
set "exitCode=%errorlevel%" & del "%tempFile%" >nul 2>nul
endlocal & exit /b %exitCode%

Batch .BAT script to rename files

Im looking for a batch script to (recursively) rename a folder of files..
Example of the rename:
34354563.randomname_newname.png to newname.png
I already dug up the RegEx for matching from the beginning of the string to the first underscore (its ^(.*?)_ ), but cant convince Windows Batch to let me copy or rename using RegEx.
From command-line prompt - without regex:
FOR /R %f IN (*.*) DO FOR /F "DELIMS=_ TOKENS=1,*" %m IN ("%~nxf") DO #IF NOT "%n" == "" REN "%f" "%n"
In batch file, double %:
FOR /R %%f IN (*.*) DO FOR /F "DELIMS=_ TOKENS=1,*" %%m IN ("%%~nxf") DO #IF NOT "%%n" == "" REN "%%f" "%%n"
EDIT: A new pure batch solution issuing following cases:
Path\File_name.ext => name.ext
Path\none.ext (does nothing)
Path\Some_file_name.ext => file_name.ext
Path\name.some_ext (does nothing)
Path\Some_file_name.some_ext => name.some_ext
Batch (remove ECHO to make it functional):
FOR /R %%f IN (*.*) DO CALL :UseLast "%%~f" "%%~nf"
GOTO :EOF
:UseLast
FOR /F "DELIMS=_ TOKENS=1,*" %%m IN (%2) DO IF "%%n"=="" (
IF NOT "%~2"=="%~n1" ECHO REN %1 "%~2%~x1"
) ELSE CALL :UseLast %1 "%%n"
GOTO :EOF
avoid _ in the extension:
#ECHO OFF
FOR /F "DELIMS=" %%A IN ('DIR /S /B /A-D *_*.*') DO FOR /F "TOKENS=1*DELIMS=_" %%B IN ("%%~NA") DO IF "%%~C" NEQ "" ECHO REN "%%~A" "%%~C%%~XA"
Look at the output and remove ECHO if it looks good.
The standard windows shell doesn't have regex capabilities, and in fact it's extremely basic. But you can use powershell to do this.
I'm not very familiar with power shell, but this other question explains how to filter files: Using powershell to find files that match two seperate regular expressions
New Solution
There is an extremely simple solution using JREN.BAT, my new hybrid JScript/batch command line utility for renaming files and folders via regular expression search and replace.
Only rename files with one underscore:
jren "^[^_]+_([^_]+\.png)$" "$1" /s /i
Preserve everything after the first underscore:
jren "^.*?_" "" /s /fm "*.png"
Preserve everything after the last underscore:
jren "^.*_" "" /s /fm "*.png"
=========================================================
Original Answer
This can be done using a hybrid JScript/batch utility called REPL.BAT that performs regex search and replace on stdin and writes the result to stdout. All the solutions below assume REPL.BAT is somewhere within your PATH.
I intentionally put ECHO ON so that you get a log of the executed rename commands. This is especially important if you get a name collision: two different starting names could both collapse to the same new name. Only the first rename will succeed - the second will fail with an error message.
I have three solutions that differ only in how they handle names that contain more than 1 _ character.
This solution will only rename files that have exactly one _ in the name (disregarding extension). A name like a_b_c_d.txt would be ignored.
#echo on&#for /f "tokens=1,2 delims=*" %%A in (
'2^>nul dir /b /s /a-d *_* ^| repl ".*\\[^_\\]*_([^_\\]*\.[^.\\]*)$" "$&*$1" a'
) do ren "%%A" "%%B"
The next solution will preserve the name after the last _. A name like a_b_c_d.txt would become d.txt
#echo on&#for /f "tokens=1,2 delims=*" %%A in (
'2^>nul dir /b /s /a-d *_* ^| repl ".*_([^\\_.]*\.[^.\\]*)$" "$&*$1" a'
) do echo ren "%%A" "%%B"
This last solution will preserve the name after the first _. A name like a_b_c_d.txt would become b_c_d.txt. This should give the same result as the Endoro answer.
#echo on&#for /f "tokens=1,2 delims=*" %%A in (
'2^>nul dir /b /s /a-d *_* ^| repl ".*\\[^\\]*?_([^\\]*\.[^.\\]*)$" "$&*$1" a'
) do echo ren "%%A" "%%B"

bat: Listing empty directories, if/else statements within for statements?

I'm trying to write a script that lists every file name in a specified folder, and notifies the user if that folder is empty. So far I've got:
for /r "O:\Mail\5-Friday" %%d in (*.pdf) do (
dir /a /b "%%~fd" 2>nul | findstr "^" >nul && echo %%~nd || echo Empty: Friday
)
but I've no idea where to put the if, else operators.
And is there a way to specify a folder based on user input without rewriting every function for each folder? So instead of:
if /i {%ANS%}=={thursday} (goto :thursday)
if /i {%ANS%}=={friday} (goto :friday)
:thursday
<do stuff>
:friday
<do the same stuff as thursday, but a different directory>
etc, I could write one function with variables in place of paths, assign the directory to a variable, and easily add/remove folders in the code as necessary?
To address the first part of your question, "where to put the if, else operators"... The notation of
command | findstr >nul && echo success || echo fail
... is shorthand for
command | findstr >nul
if ERRORLEVEL 1 (
echo fail
) else (
echo success
)
The magic that happens is in the conditional execution operators, the && and ||. If findstr exits with status zero, then a match was found. Therefore, execute the stuff after &&. Otherwise, status is non-zero, no match was found, so execute the stuff after ||. See how that works?
For the second part, here's a typical way to prompt the user to provide entry based on a finite number of choices.
#echo off
setlocal
:begin
set /p "day=What day? "
for %%I in (monday tuesday wednesday thursday friday) do (
if /i "%day%" equ "%%I" goto %%I
)
goto begin
:monday
call :getdirs "O:\Mail\1-Monday"
goto :EOF
:tuesday
call :getdirs "O:\Mail\2-Tuesday"
goto :EOF
:wednesday
call :getdirs "O:\Mail\3-Wednesday"
goto :EOF
:thursday
call :getdirs "O:\Mail\4-Thursday"
goto :EOF
:friday
call :getdirs "O:\Mail\5-Friday"
goto :EOF
:getdirs <path>
setlocal enabledelayedexpansion
for /f "delims=" %%I in ('dir /b /s /ad "%~1"') do (
dir /b "%%I" 2>NUL | findstr "^" >NUL || echo %%I has no files
)
goto :EOF
Or, even hacksier, I'll do something you probably weren't expecting was possible. I'll have the script open a folder selection dialog to allow the user to select the directory to scan. It's a batch / JScript hybrid script.
If you wish, you can set the root of the folder browser to a ShellSpecialConstants folder by changing the last argument in the next-to-the-last line. Using a value of 0x11 makes the root your system's drives. No value or a value of 0x00 makes the root "Desktop". Or leave the script as-is to set the root as "O:\Mail".
#if (#a==#b) #end /*
:: fchooser2.bat
:: batch portion
#echo off
setlocal
set initialDir="O:\Mail"
for /f "delims=" %%I in ('cscript /nologo /e:jscript "%~f0" "%initialDir%"') do (
call :getdirs "%%I"
)
exit /b
:getdirs <path>
setlocal enabledelayedexpansion
for /f "delims=" %%I in ('dir /b /s /ad "%~1"') do (
dir /b "%%I" 2>NUL | findstr "^" >NUL || (
rem This is where you put code to handle empty directories.
echo %%I has no files
)
)
goto :EOF
:: JScript portion */
var shl = new ActiveXObject("Shell.Application");
var hint = 'Double-click a folder to expand, and\nchoose a folder to scan for empty directories';
var folder = shl.BrowseForFolder(0, hint, 0, WSH.Arguments(0));
WSH.Echo(folder ? folder.self.path : '');
Edit Since apparently BrowseForFolder accepts an absolute directory, there's really no benefit to using PowerShell / C#. The hybrid batch / PowerShell / C# script shall henceforth be retired to the revision history.
This is fun!