I need to make a condition for unzipping a file using batch.
The method is as follows:
If dummy=1, the batch will unzip an xlsx file to a particular folder, else , it will copy another xlsx file directly to that folder.
my code is as follows:
set dummy=1
If %dummy% EQU 1 (
SET CHEMIN=C:\file_to_unzip
cd %CHEMIN%
for /f %%j in ('dir /b %CHEMIN%\*.zip') do (
C:\PROGRA~1\WinZip\Winzip32 -min -e -o -j %CHEMIN%\%%j %CHEMIN%
)
COPY "C:\file_to_unzip\*.xlsx" "C:\destination"
) else (
COPY "C:\file_to_copy\*.xlsx" "C:\destination"
)
The problem is that without the IF condition, the unzip command will work properly. But once the IF condition is included, the cmd will say that the zip file cannot be found. I am not sure what causes this issue and how to solve this.
Any help appreciated.
Do you have delayed expansion enabled? setlocal enabledelayedexpansion
With your example code above, the failure will occur due to the CHEMIN variable being set within the if statement. If a variable is set within a parentheses scope, the new value set for that variable will not be available until the all the sub scopes have ended. Run the following script to see what I mean:
#echo off
set "xValue=1"
echo.%xValue%
if 1 EQU 1 (
set "xValue=2"
echo.%xValue%
echo.!xValue!
)
If delayed expansion is disabled (which it is by default), the results will be
1
1
1
if enabled, add setlocal enabledelayedexpansion at the beginning
1
1
2
Try your script as follows. To use
setlocal enabledelayedexpansion
set dummy=1
If %dummy% EQU 1 (
SET CHEMIN=C:\file_to_unzip
cd !CHEMIN!
for /f %%j in ('dir /b !CHEMIN!\*.zip') do (
C:\PROGRA~1\WinZip\Winzip32 -min -e -o -j !CHEMIN!\%%j !CHEMIN!
)
COPY "C:\file_to_unzip\*.xlsx" "C:\destination"
) else (
COPY "C:\file_to_copy\*.xlsx" "C:\destination"
)
Another solution would be to just move the set CHEMIN=C:\file_to_unzip line outside of the if statement.
Related
I was trying to convert current working directory of a .bat script into linux format by using wsl wslpath. To show you it works on CMD:
However, when I put it in a .bat file, and changed %cd% to %~dp0, the path is empty:
test.bat contains:
FOR /F %%i IN ('wsl wslpath -a %~dp0') DO set lp=%%i
echo %lp%
Any idea why?
Try this:
echo "%cd%" -- "%~dp0"
%cd% returns the path without ending backslash. So you can add a second variable that clears it.
set "scriptDir=%~dp0"
set "scriptDir=%scriptDir:~0,-1%"
UPDATE (with string substitution only - use the toLinuxPath subroutine)
#echo off
call ::toLinuxPath "%userprofile%\AppData\Local\Temp" tempF
echo %tempF%
exit /b 0
:toLinuxPath [returnVariable - the result will be stored in it; If omitted will be only echoed]
setlocal
set "_path=%~p1"
set "name=%~nx1"
set "drive=%~d1"
set "rtrn=%~2"
set "result=/mnt/%drive:~0,1%%_path:\=/%%name%"
endlocal & (
if "%~2" neq "" (
set "%rtrn%=%result%"
) else (
echo %result%
)
)
I write two for loops to do data automation. While variables echoed well in each loop, the last step (data process using a well-written batch) keeps giving errors that variables set previous do not exist.
The code loops through the subfolders (q1, q2, etc.) under the directory. For each subfolder, there is another for loop to set several variables. I echoed three variables fine in loops.
However, when using a batch called abc.rb, the error is COM_M does not exist.
Actually, the error is all three variables do not exist.
setlocal ENABLEDELAYEDEXPANSION
for /f %%f in ('dir /ad /b ') do (
echo %%f
pause
pushd %%f
for %%a in (*.a*.dat) do (
set COM_DATA=%%a
echo !COM_DATA!
set COM_V=%%f\com-v.dat
echo !COM_V!
set COM_M=%%f\com-M.dat
echo !COM_M!
)
chdir
set fig=someA
set matrix=someB
rem use a written batch (called abc.rb) to process data
abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
popd
)
endlocal
Can anyone find any bugs? Thank you!
I am not sure why the need to pushd into the dir, but as far as I can see, there is only a need for a single for loop:
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat
echo !COM_V!
set COM_M=%%~dpacom-M.dat
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
)
If you require the pushd (which I doubt)
#echo off
setlocal enabledelayedexpansion
set "fig=someA"
set "matrix=someB"
for /R %%a in (*.a*.dat) do (
pushd "%%~dpa"
set "COM_DATA=%%a"
echo !COM_DATA!
set "COM_V=%%~dpacom-v.dat"
echo !COM_V!
set "COM_M=%%~dpacom-M.dat"
echo !COM_M!
rem If abc.rb is is NOT a windows batch file, remove call below
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d !fig! -e !matrix!
popd
)
The double quotes will help if the paths have whitespace, if your program has an issue with them, then you can remove them: abc.rb -a !COM_DATA! -b !COM_V! -c !COM_M! -d !fig! -e !matrix!
#echo off
setlocal ENABLEDELAYEDEXPANSION
set "fig=someA"
set "matrix=someB"
set "COM_V=com-v.dat"
set "COM_M=com-M.dat"
for /f %%f in ('dir /ad /b') do (
echo %%f
pause
if exist "%%~f\*.a*.dat" (
pushd "%%~f" && (
for %%a in (*.a*.dat) do (
set "COM_DATA=%%~a"
echo !COM_DATA!
)
chdir
rem use a written batch called abc.rb to process data
call abc.rb -a "!COM_DATA!" -b "!COM_V!" -c "!COM_M!" -d "!fig!" -e "!matrix!"
popd
)
)
)
endlocal
Issues:
If the nested for loop finds no files with the matched pattern of *.a*.dat, then the variables COM_DATA, COM_V and COM_M may not be defined or updated with a newer value.
Value of COM_DATA is a filename. Values of COM_V and COM_M is the parent folder name and filename, which is inconsistent. Based on the current directory, I would consider filenames as correct. This means that COM_V and COM_M never need to change.
If abc.rb is a batch-file, then you need to use call for the interpreter to return control back to the main script.
Changes:
Test if the file pattern exists, and run the code within the code block if true.
COM_V and COM_M moved out of the for loop as values never change.
Calling abc.rb as being a batch-file.
fig and matrix moved out of the for loop as values never change.
Double quote setting of variables and use of variables to avoid issues with spaces, special characters etc.
pushd && ( ensures the code within the parentheses is run only on success of changing directory.
Removed parentheses in the rem line. They may not cause a problem, though rem lines are parsed and can cause a syntax error. Suggest avoiding special characters in rem lines unless you intend to debug.
I have a batch file that will run csc using a file as input. I want to modify it to read references from a file, and add them to the line that is executed when the script runs.
I've tried a few different things but can't seem to get it work. The references are added with /r: and then each reference path has semi-colon as a separator.
Ideally, I'd like to just have a reference on a new line in the text file. The ref.txt file is in the same directory as the input file, and I'm not sure if it was looking in this directory or not. I also want to make it attempt to run without the ref.txt file, so I added the exists line to do this. I've never used batch scripting before, so maybe someone else knows how to do this better than me. I think that the first line needs to match the start line, which I tried to do in other attempts, but it wasn't working.
The script works in Notepad++, and was from this answer. I think now that the run command also needs to be modified.
This is the run command in Notepad++:
C:\bin\csc.bat "$(CURRENT_DIRECTORY)\$(NAME_PART).exe" "$(FULL_CURRENT_PATH)"
This is the version from that answer:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc /out:%1 %2
#echo off
if errorlevel 1 (
pause
exit
)
start %1 %1
This is an attempt to use references:
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc /out:%1 %2
#echo off
if errorlevel 1 (
pause
exit
)
if not exist ref.txt GOTO :write
set a = /r:
set refs = type ref.txt
start %1 %a% and %refs% and %1
exit
write
start %1 %1
The refs.txt file contains file paths like this:
C:\windows\some_path\some_file.dll;C:\windows\some_path\another_file.dll;
An example command from Microsoft is:
csc /t:exe /r:MyCodeLibrary.dll;NewLib.dll *.cs
IIUR you are trying to apply the refs to the compiled exe not to csc itself.
You need to adapt the path to the ref.txt file
:: Q:\Test\2019\01\25\SO_54360791.cmd
#echo off & Setlocal EnableDelayedExpansion
Set CSC="C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
Set Ref=".\ref.txt"
if exist %Ref% (
<%Ref% Set /p "refs="
set "refs=/r:!refs!"
) else set "refs="
%CSC% %refs% /out:%1 %2
if errorlevel 1 (
pause
exit
)
sample (echoed) output
> SO_54360791.cmd new.exe source.cs
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /r:C:\windows\some_path\some_file.dll;C:\windows\some_path\another_file.dll; /out:new.exe source.cs
I'm not sure if the trailing semicolon in your sample ref.txt will work.
EDIT: Variant with ref.txt file containing quoted pathes with trailing semiclon
:: Q:\Test\2019\01\25\SO_54360791.cmd
#echo off & Setlocal EnableDelayedExpansion
Set CSC="C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe"
Set Ref=".\ref.txt"
Set "refs="
if not exist %Ref% goto :cont
set "refs=/r:"
for /f "usebackq delims=" %%A in (%Ref%) Do set "refs=!refs!%%A"
:cont
echo %CSC% %refs% /out:%1 %2
if errorlevel 1 (
pause
exit
)
goto :Eof
sample (echoed) output
> SO_54360791.cmd new.exe source.cs
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /r:"C:\windows\some_path\some_file.dll";"C:\windows\some_path\another_file.dll"; /out:new.exe source.cs
Recently I started working and my first task is to write a batch file that automatically changes filenames to filename_date with the original file-ending.
For that you should be able to write paths into a textfile (e.g. paths.txt) and when you start the program, it should take any line (=path->file) from there and rename it.
I got it to work on my PC quiet well but as I gave it to testing they asked to make the use of wildcards Z:\Path\*.* possible.
My current code looks as follows:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
For /F "tokens=*" %%m in (paths.txt) do (
set path=%%~dpm
set name=%%~nxm
pushd "!path!"
dir
For /r !path! %%f in (!name!) do (
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_"
set "name=!name!!date:~6,4!"
set "name=!name!!date:~3,2!"
set "name=!name!!date:~0,2!"
set "name=!name!!ending!"
copy "!datsave!" "!name!"
del "!datsave!"
cls
popd
)
)
I know that a lot of it is probably easier and more efficient to do, but this is my first batch project and I am quiet happy except for the wildcard problem.
So an example would be:
C:\Some\Path\*.*
This line would be in paths.txt.
With the splitting
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
I get the following:
path: C:\Some\Path
name: C:\Some\Path
ending: -empty-
datsave: C:\Some\Path
because name is set to the Path at the start of the first FOR-Loop. But that seems to be working if I do not use wildcards.
Now the question: Why does this happen and how do I get rid of it? Or do I just use the wrong type of wildcards?
Again: This is my first time I work with batch, so it might be something simple ;)
Ok, I figured out 2 problems and now it works
set name=%%~nxm evaluates the wildcard. Even if name is *.txt it will return bar.txt.
I replaced that by a basename computation instead: set name=!name:*\=! done enough times (not very subtle but hey batch files forces us to do such things) which preserves the wildcard
The other problem is the for /R loop: after pushd, the argument needs to be . or it won't be scanned.
Last minor one: use rename instead of copy plus delete. It preserves file time and is very fast. Copying then deleting a large file can take a long time.
#echo off
set DEPTH=20
setlocal EnableDelayedExpansion
cd %~dp0
For /F %%m in (paths.txt) do (
set pth=%%~dpm
set z=%%m
set name=!z!
rem brutal basename. We cannot break the inner loop or
rem it would break the upper loop too
for /L %%I in (1,1,%DEPTH%) do set name=!name:*\=!
rem but we can check if it is really a basename
set chkname=!name:*\=!
if not !chkname!==!name! ( echo please increase DEPTH value
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
set pth=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!
echo renaming "!datsave!" to "!name!"
rem ren "!datsave!" "!name!"
popd
)
)
paths.txt contains just a line C:\full\path\to\test\*.txt
my test directory contains 2 text files and 1 other file
output:
renaming "bar.txt" to "bar_20160812.txt"
renaming "foo.txt" to "foo_20160812.txt"
(just uncomment the ren line to get the job done)
Weeeeell First of all thanks again to #Jean-François Fabre and #aschipfl for their patience with me :)
After the hint with the second batch file I had to test a few things as not everything worked as fine, but now everything works great!
Code of the Main file:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
set DEPTH=20
For /F %%m in (paths.txt) do (
pause
set pth=%%~dpm
REM pushd !pth!
REM set origpth=!cd!
REM popd
set z=%%m
set name=!z!
For /L %%i in (1,1,%DEPTH%) do set
name=!name:*\=!
set chkname=!name:*\=!
if not !chkname!==!name! ( echo depth to small
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
pushd %~dp0
call renamefiles.bat %%f REM "!origpth!"
popd
)
)
And the code of the sub-file:
#echo off
REM set pth=%~dp1
REM set origpth=%2
REM set origpth=%origpath:"=%\
REM If !pth!==%origpth% (
set path=%~dp1
set name=%~n1
set ending=%~x1
set datsave=%~nx1
pushd !path!
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!"
pause
echo renaming "!datsave!" to "!name!"
rem "!datsave!" "!name!"
cls
popd
REM )
EDIT: After testing around a bit I figured, that subfolders are included as well! I put extra code to both codes marked with REM and two extra spaces. Take out those REM's and the programm will not longer include subfolders when renaming :)
File names and directories containing variables are being evaluated indside procedures. How do I make batch store the file's name as a variable without evaluating it?
EG: my path is:
"C:\Rare\Names in a File\%CD%\"
batchfile.bat
file.txt
I want to run a script inside this directory:
REM #####BATCHFILE.BAT#######
pushd "%dp0"
for %%A in ("C:\Test") do set EXEPATH=%%~A
for %%A in ("Test.exe") do set EXECUTABLE=%%~A
for %%A in ("%CD%") do set DIRNAME=%%~A
for %%A in ('dir /b /a-d "*.txt"') do (
SET FILENAME=%%~A
CALL :PROCEDURE
)
exit /b
:PROCEDURE
start /w "" "%EXEPATH%\%EXECUTABLE%" "%DIRNAME%\%FILENAME%"
I have made a menu in batch which is a little bit different to your problem.
You need to have one variable per file. The easiest way is to just increment their names:
var1=path1\exe1
var2=path1\exe2
var3=path2\exe1
var4=path2\exe2
On each variable in the for loop you could call a procedure which saves the file with the path in a variable.
for ....(
call :Var %path% %file%
)
:Var
set /a id+=1
set %id%.path=%1&& shift
set %id%.file=%1
goto:EOF
after this you could do
for ..(1,1,%id%) do (
start !%id%.path!\!%id%.file!
)
if you want to have a look at my Batch menu, you can contact me.