Using FOR loop to move files into another directory - windows

What I am trying to do:
Prompt the user to enter an account number.
The account number is the their folder.
The user is then prompted to select another directory (their time-stamped folder).
The script will then kick off an executable.
There will then be the parsing for findstr in a log file.
If the string comes back that it exists, then the script will then goto the :move sub-routine to attempt to recursively move through the "time-stamped" directory that the user selected to move all "*.ARC" files into their "Media1" folder.
The issue I am having is that the final step of running the for loop command in the "sub routine" :move is outputting the following error:
The filename, directory name, or volume label syntax is incorrect.
The filename, directory name, or volume label syntax is incorrect.
I'm not sure what is going on here as I can input this into a command prompt (without the !variables!) and get it to work. However, using the variables doesn't seem to work.
Another interesting point is that I am basically using the same !variables! under the :string "sub routine" and it finds exactly what I asked it to. So, I'm thinking there is nothing wrong with the !variables!?
Am I missing something here?
:main
cls
echo.
set /P acct=Please type the 9 digit account number you would like to restore:
set acctDir=x:\!acct!
set acctDir2=media1\Setup\setup.exe /cd
set log=c:\log.txt
echo. Starting on !date! !time! on !Computername! >> !log!
echo.
echo The account number you selected is: !acct!
echo.
goto :user
:user
set /p answer=Is this correct (Y/N)?
echo.
if /i !answer!==y goto :yes (
) else (
echo.
echo Ok. Let's try again^^!
echo.
pause
cls
goto :main
)
)
:yes
set c=0
For /f %%a in ('dir !acctDir! /B /A:D') do (
set /a c+=1
echo !c! %%a
set dir!c!=%%a
)
echo.
set /p userIn="Select a directory [1-!c!]: "
set userDir=!dir%userIn%!
echo.
echo You selected !userDir! for your data retrieval.
echo.
goto :string
:execute
echo.
echo The Data Protector Program will now be initialized...
start !acctdir!\!userDir!\!acctDir2!
goto :string
:string
set sumLog=!acctdir!\!userDir!\SummaryLog.txt
set succ=finished
set acctDir3=media1
set x=x:\!acct!\!userDir!
findstr " .*!succ!" !sumLog!
if exist errorlevel 0 (
pause
goto :move
) else (
goto :eof
)
:move
for /r "!acctdir!\!userDir!\" %%g in (*.ARC) do echo move "%%g" "!acctdir!\!userdir!\!acctdir3!\"
if exist errorlevel 1 (
echo Cannot move files. Error occurred.
pause
)
endlocal
goto :eof

Here is another tack to test your variables.
Add the four lines below after the :move label and run your batch file.
If there are any spaces or illegal characters you will see them there before the first pause.
:move
echo "!acctdir!"
echo "!userDir!"
echo "!acctdir3!"
pause
for /r "!acctdir!\!userDir!\" %%g in (*.ARC) do echo move "%%g" "!acctdir!\!userdir!\!acctdir3!\"
pause

Related

Multiple For Loops in Batch Script on same Content

