I've made a program which needs Administrator privileges to copy itself to another folder. That's the reason i've added this (i found it on stackoverflow) to the beginning of my program:
#echo off
:: BatchGotAdmin
:-------------------------------------
REM --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
:--------------------------------------
However after the program is copied to the folder i do not want this code anymore because it isn't required and it will still give pop ups.
My question is: Can this code somehow be deleted after one use of the program?
All the code has to be done in the same batch file.
Thank you!
Instead of copying the file to the new directory and deleting parts of the code afterwards, how about this:
Start your batch file, check for admin and if successful "write" the content to the destination directory simply skipping the lines you want to delete.
Something like this:
#echo off
SET targetfile=C:\new\folder\targetfile.bat
:: BatchGotAdmin
REM your admin code here
:--------------------------------------
ECHO 1st line of your code>%targetfile%
ECHO 2nd line of your code>>%targetfile%
ECHO 3rd line of your code>>%targetfile%
REM ... your code ...
ECHO last line of your code>>%targetfile%
This way you will "create" the file in the target location.
Another option is to use a FOR /F loop to walk through your file, copy it line by line into a temp file, skip the "admin check" part, delete the the original file and rename the new file to the old name. This is also possible but it's really ugly. If you prefer this solution, tell me and I'll post some code. But beware! You won't like it! :D
Related
I'm writing a script on a BAT file to use when necessary, to backup a folder of an application on several computers.
This script works on Windows 7: will it also work on Windows 10?
:: Backup script with logging
#echo off
net use \\SERVER\Shared_Folder userPassword /USER:userName
set PATH=c:\WINDOWS\system32;
set SRC="C:\Program Files (x86)\ApplicationName\TargetFolder"
set DST=\\SERVER\Shared_Folder\Backups
set LOG=%DST%\Backup_LogFile.log
echo:>>%LOG%
echo Backup from computer %COMPUTERNAME% >>%LOG%
echo Starts -- %DATE% %TIME% >>%LOG%
echo Wait please: backup is running...
xcopy %SRC% %DST%\%COMPUTERNAME%\ /A /D /E /J /Y /Z>>%LOG%
echo Ends -- %DATE% %TIME% >>%LOG%
echo:>>%LOG%
My script works fine but I want a better response on terminal for the user than execute it.
The script adds correctly the actions on a log file, but I want the user can see only the number of file copied not the list of all files copied.
Here is one way to accomplish what you ask. There are other ways too. The secret here is using "for /F" and sending each result to another function. The other function will log each line to a file. It will then look for xcopy's "File(s) copied" line and pipe that to the user if it sees it.
Also... note the "goto :EOF" statements. These tell the batch interpreter to return to the caller much like any other programming language.
I hope this does what you are asking. :)
:: Backup script with logging
#echo off
net use \\SERVER\Shared_Folder userPassword /USER:userName
set SRC="C:\Program Files (x86)\ApplicationName\TargetFolder"
set DST=\\SERVER\Shared_Folder\Backups
set LOG=%DST%\Backup_LogFile.log
echo:>>%LOG%
echo Backup from computer %COMPUTERNAME% >>%LOG%
echo Starts -- %DATE% %TIME% >>%LOG%
echo Wait please: backup is running...
for /f "delims=" %%f in ('xcopy %SRC% %DST%\%COMPUTERNAME%\ /A /D /E /J /Y /Z') do call :log_items "%%f"
echo Ends -- %DATE% %TIME% >>%LOG%
echo:>>%LOG%
goto :EOF
:log_items
Set InputLine=%~1
:: Log everything
echo %InputLine%>>%LOG%
:: Check if the line coming in contains "File(s) copied" if it doesn't, return
if "%InputLine:File(s) copied=%"=="%InputLine%" goto :EOF
:: If it does, show it to the user and return
echo %InputLine%
goto :EOF
The comparison done for the files copied looks like this:
For a line with your file name: (here they match so it returns)
C:\git\ps>if "test\targetver.h" == "test\targetver.h" goto :EOF
For a line with your number of files: (here they dont match do it doesn't return)
C:\git\ps>if "205 " == "205 File(s) copied" goto :EOF
I want to run a script
If it is, it adds the location of the script to %PATH% environment variable.
If it is not, it elevates, and then runs itself, to do the thing above.
But no matter what I do, I either get an empty window that is elevated, my code running that not elevated, my code running elevated but in System32 and therefore with wrong %cd%.
The code on the right (different form pastebin at the orange marker) is run in the correct dir, it opens a UAC prompt, and an elevated terminal opens (bottom) with wrong path
https://i.imgur.com/vU0rAcN.png "Original problem"
I think the problem is at the orange marker in image 1, I have tried replacing it with %~dp0%0 and it worked! But, only if run from a cmd terminal, not as a "double click" form explorer
Side note, I expected %~dp0 to return the full path of the file, but it only returned the dir.
https://i.imgur.com/z8VNawM.png "My atempt at fixing it"
My code on pastebin
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"="
echo UAC.ShellExecute "cmd.exe", "/c %~dp0%0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
echo %cd%
pause
I have removed the add to path part in an atempt to isolate the problem, and prevent myself, and others from adding junk to path:)
I expected the original path to be passed down to the elevated window. Via .vbs, but it didn't. And when it did, it couldn't from explorer.exe
For the life of me, I can't figure out why the below set prompt won't work when it is in the if statement:
#echo off
REM :askdeletecsvs
if exist *.csv (
echo Warning! All files in the scripts folder that have the "CSV" extension will be deleted!
echo Answering "n" will continue the script without deleting the CSVs.
set /p ASKDELETE=Delete CSVs? (y/n):
REM
REM if ( /i %ASKDELETE% equ "y" goto :deletecsvs )
REM if ( /i %ASKDELETE% equ "n" goto :runscripts )
REM goto :askdeletecsvs
)
When I run the batch file as it is above the cmd window opens and then shuts quickly. If I move the set line outside of the if statement then the prompt shows as expected. (There are csvs in the folder the bat file is running from)
What am I missing?
To start with you had used a closing parenthesis which was prematurely ending your opening If parenthesis.
I'd suggest reversing the thinking:
If Not Exist *.csv GoTo runscripts
Echo Warning!
Echo All files in the scripts folder that have the "CSV" extension will be deleted!
Echo Answering "N" will continue the script without deleting the CSVs.
Choice /M "Delete CSVs"
If ErrorLevel 2 GoTo runscripts
:deletecsvs
Del /F /Q /A "PathTo\scripts\*.csv"
GoTo :EOF
:runscripts
You can change GoTo :EOF to a relevant valid label as necessary or remove it if you want to continue on to :runscripts. You can also replace PathTo\scripts\ with %~dp0 if the batch file is running from the scripts directory, or remove PathTo\scripts\ if the current directory holds those files. (note that the current directory and batch file path may not necessarily be the same)
this is my first question thing so dont judge me too hard.
I'm trying to make a batch file that inputs the PC name, User, date and time into a text (.txt) file. I want/need It to have multiple logs and only In on file.
(the text file will be in the same directory)
Here is what I have done so far and it seems to work for the first 2 (you'll see what I mean) but then it makes another file called 1. I dont know if its my laptop or if its my lack of any coding skill.
#echo off
:make1
if exist "1.txt" goto make2
set SomeVar=%ComputerName% %UserName% %date% %time%
echo %SomeVar% > "1.txt"
exit
:make2
if exist "2.txt" goto make3
ren "1.txt" "2.txt"
echo %SomeVar% >> "2.txt"
exit
:make3
if exist "3.txt" goto make4
ren "2.txt" "3.txt"
echo %SomeVar% >>> "3.txt"
exit
:make4
if exist "4.txt" goto make5
ren "3.txt" "4.txt"
echo %SomeVar% >>>> "4.txt"
exit
:make5
if exist "5.txt" goto make6
ren "4.txt" "5.txt"
set SomeVar=%ComputerName% %UserName% %date% %time%
echo %SomeVar% >>>>> "5.txt"
exit
:make6
set SomeVar=%ComputerName% %UserName% %date% %time%
echo %SomeVar% >>>>>> "6.txt"
pause
exit
I know the last one will be overwritten but i can be bothered adding heaps
(also if you have a way of making it infinite it would be greatly appreciated.)
Im making most of my stuff during class at school and I first got into batch about a year ago so sorry for the bad/crude/excessive/annoying code
Thanks, ThePolarBear
> mean overwrite entire line
>> append to last line
you don't need to put >>>>>
#echo off
setlocal
:make1
set next=1
if exist "1.txt" goto makeNext
set SomeVar=%ComputerName% %UserName% %date% %time%
echo %SomeVar% > "1.txt"
exit
:makeNext
set /A next+=1
if exist "%next%.txt" goto makeNext
set /A prev=next-1
ren "%prev%.txt" "%next%.txt"
set SomeVar=%ComputerName% %UserName% %date% %time%
echo %SomeVar% >> "%next%.txt"
exit
The cause of the error is the multiple >>>.... Use one > to create a new file. Use two >> to append data to an existing file, or create it if it does not exists.
The setlocal command automatically deletes the variables created in this Batch file when it ends, like SomeVar. This is a convenient feature in this case. The use of setlocal is required in certain advanced programs (related to "Delayed Expansion").
If you have any questions about this code, please ask. However, you should look first at the help of the related command, for example: set /?; otherwise, I will reply: "See the help of such a command" for simple questions! ;)
I wanna make a batch file which prompts for admin rights, but not just that (read until the end before posting pls). Normally i create a vbs script via the batch file which will prompt for admin rights, but the problem is if the user chooses "no" in the UAC window, the batch file won't run, and i want it to run anyway (with or without admin privileges). My code (wrong/incomplete) :
#echo off
if "%1" == "S" goto skip
::checks if the user already have admin rights, in this case no prompt needed
net file 1>nul 2>nul
if '%errorlevel%' == '0' goto skip
::create vbs script from which the batch file should run
echo Set UAC = CreateObject^("Shell.Application"^) > runasAdmin.vbs
echo UAC.ShellExecute "%~0", "S", "", "runas", 1 >> runasAdmin.vbs
start "" runasAdmin.vbs
::here, a code should check if the batch file is running as admin in another process
::if not, goto skip
exit /b
:skip
::commands and stuff...
The problem with checking if the process is running as admin is that a non admin will not be able to see the admin process. i.e. admin processes are protected from non elevated eyes. So, that won't work.
ShellExecute returns a HWnd when you code against and this value can be used to check for errors, but of course that doesn't work with the shell.application com object either. So, you can't check the return and do a nice WScript.Quit(err value) for the bat to check.
Using start "" runasAdmin.vbs means that your bat file goes straight to the next line, instead of waiting until the UAC prompt has finished. Using start /wait or even simpler cscript -nologo ensures that the bat waits until the prompt has been answered.
So, you can't check the success/failure of the elevated process creation with VBS nor by checking processes. All that I can think of is to use a temp file creation as a locking mechanism. To make this work, the elevated process needs to create a temp file that the non elevated process checks for, so a small sleep in each is required (achieved with a localhost ping).
#echo off
setlocal
rem Note: Any user entered parameter will run unelevated
set runningFile=%1
if not defined runningFile goto checkElevation
echo Running as admin > %runningFile%
goto skip
:checkElevation
rem checks if the user already have admin rights, in this case no prompt needed
net file 1>nul 2>nul
if '%errorlevel%' == '0' goto skip
rem create vbs script from which the batch file should run
set filename=%tmp%\RunAsAdmin%random%
set runningFile="%filename%.lck"
set vbscript="%filename%.vbs"
echo Set UAC = CreateObject^("Shell.Application"^) > %vbscript%
echo UAC.ShellExecute "%0", %runningFile%, "", "runas", 1 >> %vbscript%
cscript -nologo %vbscript%
rem sleep 1 to allow elevated version to create lock file
ping -n 2 127.0.0.1 > NUL
del %vbscript%
rem check if running file has been created
if not exist %runningFile% goto skip
if exist %runningFile% del /f %runningFile%
exit /b
:skip
rem sleep 1 so that non elevated version can check lock file created
ping -n 2 127.0.0.1 > NUL
echo Running other things here %1
if exist %runningFile% del /f %runningFile%
pause
rem commands and stuff...
I optimize this code below:
#echo off
#title
#NET FILE 1>nul 2>nul
#if %ERRORLEVEL% neq 0 (
#if not defined UAC (
#set UAC=1;
) else (
#echo.
#echo UAC elevating failed !
#echo.
#pause
#exit
)
#echo CreateObject^("Shell.Application"^).ShellExecute "%~f0", "%*", "%~dp0", "runas", 1 > %temp%\UACtemp.vbs
#%temp%\UACtemp.vbs
#del /f /q %temp%\UACtemp.vbs
#exit
)
#cd /d %~dp0
#echo on
::command under this line