I have wrote a batch file to Backup User Profile, but I would like to make it idiot proof.
A few times I have actually left the batch file on my desktop and run it on my own profile and as the batch file contains a robocopy command to copy the Desktop folder, it creates an infinite loop of duplicate folder inside one and other, which are difficult to get rid off due to the 256 character limit.
I am trying to get the batch file to check its own location is not within the user profile before executing.
I was messing around last night with the IF command and this is what i came up with:
#ECHO off
SET /P %UserName%=Enter Username:
IF "%~dp0"=="C:\Users\%UserName%\NUL" (
GOTO ERROR
) ELSE (
GOTO PROFILEBACKUP
)
:PROFILEBACKUP
REM To Be replaced by Profile Backup Script
ECHO Correct Location
:ERROR
ECHO Move ProfileBackup.CMD to another location
But the only output I get is:
Enter Username: smithsl
Correct Location
Move ProfileBackup.CMD to another location
Any help would be appreciated
Here is a suggestion for a batch code checking if location of batch file is somewhere in user's profile directory tree.
#echo off
setlocal EnableDelayedExpansion
set "BatchPath=%~dp0"
:EnterName
rem Define as default name of current user.
set "NameOfUser=%UserName%"
set /P "NameOfUser=Enter user name (default: %NameOfUser%): "
rem Remove double quotes from entered string.
set "NameOfUser=!NameOfUser:"=!"
rem Were something different than just double quotes entered?
if "%NameOfUser%"=="" goto EnterName
rem Remove also angle brackets and exclamation marks.
set "NameOfUser=!NameOfUser:<=!"
if "%NameOfUser%"=="" goto EnterName
set "NameOfUser=!NameOfUser:>=!"
if "%NameOfUser%"=="" goto EnterName
set "NameOfUser=%NameOfUser:!=%"
if "%NameOfUser%"=="" goto EnterName
rem Starts the path of the batch file with C:\Users\entered user name?
if not "!BatchPath:C:\Users\%NameOfUser%=!" == "%BatchPath%" goto LocationError
rem To be replaced by profile backup script
echo Correct Location
endlocal
goto :EOF
:LocationError
echo Move %~nx0 to another location.
endlocal
goto :EOF
Although some tests are made for checking invalid user input, it is still not idiot proof as this is nearly impossible when a user can enter something.
The mistake you made was assigning entered value to %UserName% which means if your user account name is for example Scott, the entered string is assigned to an environment variable with name Scott and not to environment variable with name UserName. It is of course no good idea to overwrite the predefined UserName environment variable value by a batch script.
Open a command prompt window, execute there set /? and read the help printed into the console window to understand the string replacements used in this batch code using additionally delayed environment variable expansion.
Related
I have a set of variables that represent directory names: folder_1, folder_2,... folder_n.
In my code I am trying to set the variable FOLDER to the correct directory name based on the user input selection after validating the value with a conditional if statement. However, as my code is, the FOLDER variable is assigned with double quote which causes the DESTINATION variable to end up looking something like this: C:\User\stackoverflow\"chosen folder"
When attempting to use this variable as the destination for a move command it fails.
#echo off
setlocal EnableDelayedExpansion
REM totally nothing import happening up here
set /p selection=""
if %selection% GEQ 0 if %selection% LEQ %a% (
echo you selected folder !folder_%selection%!
set FOLDER="!folder_%selection%!"
set DESTINATION=!PATH!\Bermuda Triangle\!FOLDER!
set /a pass=1
)
REM nothing here either
move /-y C:\wherever\this\file\is DESTINATION
pause
Okay, that's fine, I'll just lose the quotes in line 10.
set FOLDER=!folder_%selection%!
Running my script with these changes "apparently" fixes the syntax issue for the move command, because it gets moved to where it is supposed to; however, the pause command at the end of my script no longer works and the window closes. So while it works I can't check my echo feedback calls to make sure everything is functioning as intended.
Can anyone help me understand what is going on here?
I am trying to figure out how to use call appropriately to pass variables backwards (to the original batch file that calls other batch files).
The way I have my batch files currently setup is my main batch file calls other batch files which may then even call a third batch file which creates the variable.
Here are some examples of my scripts so far, for this example I will be focusing on main.bat, VerifyCredentials.bat, and the CreateAdminCreds.bat files. In these I am trying to pass the %AdminUser% and %AdminPass% variables back to main.bat:
main.bat example:
REM Define all the variables that will be updated by other batch files.
set NewCompName= & set DomainUser= & set DomainPass= & set AdminUser= & set AdminPass=
REM Create the variables for the domain tech account and local admin account. Verify tech account can access SCCM file share.
set CredTestVar=0
:CredTestLoop
call CreateCompName.bat & call VerifyCredentials.bat & call TestCredentials.bat
if %CredTestVar% LSS 1 goto CredTestLoop
REM Activate the local admin account and set password
ECHO Enabling Administrator account
net user administrator /active:yes
net user administrator "%AdminPass%"
ECHO.
VerifyCredentials.bat example:
#ECHO off
if not exist Credentials\DomainUser.txt call CreateDomainCreds.bat
if not exist Credentials\DomainPass.txt call CreateDomainCreds.bat
for /f "delims=" %%x in (Credentials\DomainUser.txt) do set DomainUser=%%x
for /f "delims=" %%x in (Credentials\DomainPass.txt) do set DomainPass=%%x
if not exist Credentials\AdminUser.txt call CreateAdminCreds.bat
if not exist Credentials\AdminPass.txt call CreateAdminCreds.bat
for /f "delims=" %%x in (Credentials\AdminUser.txt) do set AdminUser=%%x
for /f "delims=" %%x in (Credentials\AdminPass.txt) do set AdminPass=%%x
CreateAdminCreds.bat example:
#ECHO off
REM Create and define the local administrator credentials
:CreateAdmin
cls
echo.
echo Please enter a new password for the local Administrator account
echo.
set "AdminUser=Administrator"
set /p "AdminPass=Administrator Password:"
goto :ResponseAdmin
:ResponseAdmin
set "AdminAnswer="
cls
echo.
echo The Administrator password is "%AdminPass%"
echo.
set /p "AdminAnswer=Is this correct? (y/n):"
if %AdminAnswer%==y () else (if %AdminAnswer%==n (goto :CreateAdmin) else (goto :ResponseAdmin))
(echo=%AdminUser%) > "Credentials\AdminUser.txt"
(echo=%AdminPass%) > "Credentials\AdminPass.txt"
set "AdminAnswer="
So all of this individually works fine.. but when I get to the step in main.bat where I try to use that variable %AdminPass% for the tech to define the local admin account it doesn't change the password to anything (on restart/lock you can login to local admin without a password)
Can anyone help explain to me why the variable %AdminPass% is not being passed back to main.bat?
I have tested this passing theory like this:
test1.bat
#ECHO off
set testvar=
echo testvar is currently equal to %testvar%
REM this returns "testvar is currently equal to"
call test2.bat
echo testvar is now equal to %testvar%
REM this returns "testvar is now equal to something"
test2.bat
#ECHO off
set testvar="something"
and this successfully changes testvar to something for the second echo in the test1.bat file.
Thank you in advance for any help.
When a cmd instance starts, it is provided with a set of string variables which form the environment (always strings - if the value of any string is all-numeric, then it can often be used in calculations.)
Any batch file that runs within that instance can modify, add to, or delete variables. Hence running batch files A and B in that order would allow B to see values established by A, but in the reverse order, those values would not be set.
It's therefore usual practice to follow the initial #echo off command (which turns command-regurgitation to the console for debugging OFF) by a setlocal command, which itself has various options. Fundamentally, a setlocal establishes a local environment with a copy of the environment as it stood when the setlocal was executed.
setlocals may be nested, and are closed by an endlocal command or by reaching the physical end-of-file, whereupon the environment is restored to its condition at the time the matching setlocal was executed. Note that this applies only to the environment, it does not affect the setting of the durrent directory or drive for instance.
So, if A calls B then B will NOT manipulate A's environment if B uses setlocal. If B has no setlocal then it will manipulate A's environment BUT if run from the prompt, it will manipulate the cmd instance's environment, which is generally undesirable.
There is a method to transmit the variable "over the endlocal barrier" like this:
endlocal&set "fred=%fred%"
which seems quite bizarre. What this does is use cmd's parser. The entire line is parsed, and %fred% is replaced by the current value of the variable fred within the inner environment, and then the line endlocal&set "fred=fredsvalue" is executed; two commands, serially. So the inner environment is discarded and then the variable in the calling environment is set to the value.
So - you pays your money, you takes your choice. Use setlocal in the called routines, and you'll need to jump the endlocal barriers. Omit the setlocal and the called routine will manipulate the current environment.
This script looks at a network location for a folder name and specific file and then should copy the file to other folder on the network with current timestamp in destination file name.
Can you advise any syntax error or reason why it is not copying?
ECHO on
Title %0
set GETfn=Q:\Cdata\mm_tn
set GETfn=%GETfn: =%
echo GETfn = %GETfn%
set f1=%GETfn%%m%%d%%y%-%hr%%mn%.csv
set f1=%f1: =%
echo f1 = %f1%
copy GETfn.csv Q:\FTP\Sent\%f1%
dir Q:\FTP\Sent\
For example, if i have specificfile.csv on a mapped network drive Q:\Cdata\mm_tn\
then using this:
#echo off
:: src -> destination
set src=Q:\Cdata\mm_tn\
set dst=Q:\FTP\Sent\
echo -------------------------------------
echo source dir ---^> %src%
echo destination dir ---^> %dst%
echo -------------------------------------
:: timestamp
:: https://stackoverflow.com/questions/1064557/creating-a-file-name-as-a-timestamp-in-a-batch-job
FOR /F %%A IN ('WMIC OS GET LocalDateTime ^| FINDSTR \.') DO (
#SET B=%%A
)
set timestamp=%B:~4,2%_%B:~6,2%_%B:~0,4%_%B:~8,2%-%B:~10,2%
echo %timestamp%
:: copy from Q:\Cdata\mm_tn\ -> Q:\FTP\Sent\
copy %src%specificfile.csv %dst%%timestamp%_specificfile.csv
the specificfile.csv is copied to Q:\FTP\Sent\ as timestamped file 12_17_2020_12-39_specificfile.csv. Now paths can be easily adjusted for your requirements.
The second block is:
set GETfn=Q:\Cdata\mm_tn
set GETfn=%GETfn: =%
echo GETfn = %GETfn%
This block first defines the environment variable GETfn with string value Q:\Cdata\mm_tn. Next it uses a string substitution to remove all spaces from the string assigned to environment variable GETfn. That is of course a completely useless command line as the folder path assigned to the environment variable does not contain any space at all on having the batch file written without trailing space(s) on the line above. The last line of this block just outputs the fixed folder path which makes also no real sense.
The third block is:
set f1=%GETfn%%m%%d%%y%-%hr%%mn%.csv
set f1=%f1: =%
echo f1 = %f1%
It defines an environment variable f1 being a concatenation of the strings assigned to the environment variables GETfn (defined above), m, d, y, hr and mn not defined in posted code at all. So f1 is defined with Q:\Cdata\mm_tn-.csv which is of course not right. The next line is again useless as it removes all spaces from the string assigned to environment variable f1 not containing any space at all, except the batch file contains one or more trailing spaces at end of the line above. The third line just outputs the wrong defined environment variable f1.
I recommend to open a command prompt, run set /? and read the output help carefully and completely from top of first to bottom of last page. Next I suggest to read my answer on Why is no string output with 'echo %var%' after using 'set var = text' on command line? It has some useful additional information about the usage of command SET.
Please read carefully and completely my answer on Time is set incorrectly after midnight. Then you should have the knowledge why the following command line is a replacement for the entire posted batch file code.
#if exist "Q:\Cdata\mm_tn\GETfn.csv" for /F "tokens=1-5 delims=/: " %%I in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do #copy "Q:\Cdata\mm_tn\GETfn.csv" "Q:\FTP\Sent\GETfn_%%J%%K%%I-%%L%%M.csv" & goto :EOF
That command line copies the file Q:\Cdata\mm_tn\GETfn.csv to directory Q:\FTP\Sent with new file name GETfn_MMddyyyy-hhmm.csv using current date/time.
I would not use this date format for the destination file name. It would be better to use GETfn_yyyyMMdd-hhmm.csv, or better to read GETfn_yyyy-MM-dd_hh-mm.csv. This is the international date format which has the big advantage that files sorted by name are with such a date/time format sorted at the same time in chronological order. That is very often very helpful. So better in my opinion would be:
#if exist "Q:\Cdata\mm_tn\GETfn.csv" for /F "tokens=1-5 delims=/: " %%I in ('%SystemRoot%\System32\robocopy.exe "%SystemDrive%\|" . /NJH') do #copy "Q:\Cdata\mm_tn\GETfn.csv" "Q:\FTP\Sent\GETfn_%%I-%%J-%%K_%%L_%%M.csv" & goto :EOF
For understanding the used commands in the single command line and how they work, open a window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
copy /?
for /?
goto /?
if /?
robocopy /?
See also:
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
I'm having trouble with a batch script that worked a few days ago, but now doesnt work, even though no changes has been made!
I believe something has changed in the system without my knowledge.
The expected link is:
order.htm?order=12345
But it becomes like this: (notice the question mark becomes %3F)
order.htm%3Forder=12345
The code is as follows:
#echo off
echo.
set "drive=%~d0"
set "runningDir=%~dp0"
:start
ClS
Echo.
Set /P Job=Enter number:^>
#echo off
if exist c:\"Program Files (x86)"\Google\Chrome\Application\chrome.exe goto program_files_x86
:program_files_x86
start c:\"Program Files (x86)"\Google\Chrome\Application\chrome.exe --disable-print-preview --ignore-certificate-errors --disable-web-security --user-data-dir --allow-file-access-from-files %runningDir%\order.htm?order=%job%
goto end
:end
goto start
Any suggestions?
Best Regards
Niclas
Double quotes should be usually used around entire folder/file string and not just parts of it.
Command START interprets first double quoted string as title for the new command process. Therefore on starting a GUI application an empty title string specified with "" should be used on START command line to avoid interpreting the double quoted file name with path of the application to execute as title string.
The batch file path referenced with %~dp0 always ends with a backslash. Therefore don't specify an extra backspace after this string or an environment variable like runningDir with path of batch file. By the way: The current directory on running a batch file can be different to directory of batch file. For that reason the name runningDir is not good as misleading. A better name for the environment variable is BatchPath.
It is possible to use start as label in a batch file. But it is not advisable to do that because of command START which makes it difficult to search for label respectively search for command. It is better to use a label like Begin.
In an url the directory separator is / and therefore each backslash (directory separator on Windows) in batch file path should be substituted by a slash.
The url should start with the protocol like http:// (Hypertext Transfer Protocol) and should be enclosed completely in double quotes.
And last echo/ or echo( is better than echo. for printing a blank line, see Difference between Echo[Special Character] for details.
The rewritten batch code:
#echo off
echo/
set "BatchPath=%~dp0"
set "BatchPath=%BatchPath:\=/%"
:Begin
clS
echo/
set /P "Job=Enter number: "
if exist "%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe" goto program_files_x86
:program_files_x86
start "" "%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe" --disable-print-preview --ignore-certificate-errors --disable-web-security --user-data-dir --allow-file-access-from-files "http://%BatchPath%order.htm?order=%Job%"
goto end
:end
goto Begin
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.
cls /?
echo /?
goto /?
if /?
set /?
start /?
I use this script to find out the current folder with its .bat file:
for /f %%i in ("%0") do set curpath=%%~dpi
echo %curpath%
it doesn't work correctly, if the path contains spaces(D:\Scripts\All Scripts -> retrieves only D:\Scripts\, if I place in the folder, whose path doesn't have spaces it retrieves the full path). How can I fix it?
2015-03-30: Edited - Missing information has been added
To retrieve the current directory you can use the dynamic %cd% variable that holds the current active directory
set "curpath=%cd%"
This generates a value with a ending backslash for the root directory, and without a backslash for the rest of directories. You can force and ending backslash for any directory with
for %%a in ("%cd%\") do set "curpath=%%~fa"
Or you can use another dynamic variable: %__CD__% that will return the current active directory with an ending backslash.
Also, remember the %cd% variable can have a value directly assigned. In this case, the value returned will not be the current directory, but the assigned value. You can prevent this with a reference to the current directory
for %%a in (".\") do set "curpath=%%~fa"
Up to windows XP, the %__CD__% variable has the same behaviour. It can be overwritten by the user, but at least from windows 7 (i can't test it on Vista), any change to the %__CD__% is allowed but when the variable is read, the changed value is ignored and the correct current active directory is retrieved (note: the changed value is still visible using the set command).
BUT all the previous codes will return the current active directory, not the directory where the batch file is stored.
set "curpath=%~dp0"
It will return the directory where the batch file is stored, with an ending backslash.
BUT this will fail if in the batch file the shift command has been used
shift
echo %~dp0
As the arguments to the batch file has been shifted, the %0 reference to the current batch file is lost.
To prevent this, you can retrieve the reference to the batch file before any shifting, or change the syntax to shift /1 to ensure the shift operation will start at the first argument, not affecting the reference to the batch file. If you can not use any of this options, you can retrieve the reference to the current batch file in a call to a subroutine
#echo off
setlocal enableextensions
rem Destroy batch file reference
shift
echo batch folder is "%~dp0"
rem Call the subroutine to get the batch folder
call :getBatchFolder batchFolder
echo batch folder is "%batchFolder%"
exit /b
:getBatchFolder returnVar
set "%~1=%~dp0" & exit /b
This approach can also be necessary if when invoked the batch file name is quoted and a full reference is not used (read here).
for /f "delims=" %%i in ("%0") do set "curpath=%%~dpi"
echo "%curpath%"
or
echo "%cd%"
The double quotes are needed if the path contains any & characters.
Use This Code
#echo off
:: Get the current directory
for /f "tokens=* delims=/" %%A in ('cd') do set CURRENT_DIR=%%A
echo CURRENT_DIR%%A
(echo this To confirm this code works fine)