I set up a batch (copy.bat) file in my Windows sendto Directory that is for Copying a bunch of files via rightclicking the directories and choosing sendto "copy.bat".
#echo off
setlocal enabledelayedexpansion
set cnt=0
set cur=0
:files
for /R "%~1" %%F in (*.7z, *.avi) do (set /a cnt+=1)
shift
if "%~1" NEQ "" goto :files
echo !cnt! files found for copying.
echo Processing ...
set "DEST_DIR=E:\"
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
if exist "%%F" (
set FILE_DIR=%%~dpF
set FILE_INTERMEDIATE_DIR=!%%~dpF:%%~1%=!
xcopy /E /I /Y "%%F" "%DEST_DIR%!FILE_INTERMEDIATE_DIR!"
)
set /a cur+=1
echo !cur!/!cnt! done...
)
shift
if "%~1" NEQ "" goto :while
pause
If I run the first for loop alone it counts the files.
If I run the second loop on its own it copies exatly those files the other loop is supposed to count.
However, both loops in one batch file will somehowe conflict. The Filecount works correctly but the copy process delivers an error: "The Path of is too long". Between "of" and "is" there is an additional Space. The final pause will correctly wait for button press. I assume it has to do something with the %%F variable, but renaming it in one of the loops does not help.
Please change the name of your .bat - copy is a keyword to cmd.
Your problem is that the first routine shifts out all of the parameters, so your second routine has no parameters to deal with.
The easiest way is probably:
set "DEST_DIR=E:\"
call :while %*
pause
goto :eof
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
...
if "%~1" NEQ "" goto while
goto :eof
which executes the second routine as en internal subroutine (the : in the call is required) providing that routine with a fresh copy of the parameter-list (%*)
You could also consider moving the if "~1"... to the start of the routine (just after the :while), change it to if "%~1" EQU "" goto :eof and change the final statement to goto while.
The label :eof is defined as end-of-file by cmd and should not be declared.

Issue with "findstr" using Batch

