Is it possible to have conditional statements in batch scripts?
For example:
I have two servers, S1 and S2.When the batch file is deployed in S1 then the output should be generated in location L1. Likewise if the batch file is deployed in S2 then output should be generated in location L2.
My script:
set ComputerName=S1
set RepServer=%ComputerName%
set DBServer=%ComputerName%
set ReportPath="/DEV/Clearviewbilling"
set SharedPath=\\scottvdr1\ClearviewBilling\DEV-TEST
set UserId=-E
set fn=Create_Log.txt
if exist %fn% del %fn%
#echo on
#rem Reports
rs -i "%CD%"\Reports\Create_Sub.rss -s http://%RepServer%/reportserver -v Path=%SharedPath% -v rootpath=%ReportPath% -v DBServer=%DBServer% -t >> %fn% 2>&1
But i want the script to be:
set ComputerName=S1
set RepServer=%ComputerName%
set DBServer=%ComputerName%
If ComputerName=S1
Set SharedPath=//blah/blah
else
Set sharedPath=//some/path
set ReportPath="/DEV/Clearviewbilling"
set UserId=-E
set fn=Create_Log.txt
if exist %fn% del %fn%
#echo on
#rem Reports
rs -i "%CD%"\Reports\Create_Sub.rss -s http://%RepServer%/reportserver -v Path=%SharedPath% -v rootpath=%ReportPath% -v DBServer=%DBServer% -t >> %fn% 2>&1
Hence when the file is deployed, the reports are generated in the required path. But this is not working.
You compare the string ComputerName against S1 with the wrong if-else syntax
This should work
if "%ComputerName%"=="S1" (
Set "SharedPath=//blah/blah"
) else (
Set "sharedPath=//some/path"
)
Related
I am trying to query each instance on my CMS. But why the errorlevel is still 0 when I tested the stopped instance? And how can I skip the last line in my Instance_list.txt (The %%a will be '(44')
:Start
REM get the full list of our instance from CMS to Instance_list.txt
"C:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqlcmd" -S cms_instance -e -d msdb -Q "SELECT DISTINCT server_name FROM sysmanagement_shared_registered_servers_internal" -o Instance_list.txt
:Run
REM Run query against all instances one by one.
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,2 skip=3" %%a in (Instance_list.txt) do (
Set Intance_NAME=%%a
"C:\Program Files\Microsoft SQL Server\100\Tools\Binn\sqlcmd" -S !Intance_NAME! -e -d master -Q "SELECT ##SERVERNAME" -o SQL_result.txt
TIMEOUT /T 2
FINDSTR "!Intance_NAME!" SQL_result.txt
IF %ERRORLEVEL% NEQ 0 (
ECHO SQL INSTANCE !Intance_NAME! HAS SOME ISSUE...
)
)
This is a SQLCMD FAQ. You need to set the -b switch for sqlcmd to set the ERRORLEVEL
-b Specifies that sqlcmd exits and returns a DOS ERRORLEVEL value when an error occurs. The value that is returned to the DOS ERRORLEVEL
variable is 1 when the SQL Server error message has a severity level
greater than 10; otherwise, the value returned is 0. If the -V option
has been set in addition to -b, sqlcmd will not report an error if the
severity level is lower than the values set using -V. Command prompt
batch files can test the value of ERRORLEVEL and handle the error
appropriately. sqlcmd does not report errors for severity level 10
(informational messages).
https://learn.microsoft.com/en-us/sql/tools/sqlcmd-utility?view=sql-server-2017
When I run the below batch file code, it thinks that the server address is 'dbuser' and the Database is 'False'
What am I doing wrong?
Here is the code:
#echo off
set toolspath=C:\Program Files (x86)\Folder\Application
Set ApplicationServer=00.0.00.000
Set ApplicationDatabase=dbApplication
Set ApplicationUser=dbuser
Set ApplicationPassword=abcdefg
Set ApplicationUpgradeFromPre12_5=False
REM echo "Attaching dbApplication .. "
REM osql -S %ApplicationServer% -U %ApplicationUser% -P %ApplicationPassword% -i %ApplicationScriptsPath%\AttachApplicationDatabase.sql
REM osql -S %ApplicationServer% -U %ApplicationUser% -P %ApplicationPassword% -i %ApplicationScriptsPath%\SetDBVersion.sql
REM echo "Done."
call ApplicationUpgradeScripts.bat %%2 %ApplicationUser% %ApplicationPassword% %ApplicationDatabase% %ApplicationUpgradeFromPre12_5%
#if ERRORLEVEL 1 goto ERRORSEXIT
#Echo %ApplicationDatabase% Database Updated Successfully.
goto FINISH
:ERRORSEXIT
#Echo Error: Database Excecuted With Error. To Find out Exact Error see file ErrorLog.log
exit /B 1
goto ENDBUILD
:FINISH
#Echo All Databases Excecuted Successfully.
exit 0
:ENDBUILD
pause
I have a batch script which asks the user to input a server. Then it checks if it is from an array, where the servers require 3 password to login. Otherwise 2 passwords are needed. I check this in a for loop but always end up going to the else statement. Why? Here is the code:
#echo off
set arrayserver[0]=server1
set arrayserver[1]=server2
set arrayserver[2]=server3
set arrayserver[3]=server4
set arrayserver[4]=server5
set arrayserver[5]=server6
set arrayserver[6]=server7
set arrayserver[7]=server8
set arrayserver[8]=server9
set arrayserver[9]=server10
set arrayserver[10]=server11
set arrayserver[11]=server12
set arrayserver[12]=server13
set arrayserver[13]=server14
set arrayserver[14]=server15
set arrayserver[15]=server16
set arrayserver[16]=server17
set arrayserver[17]=server18
set arrayserver[18]=server19
set arrayserver[19]=server20
set arrayserver[20]=server21
set arrayserver[21]=server22
set arrayserver[22]=server23
set arrayserver[23]=server24
set arrayserver[24]=server25
set arrayserver[25]=server26
set arrayserver[26]=server27
set user2=user2
set user3=user3
set i=0
set /p server=Enter server:
for /F "tokens=2 delims==" %%s in ('set arrayserver[') do (
if "%server%" == "%%s" (
goto breakit
) else (
goto breakit2
))
:breakit
echo You will be prompted for 3 passwords:
echo -pass1
echo -pass2
echo -Oracle user
echo ssh -t -A %user2%#hop2 ssh -t -A %EDSUSER%#%server% su - oracle > C:\Temp\linkche.txt
"C:\Program Files (x86)\Putty\putty.exe" -ssh -A -t %USER%#hop1 -m C:\Temp\linkche.txt
del C:\Temp\linkche.txt
goto :oef
:breakit2
echo You will be prompted for 2 passwords:
echo -pass1
echo -Oracle user
echo ssh -t -A %user2%#hop2 ssh -A oracle#%server% > C:\Temp\linkche.txt
"C:\Program Files (x86)\Putty\putty.exe" -ssh -A -t %USER%#hop1 -m C:\Temp\linkche.txt
del C:\Temp\linkche.txt
goto :oef
Well, that's simple. You are in a loop but after checking the first server you are entering the ELSE case. So you are jumping out of the loop after the first iteration. You can verify this by entering server1. In this case the IF case is executed. Further, GOTO :oef makes no sense. I suppose you want to do GOTO :EOF. While EOF means EndOfFile, OEF means nothing (or possibly OfEndFile? :D).
To fix your problem you should modify your loop like this:
...
for /F "tokens=2 delims==" %%s in ('set arrayserver[') do (
if "%server%" == "%%s" (
goto breakit
)
)
goto breakit2
...
Now you will jump to breakit if the input is between server1 and server27 or to breakit2 otherwise. And don't forget to replace oef with eof.
You do not need to iterate over the array elements. If you define the array with the server values as subscripts with any value in the element (instead of numeric subscripts with the server in the value), then you may directly test if such server exist via a if defined arrayserver[%server%] ... command. I also use a simpler way to define the array.
#echo off
rem Define the server array with the server *in the subscript*; the assigned value does NOT matter
for %%a in (server1 server2 server3 server4 server5 server6 server7 server8 server9 server10
server11 server12 server13 server14 server15 server16 server17 server18 server19 server20
server21 server22 server23 server24 server25 server26 server27) do (
set arrayserver[%%a]=3
)
set user2=user2
set user3=user3
set /p server=Enter server:
if not defined arrayserver[%server%] goto passwords-2
:passwords-3
echo You will be prompted for 3 passwords:
echo -pass1
echo -pass2
echo -Oracle user
echo ssh -t -A %user2%#hop2 ssh -t -A %EDSUSER%#%server% su - oracle > C:\Temp\linkche.txt
"C:\Program Files (x86)\Putty\putty.exe" -ssh -A -t %USER%#hop1 -m C:\Temp\linkche.txt
del C:\Temp\linkche.txt
goto :eof
:passwords-2
echo You will be prompted for 2 passwords:
echo -pass1
echo -Oracle user
echo ssh -t -A %user2%#hop2 ssh -A oracle#%server% > C:\Temp\linkche.txt
"C:\Program Files (x86)\Putty\putty.exe" -ssh -A -t %USER%#hop1 -m C:\Temp\linkche.txt
del C:\Temp\linkche.txt
goto :eof
You may even define array elements with different values for 2 and 3 passwords, and then directly use their values to go to the appropriate section with no if command!
setlocal EnableDelayedExpansion
rem Servers with 3 passwords:
for %%a in (server1 server2 server27) do set arrayserver[%%a]=3
rem Servers with 2 passwords:
for %%a in (serverA serverB serverZ) do set arrayserver[%%a]=2
set /p server=Enter server:
goto passwords-!arrayserver[%server%]!
For further info about array management in Batch files, see this post.
I'm using SQLCMD to get the count of rows in a table, but I also want to be aware if the query hits an error.
The sqlcmd I'm using looks like this:
sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%"
If it works, it will return:
-----------
10205
(1 rows affected)
(Note, there is a blank line above the ------- for the column name I'm not specifying.)
If I pass in a table that doesn't exist, I get the following response:
Msg 208, Level 16, State 1, Server devServer, Line 1
Invalid object name 'dbo.no_table'.
Since I have the -b flag, I can check ERRORLEVEL for a value (in this case, 1).
To store the count variable, I've been using the following line:
for /F %%i in ('sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%" ^| findstr /r "[^(][0-9]"') do SET /a rec_count=%%i
After the for, %errorlevel% returns 0. Even inside the do, errorlevel is 0.
Is there any simple way to run sqlcmd, store the count if there is not an error, and print both lines if there is an error?
Commands that are executed by FOR /F are implicitly executed via a new CMD session. For example, with for /f %a in ('echo hello') do ..., the command that is executed becomes C:\Windows\system32\cmd.exe /c echo hello.
Your command is properly setting the ERRORLEVEL, but then the value is lost as soon as the child CMD session terminates and control is returned to your batch script.
So the /b option is not really doing any good for you, and can be dropped.
You can suppress the header info by adding the -h -1 option.
You can suppress the (1 rows affected) message by prefixing your command with set nocount on;
You can add the -r 1 option to cause error messages to appear on stderr instead of stdout. This will prevent FOR /F from processing any error, and the error message will appear on the screen instead.
You can clear the rec_count variable before you execute the command. Then it will remain undefined if there was an error, else it will contain the count if there was no error.
set "rec_count="
for /f %%A in (
'sqlcmd -S %server% -U %user% -P %pass% -h -1 -r 1 -Q "set nocount on;select count(*) from %table%"'
) do set "rec_count=%%A"
if not defined rec_count echo There was an error!
One other thing you might consider is using environment variables recognized by SQLCMD for your server, username, and password. Then you won't have to use the -S, -U, or -P options. This is especially handy if your batch script runs many SQLCMD commands.
set "sqlcmdServer=YourServer"
set "sqlcmdUser=YourUserName"
set "sqlcmdPassword=YourPassword"
set "rec_count="
for /f %%A in (
'sqlcmd -h -1 -r 1 -Q "set nocount on;select count(*) from %table%"'
) do set "rec_count=%%A"
if not defined rec_count echo There was an error!
The reason errorlevel does not seem to be getting set is because the for command is executing successfully, regardless of how the code that it loops through executes. So you can only interact with the errorlevel that is set by the sqlcmd command on the same line (inside the for loop brackets).
You should be able to use || (double pipe) after the sqlcmd command. Any code after || will only run if the previous command fails. Example:
notACommand || echo test
Will return "test". While the following will output only "a command":
echo a command || echo test
I can't test it, but something like the following should work for you:
for /F "EOL=(" %%i in ('sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%" ^|^| echo fail') do (
SET rec_count=%%i
)
if "%rec_count%"=="fail" echo SQL command failed
If the output is exactly as you say, then you should not need the findstr command - just set ( open bracket as an EOL character in the for loop, so you effectively drop the "(1 rows affected)" line. You will probably want to use the variables differently, but this is just one way you can tell if the sqlcmd command has failed or not.
As for outputting the error - a bad solution is to run the same sqlcmd command again. Something like the following:
set command=sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%"
for /F "EOL=(" %%i in ('%command% ^|^| echo fail') do SET rec_count=%%i
if "%rec_count%"=="fail" (%command%) else echo rec_count is %rec_count%
Note that I removed the /a switch when setting the rec_count variable, because it can now be set as a word.
Okay, a brief explanation of what I am doing: I use Windows Media Center (Windows 7) to record Jeopardy every evening. I then use Handbrake to convert the .wtv files to .mkv files and then transfer them to my NAS so I can watch them later using Plex Media Server/Center. Rather than doing this "by hand", I'm trying to automate the process using a batch file as a scheduled task. Initially, I had set up a script so that I could right-click > Send To > convert.bat and it would initiate the command-line interface for Handbrake and convert the file, move the output to my NAS, and delete the original file (worked great).
Now, what I'm doing is initiating the batch script as a scheduled task and looping through the contents of my "recorded tv" directory and looping through any .wtv files to convert/move/delete them.
The problem lies in the fact that Windows Media Center correctly names the Jeopardy files with the "!" in them (Eg: Jeopardy!_KHQ_2012_12_04_21_12_12.wtv), which completely bricks my script. The "Send To" batch file worked great, but when I loop through the *.wtv files in the directory, it returns all the filenames with the "!" stripped out which means I can't do squat with them. Files without "!" do process without a hitch.
Thanks in advance to anyone who can get me pointed in the right direction! (and if you happen to see any other areas where this script could be improved, that's fine too...)
Here is the basic code that I am attempting to use:
#echo off
setlocal ENABLEDELAYEDEXPANSION
SET count=0
SET getFolder=C:\Users\Public\Recorded TV\
SET ripFolder=C:\Rips\
SET putFolder=Z:\Videos\Recorded TV\
FOR %%F IN ("%getFolder%*.wtv") DO (
SET /A count=!count!+1
REM DETERMINE OUTPUT FILENAME
for /f "tokens=5,6,7,8,9,10 delims=\_" %%a in ("%%F") do (
set show=%%a
set station=%%b
set year=%%c
set month=%%d
set day=%%e
set hour=%%f
REM GENERATE OUTPUT NAMING CONVENTION
set output=!show! s!year!e!month!!day! !hour!
)
REM PROCESS WITH HANDBRAKE CLI
"C:\Program Files\Handbrake\HandBrakeCLI.exe" -i "%%F" -t 1 -c 1 -o %ripFolder%!OUTPUT!.mkv -f mkv --deinterlace="fast" --crop 58:60:2:2 --strict-anamorphic -e x264 -q 20 --vfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0 --gain=0 --audio-copy-mask none --audio-fallback ffac3 -x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0:8x8dct=0 --verbose=1
REM MOVE CONVERTED FILE TO NAS
copy "%ripFolder%!OUTPUT!.mkv" "%putFolder%"
REM DELETE ORIGINAL
del "%%F"
REM DELETE LOCAL RIP
del "%ripFolder%!output!.mkv"
)
echo %count% files processed
pause
ENDLOCAL
As you have recognized, the exclamation mark is stripped before you can escape it.
That's because you expand the FOR-loop variable %%F while delayed expansion is enabled, and the exclamation mark tries to expand a variable.
You need to toggle the delayed expansion here, as the variable contents are safe when using with delayed expansion, but to get the value you need the disabled mode.
#echo off
setlocal DisableDelayedExpansion
SET count=0
FOR %%F IN ("%getFolder%*.wtv") DO (
set "orgFile=%%F"
SET /A count+=1
REM DETERMINE OUTPUT FILENAME
for /f "tokens=5,6,7,8,9,10 delims=\_" %%a in ("%%F") do (
set show=%%a
set station=%%b
set year=%%c
set month=%%d
set day=%%e
set hour=%%f
setlocal EnableDelayedExpansion
REM GENERATE OUTPUT NAMING CONVENTION
set output=!show! s!year!e!month!!day! !hour!
)
REM PROCESS WITH HANDBRAKE CLI
"C:\Program Files\Handbrake\HandBrakeCLI.exe" -i "%%F" -t 1 -c 1 -o %ripFolder%!OUTPUT!.mkv -f mkv --deinterlace="fast" --crop 58:60:2:2 --strict-anamorphic -e x264 -q 20 --vfr -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0 --gain=0 --audio-copy-mask none --audio-fallback ffac3 -x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0:8x8dct=0 --verbose=1
REM MOVE CONVERTED FILE TO NAS
copy "%ripFolder%!OUTPUT!.mkv" "%putFolder%"
REM DELETE ORIGINAL
del "!orgFile!"
REM DELETE LOCAL RIP
del "%ripFolder%!output!.mkv"
endlocal
)