I am writing a batch script where I am writing a set of instruction inside function as I want to call it many time so want to reuse it.For me it works when i write outside function but inside function it never works . Below the code which I have used .
#echo off
set _prefs="%APPDATA%\test\test\BrowserProfile\prefs.js"
set _prefs_notes="%ProgramFiles(x86)%\test\tset1\Data\workspace\BrowserProfile\prefs.js"
#rem it works
copy /y %_prefs_notes% %_prefs_notes%.copy1 > nul
CALL :AMEND_PREFJS %_prefs_notes%
EXIT /B
:AMEND_PREFJS
rem make copy of prefs file
#rem it does not work
copy /y %~1 %~1.copy > nul
findstr /v "layers.acceleration.disabled" "%~1" > "%~1.tmp"
echo end
set %~1=
EXIT /B 0
goto end
:prefs_not_found
rem set error level?
echo "file does not exist -- %_prefs_notes%"
:end
set _prefs=
I am going to tell you what most of us do as best practices for writing batch files.
Never assign quotes to variables. You can use quotes though to protect the assignment of the variable. This helps with protecting special characters within the assignment and also keeps you from assigning trailing spaces.
Get into the habit of always using quotes to surround your file paths when using them with another command.
This is how I would write your batch file.
#echo off
set "_prefs=%APPDATA%\test\test\BrowserProfile\prefs.js"
set "_prefs_notes=%ProgramFiles(x86)%\test\tset1\Data\workspace\BrowserProfile\prefs.js"
#rem it works
copy /y "%_prefs_notes%" "%_prefs_notes%.copy1" > nul
CALL :AMEND_PREFJS "%_prefs_notes%"
EXIT /B
:AMEND_PREFJS
rem make copy of prefs file
#rem it does not work
copy /y "%~1" "%~1.copy" > nul
findstr /v "layers.acceleration.disabled" "%~1" > "%~1.tmp"
echo end
EXIT /B 0
Related
I am a complete novice when it comes to scripting, but am attempting to write a batch script which runs a command to output a png file to a printer. The script I have works fine for one file, but when there are multiple files it does not.
Can anyone point me in the right direction please?
#echo off
REM ___Change Directory to where Label Is Stored___
pushd C:\AFP\to
REM ___Create Variable to capture filename of any png file___
for /F %%a in ('dir /b *.png') do set FileName=%%~na.png
REM ___Now we have the filename as a variable, send it to printer using Zebra SSDAL___
\\172.16.100.2\nDrive\Prime_DPD_Label_Print\ssdal.exe /p "TSC DA200" send %FileName% >> C:\AFP\Log\Label_Printing_Log.txt
REM ___Copy PNG File to Backup Folder___
XCOPY /y /q /c C:\AFP\to\*.png C:\AFP\backup\
REM ___Delete PNF File from To Folder___
DEL C:\AFP\to\*.png
When the script runs, the first file prints fine. The subsequent files then do not print, I get "File does not exist" back from the ssdal.exe command. Why would the first one work but not the subsequent prints? I would have expected the for to loop through.
#ECHO Off
SETLOCAL
REM ___Change Directory to where Label Is Stored___
pushd C:\AFP\to
REM ___Process all png files___
for /F "delims=" %%a in ('dir /b *.png') do (
REM ___Now we have the filename as "%%a", send it to printer using Zebra SSDAL___
\\172.16.100.2\nDrive\Prime_DPD_Label_Print\ssdal.exe /p "TSC DA200" send "%%a" >> C:\AFP\Log\Label_Printing_Log.txt
IF ERRORLEVEL 1 (
CALL ECHO SSDAL returned ERRORLEVEL %%errorlevel%% FOR "%%a"
) ELSE (
REM ___Move PNG File to Backup Folder___
IF EXIST "c:\afp\backup\%%a" (
ECHO MOVE "%%a" to backup skipped as file already exists IN backup
) ELSE (
MOVE "%%a" C:\AFP\backup\
)
)
REM Two-second delay
TIMEOUT /t 2 >nul 2>nul
)
POPD
GOTO :EOF
Ah! using Zebra printers. Sensible lad!
This replacement script should do what you want.
The setlocal command is used to ensure that any variation made by this batch to the cmd environment is discarded when the batch ends.
The delims= sets "no delimiters" so for/f will set %%a to the entire filename, even if it contains spaces or other delimiters. Quoting %%a ensures such filenames are kept together as a single unit, not interpreted as separate tokens.
I'm assuming that ssdal acts responsibly and returns errorlevel non-zero in the case of errors. The if errorlevel 1 means if the errorlevel is currently 1 or greater than 1 and in that case, the error message is generated. We need to call echo ... %%varname%% ... in order to display the current value of the variable, if we're not using delayed expansion (many SO articles explain this)
Otherwise, if ssdal was successful, check for the existence of the filename in the backup directory, and either move it there or report that it already exists.
Of course, there are many ways in which this could be manipulated if features I've added are not desired. I'm happy to adjust this script to comply.
timeout is a standard utility to wait for a keypress. The redirection takes care of its prompting (it will provide a countdown unless gagged).
So I'm not experienced at all with scripting so just trying to figure it out as I go and I'm kinda stuck. So with the following script I'm trying to basically search through a set of folders and within each folder there's two files. They stay paired with each other (for calling an external application to run them together). So I'm having them moved, then trying to call the external application and such. I've also had in to delete the two files after the other application has ran... The issue is that after the 2nd file move, I can't put anything else code wise after it for it to execute. For example, there's two test files I was trying this with. If I add the command to run the external application AFTER the move is performed which is what I'm trying to do....it moves the files but it basically moves the first file twice and uses it as both files if that makes sense. So there's file A and file b. I end up with two file A's, just one of them ends up named file b. If I comment any code after the move, it works perfect.
#ECHO ON
SET Loc1Dir=C:\Users\*****\Desktop\temp
SET Loc2Dir=S:\shared\*****\*****\Input\Run\Working_Folder
CD /D "%Loc1Dir%"
FOR /R %%F IN ("*.STMT*") DO CALL :CopyFile %%~F
FOR /R %%I IN ("*.CARD*") DO CALL :CopyFile2 %%~I
:CopyFile
SET copyfname=%~1
SET fname=driver
ECHO F | XCOPY /Y /F "%copyfname%" "%Loc2Dir%\%fname%"
:::CALL :CopyFile2
:::GOTO :EOF
:CopyFile2
SET copyfname2=%~1
SET fname2=card
ECHO F | XCOPY /Y /F "%copyfname2%" "%Loc2Dir%\%fname2%"
I know some is commented out, but that's just from moving stuff around and trying to get it to work correctly. Essentially after the 2 moves are done, I will call an external application, and then it will run and then come back and I'll have this script delete the files and then proceed with the next folder/set of files.
It could be an issue with how you are returning after your CALL statements. Using EXIT /R or a GOTO :EOF at the end of the subroutine will return you to where the CALL was made.
I would try something like this:
EDIT: fixed EXIT /R to be EXIT /B
EDIT2: Added EXE in second SUB
```
#ECHO ON
SET Loc1Dir=C:\Users\*****\Desktop\temp
SET Loc2Dir=S:\shared\*****\*****\Input\Run\Working_Folder
CD /D "%Loc1Dir%"
FOR /R %%F IN ("*.STMT*") DO CALL :CopyFile %%~F
FOR /R %%I IN ("*.CARD*") DO CALL :CopyFile2 %%~I
:CopyFile
SET copyfname=%~1
SET fname=driver
ECHO F | XCOPY /Y /F "%copyfname%" "%Loc2Dir%\%fname%"
foo.exe -argument
EXIT /B
:CopyFile2
SET copyfname2=%~1
SET fname2=card
ECHO F | XCOPY /Y /F "%copyfname2%" "%Loc2Dir%\%fname2%"
EXIT /B
:EOF
```
I want count only files in a directory with Windows batch.
(The ultimate purpose is to call a vbs file if I have any files whatsoever.)
Here's what I have so far:
set /a db=0
echo %db%
for /f %%i in ('dir /b') do (
set /a db=%db%+1
echo %db%
)
echo %db%
This will me give the following: 0 0 0 0 1 for the value of %db%
(as I have 3 files in the directory right now)
Maybe trivial, but why won't it increase the value of %db% during the loop, only in the end?
What happens between the last loop of for (where %db% still was 0) and the last line (where %db% is 1 already)?
How should I fix it?
Thanks in advance!
While the batch file is being executed, each line or block of lines (lines enclosed in parenthesis) is first parsed and then executed. During the parse phase, variable read operations (where you retrieve the value of the variable, that is %var%) is removed, being replaced with the value inside the variable. Once this is done, the resulting command is executed.
Inside your for loop you are changing the db variable, but you can not retrieve the changed value. All the read operations were replaced with the value in the variable before the for command start to execute.
The usual way to solve this problem is to enable delayed expansion and change the %var% syntax into !var! syntax where needed. This tells the batch parser that the variable/value substitution must be delayed until the command is executed, not when the line/block is parsed.
setlocal enabledelayedexpansion
set /a db=0
echo %db%
for /f %%i in ('dir /b') do (
set /a db=!db!+1
echo !db!
)
echo %db%
Now the read operations inside the for loop are using delayed expansion.
But, you don't even need it. The set /a uses its own parser that is able to retrieve the value in the referenced variables, so you can use any of those options
set /a db=db+1
set /a db+=1
to change the variable without having to use read syntax.
Also, unless you need to take into consideration hidden files, it is better to not use a for /f processing the output of a dir command that is executed in a separate cmd instance. Just use a for loop
set /a "db=0"
for %%a in (*) do set /a "db+=1"
echo %db%
But, if as you point all you need is to know if you have any file, and not the number of them, all this is not even needed
dir /a-d >nul 2>nul && ( echo there are files ) || ( echo there is not any file )
It just executes the dir command, with folders excluded (/a-d), discarding any output (>nul) or error message (2>nul).
- If any file is found (no errors), the command after the conditional operator && is executed.
- If there is not any file, the dir command fails and the command after the || conditional operator is executed.
If you just want to detect whether or not there are any files, MC ND already showed a reliable way in their answer.
To count the number of files you can let the find command do the work, as it features a /C switch that counts the number of matching lines. If you specify to do an inverse search by /V (so to return all non-matching lines), together with an empty search string "", all lines are returned.
So when the input for find comes from a dir command line that lists all files, the total count of files is returned (2> nul suppresses the error message in case no files are present):
2> nul dir /B /A:-D | find /C /V ""
To capture the count in a variable, use a for /F loop:
for /F %%C in ('
2^> nul dir /B /A:-D ^| find /C /V ""
') do set "COUNT=%%C"
echo %COUNT%
Note that special characters like > and | need to be escaped by preceding with ^ in order for them not to be processed immediately but in the cmd instance initiated by for /F.
A working script that moves various files based on file name runs successfully. After the script is done it will check two directories for any lingering files using IF EXIST *.txt. This works great except I've noticed some files with no extension. These were not an issue before and since that cannot be helped due to processes out of my control, I need to amend my script.
My only idea is the following code. Bear with as there are two conditions:
:check1
PUSHD "\\UNC\path1" &&(
DIR /A-D *.
IF %errorlevel% NEQ 0 GOTO check2
) & POPD
:add1
ECHO Add note to the log file
:check2
PUSHD "\\UNC\path2" &&(
DIR /A-D *.
IF %errorlevel% NEQ 0 GOTO laststep
) & POPD
:add2
ECHO Add note to the log file
:laststep
Some other code before exiting
This should run DIR on the path and if files without extensions exist, it will have %errorlevel% zero and move on to the next check. If there are no files present, it will have %errorlevel% not zero (likely 1) and it will append some text to the log before the next check. Check two will do the same.
This seems to be awfully complicated and I am not able to find a "one-liner" solution that is as easy as IF EXIST. I realize I can use *. but that returns directories as well and may result in an incorrect %errorlevel%.
Updated Code
Where I normally set my variables, I also SET the two paths to run DIR against. This way they can be used more easily elsewhere and I bypass the UNC Path error I normally get - reasons for that are unknown to me. The updated file check, used only for files without an extension, is:
DIR %p1% /b /a-d|FIND /v "." && ECHO Found 1 >> %log%
DIR %p2% /b /a-d|FIND /v "." && ECHO Found 2 >> %log%
FINDSTR /I "Found" %log%
IF %errorlevel% EQU 0 GOTO stillthere
:nofiles
Some code
GOTO domore
:stillthere
Some code
:domore
Other code before exit
Thank you for the responses, I've learned from this.
Is this what you want to find?
dir /b /a-d |find /v "."
#ECHO OFF
SETLOCAL
:check1
PUSHD "u:\path1"
DIR /A-D *. >NUL 2>NUL
IF %errorlevel% EQU 0 ECHO Add note \path1 to the log file
POPD
PUSHD "u:\path2"
DIR /A-D *. >NUL 2>NUL
IF %errorlevel% EQU 0 ECHO Add note \path2 to the log file
POPD
:laststep
:: Some other code before exiting
GOTO :EOF
Your problems include:
Within a block statement (a parenthesised series of statements), the entire block is parsed and then executed. Any %var% within the block will be replaced by that variable's value at the time the block is parsed - before the block is executed - the same thing applies to a FOR ... DO (block).
Hence, IF (something) else (somethingelse) will be executed using the values of %variables% at the time the IF is encountered.
and you are potentially jumping out of a PUSHD/POPD bracket which would mean your POPD won't necessarily restore your starting directory.
(Note that I used u:\ rather than a server to suit my system)
You are already using && to verify PUSHD worked. You can do the same with your DIR /-D. I find it makes life easier. Also, you probably want to hide any error message if *. is not found, especially since that is the expected condition. I also hid the display of any files that might be found, but you can certainly get rid of that redirection. You might also want to hide error message if PUSHD fails, but I didn't implement that.
PUSHD "\\UNC\path1" && (
DIR /A-D *. 1>nul 2>nul && ECHO Add note to the log file
POPD
)
PUSHD "\\UNC\path2" && (
DIR /A-D *. 1>nul 2>nul && ECHO Add note to the log file
POPD
)
ECHO Some other code before exiting
I have a directory with the following structure:
C:\Directory1\
sub1\
sub2\
sub3\
somefilename.txt
someotherfile.txt
Inside each sub*\ there are .dat files that I need to copy to another directory mirroring along the way the directory name where they were found. So if I find C:\Directory1\sub2\file.dat I would copy that into C:\mirror\sub2\file.dat and so on.
I tried several combinations of things similar to
for /R %SRC_DIR% %%f in (*.dat) do copy "%%f" %BACKUP_DIR%\%%~nf%%~xf
(please note this is just an example of code I was playing with, i know it doesn't work)
anyway, after trying to a couple of day I still don't know how to do it. Any chance of help?
Code is appreciated.
thanks!
This works for me:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set SourceDir=c:\source\dir
set TargetDir=d:\target\path
set FileMask=*.cpp
for /r "%SourceDir%" %%F in (%FileMask%) do (
call :ReplacePrefix target_path "%%~F" "%SourceDir%" "%TargetDir%"
call :CopyFile "%%~F" "!target_path!"
)
endlocal
goto :EOF
:CopyFile %1=source_path %2=target_path
mkdir %~dp2
copy %1 %2
goto :EOF
:ReplacePrefix %1=result_var_name %2=string %3=replace_what %4=replace_with
rem a question mark is prepended to ensure matching only at the beginning of the string
set rp_value=?%~2
call :DoIt "set %1=%%rp_value:?%~3=%~4%%"
goto :EOF
:DoIt %1=cmd
%~1
goto :EOF
Keep in mind though that it can break if paths contain unusual characters (such as = and some others which I can't remember now).
Use the following XCOPY command:
xcopy "c:\directory1\*.dat" "c:\mirror\" /s /v /c /y
If you do not want to see the filenames displayed on the screen add '/q' to the list of options.
The '/s' will copy files from subfolders. If the subfolders don't already exist they will be created.
The '/v' forces verification. Not necessary but it's nice to have that peace of mind.
The '/c' forces XCOPY to continue with the rest of the files if it encounters any problems - in other words, your batch file won't halt abruptly with only 'some' of your files copied. XCOPY will copy all that it can.
The '/y' suppresses prompting to overwrite an existing file.