Below, I wrote some code. It detects if a Micro-SD card is inserted into the computer and if so, it will ask to enter your pin. After you enter the pin, it will look through the card for a text file that contains a list of pins.
:start
cls
echo.
echo.
if exist E:\ (
goto enterPin
) else (
echo INSERT YOUR CARD
)
timeout 1 >nul
goto start
:enterPin
echo Enter Account Pin: %chip%
set /p pin=": "
:: Finding the specified pin
findstr /m "%pin%" E:\Chip\CardInfo.txt >Nul
if %errorlevel%==0 (
echo Pin "%pin%" is valid
timeout 1 >nul
goto account
)
if %errorlevel%==1 (
echo Pin "%pin%" is invalid
pause
goto start
)
:account
cls
:: Finds the name of the account owner and continues
setlocal enableextensions disabledelayedexpansion
for /F "tokens=2 delims=/" %%a in ('findstr /I "%pin%/" E:\Chip\CardInfo.txt') do set "user=%%a"
for /F "tokens=3 delims=/" %%b in ('findstr /I "%pin%/%user%/" E:\Chip\CardInfo.txt') do set "balance=%%b"
echo.
echo.
echo Welcome, %user%.
echo.
echo ACCOUNT BALANCE: $%balance%
echo.
echo 1=Deposit / 2=Withdraw / 3=Exit / 4=Refresh
choice /c 1234 >nul
if %ERRORLEVEL% EQU 1 goto deposit
if %ERRORLEVEL% EQU 2 goto withdraw
if %ERRORLEVEL% EQU 3 exit
if %ERRORLEVEL% EQU 4 goto account
:deposit
echo.
echo.
set /p add="Money to Deposit: "
set /a moneytoadd=%balance%+%add%
call jrepl "%pin%/%user%/%balance%" "%pin%/%user%/%moneytoadd%" /f E:\Chip\CardInfo.txt /o -
goto account
:withdraw
echo.
echo.
set /p sub="Money to Withdraw: "
set /a moneytosub=%balance%-%sub%
call jrepl "%pin%/%user%/%balance%" "%pin%/%user%/%moneytosub%" /f
E:\Chip\CardInfo.txt /o -
goto account
endlocal
Here's when the issue comes in. A pin consists of 4 numeric characters (ex. 1234), but if there's two pins with the same characters (ex. 1234, 6543), it will say the pin is valid. So for example, if I type 4, it will just look for just the number 4 in the file. And will say the pin is valid. Even though, just the number 4 is not an existing pin. My guess is that it's a flaw with "findstr". But I'm not sure.
Contents of "CardInfo.txt":
1234/Test User/1000
6543/Another Test User/2000
use REGEX (using <StartOfLine><PIN></>):
findstr /m "^%pin%/" E:\Chip\CardInfo.txt >Nul
where ^ is "Start of Line".
Here is what exactly does what you want:
#echo off
::add your path below
for /f %%a in (file.txt) do (
call :funch %%a
)
:funch
set input=%1
set "modifiedinput=%input:~0,4%"
set /p pin=Enter Account Pin:
if %modifiedinput% equ %pin% ( goto authentication_passed) else ( goto authentication_failed)
:authentication_passed
echo auth passed
rem your code
pause >nul
exit
:authentication_failed
echo auth failed
goto funch
it will read the input from file and then extract first four characters which in your case is the pin.
I rewrote the entire batch file to be more fail safe on execution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem endlocal is executed implicitly by cmd.exe on exiting batch file processing.
:Begin
cls
echo\
echo\
if exist E:\ goto enterPin
echo INSERT YOUR CARD
%SystemRoot%\System32\timeout.exe /T 1 >nul
goto Begin
:enterPin
set "pin="
set /p pin="Enter account pin for %chip%: "
if not defined pin goto enterPin
set "pin=%pin:"=%"
if not defined pin goto enterPin
for /F delims^=01234567890^ eol^= %%I in ("%pin%") do goto enterPin
rem Finding the specified pin.
%SystemRoot%\System32\findstr.exe /B /L /M /C:"%pin%/" E:\Chip\CardInfo.txt >Nul
if errorlevel 1 (
echo Pin "%pin%" is invalid.
pause
goto Begin
)
echo Pin "%pin%" is valid.
%SystemRoot%\System32\timeout.exe /T 1 >nul
:account
cls
rem Finds the name of the account owner and continues
for /F "tokens=2,3 delims=/" %%I in ('%SystemRoot%\System32\findstr.exe /B /L /C:"%pin%/" E:\Chip\CardInfo.txt') do set "user=%%I" & set "balance=%%J"
echo/
echo/
echo Welcome, %user%.
echo/
echo ACCOUNT BALANCE: $%balance%
echo/
echo 1=Deposit / 2=Withdraw / 3=Exit / 4=Refresh
%SystemRoot%\System32\choice.exe /c 1234 >nul
if not errorlevel 1 goto account
if errorlevel 4 goto account
if errorlevel 3 exit /B
if errorlevel 2 goto withdraw
:deposit
echo/
echo/
set "add="
set /P "add=Money to deposit: "
set /A moneytoadd=balance + add
call "%~dp0jrepl.bat" "%pin%/%user%/%balance%" "%pin%/%user%/%moneytoadd%" /L /f E:\Chip\CardInfo.txt /o -
goto account
:withdraw
echo/
echo/
set "sub="
set /P "sub=Money to withdraw: "
set /A moneytosub=balance - sub
call "%dp0jrepl.bat" "%pin%/%user%/%balance%" "%pin%/%user%/%moneytosub%" /L /f E:\Chip\CardInfo.txt /o -
goto account
Issues fixed with this code:
There is the command START. For that reason it is not advisable to use the string start as label although it is possible.
It is advisable to avoid an IF (...) ELSE (...) condition if a simple IF condition with GOTO can be used too.
The usage of full qualified file names wherever possible avoids a batch file not running as expected on environment variables PATH (too often) or PATHEXT (rarely) are corrupted on the userĀ“s machine.
A user has the freedom to enter on a prompt done with set /P really anything from nothing to something resulting in further processing in a syntax error with an immediate exit of batch file execution on code not being prepared for any user input. For that reason the first set /P prompt is improved and validates if the user entered a string consisting only of digits.
The usage of FINDSTR is done with additional options to find the pin only at beginning of a line case-sensitive with a literal search and the next character must be a slash character.
The recommended syntax for ERRORLEVEL evaluation is used in the code.
The arithmetic expressions are written using the recommended syntax to work even if the user enters nothing or a string which cannot be converted to an integer number at all.
The command EXIT is used with option /B to just exit the processing of the batch file and not the entire command process.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
choice /?
cls /?
echo /?
findstr /?
for /?
goto /?
if /?
pause /?
rem /?
set /?
setlocal /?
timeout /?
Useful pages regarding to the improvements on code:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
How to stop Windows command interpreter from quitting batch file execution on an incorrect user input?
Syntax error in one of two almost-identical batch scripts: ")" cannot be processed syntactically here
DosTips forum topic: ECHO. FAILS to give text or blank line - Instead use ECHO/
Single line with multiple commands using Windows batch file
Safe number comparison in Windows batch file

Batch: How to scroll through files to read to a variable?

I'm trying to have it scroll through a directory and present a new variable when the user replies "N". I have it all figured out except how to go to the next variable.
cd "C:\Test"
for /r %%F in (*) do SET Show=%%~NF
echo %Show%
echo.
SET /P Continue=Continue?(Y/N)
if /I "%Continue%" EQU "y" goto :Run
if /I "%Continue%" EQU "n" goto :Start
If you're looking to scroll the directory and prompt the user the file name and have them choose to choose it or continue, then bellow should help you.
Firstly, we can use dir /b /a:d to display only directories (folders) in the the current directory. By using a code block ( & ) we can put batch script inside the for loop. For your sake, we can use the CHOICE command to prompt to continue the loop or to save current folder to string and do something with it.
ScrollTreeWithPrompt.bat:
#echo off
setlocal EnableDelayedExpansion
Rem | Configuration
Set "MainDir=C:\Test"
Rem | Get Each Project Folder
for /f "tokens=*" %%A in ('dir "!MainDir!\" /b /a:d') do (
Cls
Echo Current Folder: %%A
echo(
CHOICE /M "Continue?"
Rem | Check for "N" - If so Set String & goto
IF "!ERRORLEVEL!"=="2" (
Set "Choice=%%A"
GOTO Run
)
)
Rem | No Further Results
Cls
Echo Warning: No further folders found.
pause>NUL
goto :EOF
:Run
Cls
echo Currently selected: !MainDir!\!Choice!
pause>NUL
goto :EOF
I have left a few Rem comments in the script to help you along. For any more help on the commands, type the following into a command prompt:
choice /?
set /?
for /?
goto /?
Is this what you need:
For /R "C:\Test" %%A In (*) Do (Choice /M "%%~nA"
If Not ErrorLevel 2 (Set "Show=%%~nA" & GoTo Run))
Exit /B
:Run
Echo Running against %Show%
Pause
Alternatively, should you wish to return to the loop after running against the file name, then use Call instead of GoTo:
For /R "C:\Test" %%A In (*) Do (Choice /M "%%~nA"
If Not ErrorLevel 2 Call :Run "%%~nA")
Exit /B
:Run
Set "Show=%~1"
Echo Running against %Show%
Pause

How to make bat file listing with options to choose

I want to make a bat file that list all of the files in a specific directory, and add numbers at the beginning of the every one of the listed items. This numbers need to be a selectable options.
Example:
I have a folder with 5 files in it, aaa.exe, bbb.exe, ccc.exe, ddd.exe, eee.exe. When i run bat file i need to see
aaa.exe
bbb.exe
ccc.exe
ddd.exe
eee.exe
So now if i wana run 5-th exe i need to press 5, than press enter and that 5th exe will now start.
I allredy find how to list all of the items in folder with this code
REM -start "c:\windows\system32" notepad.exe
for /r %%i in (*) do echo %%i
pause
exit
but i can't figure out how to add numbers in front of the text and make that numbers to be a selectable options.
Edit---
Now im getting
ERROR: Duplicate choices are not allowed. running '""' is not
recognized as an internal or external command, operable program or
batch file.
when i'm trying to run this loop for a second time.
This is code that i wrote:
#ECHO OFF
setlocal enabledelayedexpansion
REM ---Prompt part
:choise
SET /P AREYOUSURE=Install programs (Y/[N])?
IF /I "%AREYOUSURE%" EQU "Y" GOTO :chooseInstall
IF /I "%AREYOUSURE%" EQU "N" GOTO :nope
REM --Cheking for Y or N
GOTO :choise
:nope
echo "Ok. Have a nice daty / night"
pause
exit
:chooseInstall
echo Wich program do you wana install ?
echo.
echo 1. 7Zip
echo 2. CPU Z
echo.
SET /P AREYOUSURE=Choosing:
IF /I "%AREYOUSURE%" EQU "1" set "pathToSoft=C:\Users\usr\Desktop\hello"
IF /I "%AREYOUSURE%" EQU "2" set "pathToSoft=C:\Users\usr\Desktop\bye"
echo.
echo.
echo %pathToSoft%
echo.
echo.
REM ---Installs
echo "Wich file to install"
cd %pathToSoft%
echo.
echo.
REM --Loops that scan files
set /A counter=0
for /R %%i in (*) do (
if not "%%~nxi" == "%~nx0" (
set /A counter+=1
echo !counter!: %%~nxi
set exe[!counter!]=%%i
set choice=!choice!!counter!
)
)
if %counter% LSS 10 (
choice /C %choice% /M "Choose: "
set EXENUM=!ERRORLEVEL!
) else set /P EXENUM="enter exe number: "
set EXECUTABLE=!exe[%EXENUM%]!
echo running %EXECUTABLE%
call "%EXECUTABLE%"
echo.
echo.
echo.
:installmore
SET /P INSTALLMORE=Do you wana install somthing else (Y/[N])?
IF /I "%INSTALLMORE%" EQU "Y" GOTO :chooseInstall
IF /I "%INSTALLMORE%" EQU "N" GOTO :nope
count the executables and associate them with the counter, creating kind of "array" variables (filter out the current batch script)
build the choice list at the same time
after the loop, use choice if no more than 9 choices, else use a classical interactive set
retrieve the user selection and call the executable/batch file
(you have to enable delayedexpansion to be able to use % and ! env. var separators & instant evaluation within the loop)
can be done like this:
#echo off
setlocal enabledelayedexpansion
set /A counter=0
set choice=
for /R %%i in (*) do (
if not "%%~nxi" == "%~nx0" (
set /A counter+=1
echo !counter!: %%~nxi
set exe[!counter!]=%%i
set choice=!choice!!counter!
)
)
if %counter% LSS 10 (
choice /C %choice% /M "type exe number"
set EXENUM=!ERRORLEVEL!
) else set /P EXENUM="enter exe number: "
set EXECUTABLE=!exe[%EXENUM%]!
echo running %EXECUTABLE%
call "%EXECUTABLE%"

FOR and MOVE in same statement in a BATCH file

I am trying to move multiple .ARC files from sub-directories within a primary directory to one of the other sub-directories. I can get this FOR statement to run just fine if I manually type it in the command line, but running it from script doesn't seem to be working at all (with the variables assigned).
Can anyone see that I am doing something wrong? It's under my impression that I have all the correct syntax in place for this to work without any issues.
:main
setlocal enabledelayedexpansion
cls
echo.
set /P acct=Please type the 9 digit account number you would like to restore:
set acctDir=x:\!acct!
set acctDir2=media1\Setup\setup.exe /cd
set log=c:\log.txt
echo. Starting on !date! !time! on !Computername! >> !log!
echo.
echo The account number you selected is: !acct!
echo.
goto :user
:user
set /p answer=Is this correct (Y/N)?
echo.
if /i !answer!==y goto :yes (
) else (
echo.
echo Ok. Let's try again^^!
echo.
pause
cls
goto :main
)
)
:yes
set c=0
For /f %%a in ('dir !acctDir! /B /A:D') do (
set /a c+=1
echo !c! %%a
set dir!c!=%%a
)
echo.
set /p userIn="Select a directory [1-!c!]: "
set userDir=!dir%userIn%!
echo.
echo You selected !userDir! for your data retrieval.
echo.
goto :string
:execute
echo.
echo The Data Protector Program will now be initialized...
start !acctdir!\!userDir!\!acctDir2!
goto :string
:string
set sumLog=!acctdir!\!userDir!\SummaryLog.txt
set succ=finished
findstr " .*!succ!" !sumLog!
if exist errorlevel 0 (
pause
goto :move
) else (
goto :eof
)
:move
set acctDir3=media1
set x=x:\!acct!\!userDir!
set y=x:\!acct!\!userDir!\!acctdir3!
echo !x!
pause
echo !y!
pause
for /r "!x!" %%i in (*.ARC) do move "%%i" "!y!"
pause
:: for /r "X:\101004357\Jan_20_2014_6_56_55_953PM" %%g in (*.ARC) do move "%%g" "X:\101004357\Jan_20_2014_6_56_55_953PM\Media1"
endlocal
goto :eof
Please disregard the Access Denied as I will correct that issue on my own. However, the output I get if I manually run the same FOR statement from the command line is:
X:\>for /r "X:\101004357\Jan_20_2014_6_56_55_953PM" %g in (*.ARC) do move "%g" "X:\101004357\Jan_20_2014_6_56_55_953PM\Media1"
X:\>move "X:\101004357\Jan_20_2014_6_56_55_953PM\Media1\FILES1.ARC"
"X:\101004357\Jan_20_2014_6_56_55_953PM\Media1"
Access is denied.
0 file(s) moved.
X:\>move "X:\101004357\Jan_20_2014_6_56_55_953PM\Media2\FILES2.ARC"
"X:\101004357\Jan_20_2014_6_56_55_953PM\Media1"
Access is denied.
0 file(s) moved.
X:\>move "X:\101004357\Jan_20_2014_6_56_55_953PM\Media3\FILES3.ARC"
"X:\101004357\Jan_20_2014_6_56_55_953PM\Media1"
Access is denied.
0 file(s) moved.
If I run it from the script, I get no output except to go to :eof. Please see the output from the script:
Please type the 9 digit account number you would like to restore: 101004357
The account number you selected is: 101004357
Is this correct (Y/N)? y
1 Jan_14_2014_12_49_11_900PM
2 Jan_20_2014_6_56_55_953PM
Select a directory [1-2]: 2
You selected Jan_20_2014_6_56_55_953PM for your data retrieval.
[2014-01-23 12:15:14 InfoTrace (8209)]DVD Media image creation finished at: Thu Jan 23 12:14:50 2014
Press any key to continue . . .
x:\101004357\Jan_20_2014_6_56_55_953PM
Press any key to continue . . .
x:\101004357\Jan_20_2014_6_56_55_953PM\media1
Press any key to continue . . .
Press any key to continue . . .
If you notice between the double PAUSE syntax above is where the FOR statement should have ran. But it never does and moves to the second pause without any output or any kind of move, etc.
Any input would be greatly appreciated!
change
for /r "!x!" %%i in (*.ARC) do move "%%i" "!y!"
to
for /r "%x%" %%i in (*.ARC) do move "%%i" "%y%"
!var! is not evaluated until after the for loop is running. Only use !var! on a variable that has changed within a block statement (a parenthesised series of statements).
I'd also advise to not use a : in goto except for the special case of goto :eof
and
if exist errorlevel 0 (
means "if the file named 'errorlevelexists, execute the excutable named0`
No idea what you are trying to do here, but IF ERRORLEVEL n is TRUE if errorlevel is n or greater than n. IF ERRORLEVEL 0 is therefore always true. IF NOT ERRORLEVEL 1 is a test for errorlevel=0. So is IF %ERRORLEVEL%==0, except that the former can be used within a block but the latter cannot.

Resources