bat file to run sqlplus and then execute command based on result? - oracle

Can I have a BAT file run a SQLPlus command, and then based on the result, execute a CMD command, and then another SQLPlus command?
I think it would look something like this
CheckRowCount.SQL file
SELECT COUNT(*) FROM dmsn.ds3r_1xrtt_voice_trigger
BAT file:
sqlplus %USER%/%PASSWORD%#ORACLE #CheckRowCount.SQL
if ROWCOUNT < 1 goto :EX
c:\Autobatch\Spotfire.Dxp.Automation.ClientJobSender.exe http://SERVER/spotfireautomation/JobExecutor.asmx c:\AutoBatch\backup\Trigger_Test.xml
:EX
but I don't think this is exatcly right, ROWCOUNT doens't seem like it would work, also, how would I execute another SQLPlus command after the CMD.
I'm very new to BAT files and SQLPlus

Do yourself a favour and learn Powershell or python. I used to build crazy batch files to automate processes and they were painful.
Powershell is much, much better at building such conditional scripts, especially with the ability to create and monitor sub-threads. Python is great also for this.
The great advantage these bring is you can access the database (and the return results within your script to process and react to the data.
The disadvantage(?) is that you will need to learn one of these languages, but I personally do not see these as disadvantages at all, as they are much better than batch files.
Otherwise, foxidrive's answer should work for you.
Powershell example (kind of long, but the code can be reused):
function CreateConnection
{
param([string]$databaseHost
,[string]$Port
,[string]$SID
,[string]$UserID
,[string]$Password
)
process
{
$ConnectString = "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST={0})(PORT={1}))(CONNECT_DATA=(SID={2})));User Id={3};Password={4};"`
-f $databaseHost,$port,$SID,$UserID,$Password
write-host $connectString
sleep 10
$connection = new-object system.data.oracleclient.oracleconnection( $ConnectString)
return $connection
}
}
$DBMSHost="somehost.somedomain.com"
$DBMSPort=1521
$SID="somesid"
$UserName="user"
$Password="password"
[System.Reflection.Assembly]::LoadWithPartialName('System.Data.OracleClient') | out-null
try {
$Connection = CreateConnection $DBMSHost $DBMSPort $SID $UserName $Password
}
catch
{
write-host ("Could not access Oracle database {0}" -f $_.Exception.ToString())
exit
}
$Query = "SELECT COUNT(*) as rowcount FROM dmsn.ds3r_1xrtt_voice_trigger" // Added name to column to make life easier
$data_set = new-object system.data.dataset
$adapter = new-object system.data.oracleclient.oracledataadapter ($Query, $Connection)
[void] $adapter.Fill($data_set)
$table = new-object system.data.datatable
$table = $data_set.Tables[0] // We now have the actual resukts data
foreach ($row in $table)
{
if ($row.rowcount <1 )
{
$Application = "C:\Autobatch\Spotfire.Dxp.Automation.ClientJobSender.exe"
$Arguments = "http://SERVER/spotfireautomation/JobExecutor.asmx c:\AutoBatch\backup\Trigger_Test.xml"
$CommandLine = "{0} {1}" -f $Application,$Arguments
invoke-expression $CommandLine
}
}

Assuming the sqlplus command returns a simple number then this may work:
#echo off
for /f "delims=" %%a in ('sqlplus %USER%/%PASSWORD%#ORACLE #CheckRowCount.SQL') do set rowcount=%%a
echo.rowcount is set to "%ROWCOUNT%"
if %ROWCOUNT% GTR 0 (
c:\Autobatch\Spotfire.Dxp.Automation.ClientJobSender.exe http://SERVER/spotfireautomation/JobExecutor.asmx c:\AutoBatch\backup\Trigger_Test.xml
)
pause

Related

How to push a command to the function parameters from the command line arguments?

I want to write a function that runs a given command and outputs a given message if there are errors when running this command. Command and message are passed to the function as an arguments.
Actually, this small script is for the project "A Continuous Integration System" from the series "50 Lines or Less". Here in a project description, all shell scripts are written in bash but i have Windows and i want rewrite all shell scripts on powershell, for some practice.
This is code that i have now:
function rof{
param(
[string] $msg,
[scriptblock] $cmd
)
try {
Invoke-Command $cmd
}
catch {
$msg
}
}
I expected that when i run this function like
rof -msg "some error :(" -cmd {git log}
that command in {} will be invoked or "some error" will outputs if, for example there are no git repository.
But function outputs
git log
What am i doing wrong? I've never written in powershell before. Help me please. Thank you!
Treat PowerShell as just another shell, you don't need to use Invoke-Command to call a command. Just call it as is, since you have to pass the arguments of the command, using Invoke-Expression will be the best option here.
function rof{
param(
[string] $msg,
[string] $cmd
)
try {
Invoke-Expression $cmd
}
catch {
$msg
}
}
The scriptblock was successfully invoked from PowerShell's point of view, meaning there is no terminating error which triggers the catch. What you can do instead is checking the $LASTEXITCODE variable, which will contain the exit code from your external command.
function rof {
param(
[string] $msg,
[scriptblock] $cmd
)
& $cmd
if ($LASTEXITCODE -ne 0) {
$msg
$LASTEXITCODE
}
}
Example:
rof -msg "test" -cmd {cmd.exe /c exit 123}
test
123
However, let me add that this proxy function sounds like a bad idea. I don't know your exact use-case, but this will just invoke anything (very unsecure in a constrained PowerShell) and always returns a message that is defined by the caller. But how would the caller know, what the error might be...?

How to convert a Windows CMD Forloop in PowerShell

I have the following code in command shell code.
SET MYdir=%NewPath%\%CUST%\SuppliesTypes
SET "MYsCount=1"
SET /p MYsCount="Number of MYs in project? (default: %MYSCount%): "
for /L %%a in (1,1,%MYsCount%) do (
SET /p MYNums="Enter %%a MY Number: "
call md "%MYdir%\MY_%%MYNums%%"
)
SET "MYsCount="
However, I am converting my code from CMD to PowerShell. I do not fully understand the correct way to convert over. Here might be how it should be done, but it's not working as it just jumps right through.
SET MYdir=%NewPath%\%CUST%\Product
SET "MYsCount=1"
SET /p MYsCount="Number of MYs in project? (default: %MYSCount%): "
For ($MYsCount = 1; $MYsCount -eq 10; $MYsCount++){
SET /p MyNums="Enter %%a Product Numbers: "
CALL MD "%MYdir%\%CUST%\Product_%%"
}
SET "$MYsCount="
I've looked at the following sites and articles:
PowerShell Basics: Programming With Loops (Helped validate)
How to do a forloop in a Django template? (Didn't really help)
Windows PowerShell Cookbook, 3rd Edition (Page 170)
I am running this code inside a While-Loop.
Thanks for your help!
You have an interesting amalgam of batch file and powershell there in your second code block. It is hard to read when some things are one language and some things are another. Let's see if we can't get it all into PowerShell here.
$MYdir = "$NewPath\$CUST\Product"
$MYsCount = 1
$UserMYsCount = Read-Host "Number of MYs in project? (default: $MYSCount): "
If([string]::IsNullOrEmpty($UserMYsCount){
$UserMYsCount = $MYsCount
}
For ($i = 1; $i -le $UserMYsCount; $I++){
$MyNums = Read-Host "Enter $i Product Numbers: "
New-Item -Path "$MYdir\MY_$MyNums" -ItemType Directory
}
I believe the issue is coming from how you are declaring your variables. SET creates the variables as environment variables that powershell does not access natively. Below is how I would write up your section of code:
$MYDir = "$env:NewPath\$env:CUST\SuppliesTypes"
$MYsCount = 1
$MYsCount = read-host -prompt "Number of MYs in project? (default: $MYSCount): "
foreach ($a in 0..$MYsCount){
$MYNums = Read-Host -Prompt "Enter $a Product Numbers: "
New-Item -Path "$MYDir\MY_$MYNums" -ItemType Directory
}
$MYsCount = $null
I used a foreach loop instead of a normal for loop because you were incrementing by one each time and I have noticed a small performance gain from using foreach when the step is not complicated. 0..$variable is a short hand for using each number from 0 to the declared variable.
If you did want to use a for loop as you mentioned then you could use:
For ($MYsCount = 1; $MYsCount -eq 10; $MYsCount++){
as you had expected. This loop will only stop if the $MYsCount variable equals 10 though so if someone set the variable to something above 10 it would run indefinitely.

Why Windows generate a lot of process for a compiled Perl script?

I've written this script (called SpeedTest.pl) to log internet speed due to resolve a problem with my ISP.
It work well, but just if I use a Perl interpreter (if I double-click on the script). I want to compile it to generate a stand-alone executable to run in a different PC without Perl installed.
Well, I've try with pp and Perl2Exe both, but when I launch the SpeedTest.exe i see a lot of process called "SpeedTest.exe" in task manager. If I don't block all these process, the PC OS will crash (a pop-up say: "the memory can't be written, blah blah blah).
Any ideas?
This is the script:
#!/usr/local/bin/perl
use strict;
use warnings;
use App::SpeedTest;
my($day, $month_temp, $year_temp)=(localtime)[3,4,5];
my $year = $year_temp+1900;
my $month = $month_temp+1;
my $date = "0"."$day"."-"."0"."$month"."-"."$year";
my $filename = "Speed Test - "."$date".".csv";
if (-e $filename) {
goto SPEEDTEST;
} else {
goto CREATEFILE;
}
CREATEFILE:
open(FILE, '>', $filename);
print FILE "Date".";"."Time".";"."Download [Mbit/s]".";"."Upload [Mbit/s]".";"."\n";
close FILE;
goto SPEEDTEST;
SPEEDTEST:
my $download = qx(speedtest -Q -C --no-upload);
my $upload = qx(speedtest -Q -C --no-download);
my #download_chars = split("", $download);
my #upload_chars = split("", $upload);
my $time = "$download_chars[12]"."$download_chars[13]"."$download_chars[14]"."$download_chars[15]"."$download_chars[16]";
my $download_speed = "$download_chars[49]"."$download_chars[50]"."$download_chars[51]"."$download_chars[52]"."$download_chars[53]";
my $upload_speed = "$upload_chars[49]"."$upload_chars[50]"."$upload_chars[51]"."$upload_chars[52]"."$upload_chars[53]";
my $output = "$date".";"."$time".";"."$download_speed".";"."$upload_speed".";";
open(FILE, '>>', $filename);
print FILE $output."\n";
close FILE;
sleep 300;
my($day_check, $month_temp_check, $year_temp_check)=(localtime)[3,4,5];
my $year_check = $year_temp_check+1900;
my $month_check = $month_temp_check+1;
my $date_check = "0"."$day_check"."-"."0"."$month_check"."-"."$year_check";
my $filename_check = "Speed Test - "."$date_check".".csv";
if ($filename = $filename_check) {
goto SPEEDTEST;
} else {
$filename = $filename_check;
goto CREATEFILE;
}
Well, Steffen really answered this by way of a Comment, but here it is as an Answer. Just compile your Perl into an EXE that does NOT have the same name as the one that the Perl script is calling, for example:
speedtest.pl compiled into myspeedtest.exe, which calls speedtest.exe

On Windows, how can I kill all processes having files open in a directory?

One really frustrating "feature" of windows is that processes lock up files and prevent the removal of directories.
I'm looking for a way to locate all processes which have files open in that directory, something like "lsof" on unix. I'm looking for some powershell magic, and I'd rather not have to buy some custom maintenance tool.
So I have good news and bad news. I'll start with the bad news... I haven't found a way to close locked files strictly within PowerShell. The good news is that it can be done with a PowerShell script, and a little help from a free utility from SysInternals called Handle.exe. Here's the script I have on hand to do this:
Function Close-LockedFile{
Param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true)][String[]]$Filename
)
Begin{
$HandleApp = 'C:\sysinternals\Handle.exe'
If(!(Test-Path $HandleApp)){Write-Host "Handle.exe not found at $HandleApp`nPlease download it from www.sysinternals.com and save it in the afore mentioned location.";break}
}
Process{
$HandleOut = Invoke-Expression ($HandleApp+' '+$Filename)
$Locks = $HandleOut |?{$_ -match "(.+?)\s+pid: (\d+?)\s+type: File\s+(\w+?): (.+)\s*$"}|%{
[PSCustomObject]#{
'AppName' = $Matches[1]
'PID' = $Matches[2]
'FileHandle' = $Matches[3]
'FilePath' = $Matches[4]
}
}
ForEach($Lock in $Locks){
Invoke-Expression ($HandleApp + " -p " + $Lock.PID + " -c " + $Lock.FileHandle + " -y") | Out-Null
If ( ! $LastexitCode ) { "Successfully closed " + $Lock.AppName + "'s lock on " + $Lock.FilePath}
}
}
}
Now, if you want to do that for all files in a directory just get all the files, and run a ForEach loop on the file's FullName property. You should then be able to delete the directory without issues.
Edit: Here's the link to Handle.exe's web page: https://technet.microsoft.com/en-us/sysinternals/bb896655.aspx (thanks to Kev because I was too lazy to look it up myself.. Thanks Kev!)

Batch script or CMD for daily task on directory content by date

I admit I am a noob when it comes to CMD and bat scripting hence maybe my question has already been answered but I couldn't find it because I am unfamiliar with the terminology.
Basically I am currently running CMD to create a txt file for a directory content, that works fine but I would like to improve this process and started to look into a batch file to run this for multiple directories and by date but only get confused with the commands.
I would really appreciated if you maybe show me the right direction to look up the possible commands. Here is was I am basically trying to achieve:
Scan Directory 1, create log file with all content (filename) with modification of date DDMMYYYY and save under Directory 1 (existing on Desktop)
Repeat above for Directory 2, 3, 4 etc.
Now I am not sure how to approach this and where to start. It looks so simply yet I am have not managed to get to work.
assuming you are on Vista or better and your date format is dd/mm/yyyy:
for /d %%a in ("%userprofile%\Desktop\Directory*") do (
for %%b in ("%%~fa\*") do (
set "fname=%%~fb"
for /f %%c in ("%%~tb") do set "fdate=%%c"
setlocal enabledelayedexpansion
echo !fname! !fdate:/=! >> "%%~fa\LOGFILE.TXT"
endlocal
)
)
First of all your batch script to parse current date-time will be locale specific. As long as you do not plan to use it on non-US Windows it will be fine. My solution was to use simple VBS script to generate current timestamps
So code of my batch file looks like
#echo off
call GetToday.bat
call %TEMP%\SetToday.bat
SET LOGFILE=Log.%TODAY%.log
echo %LOGFILE%
Use your log here
GetToday.bat:
#echo off
set TOOLS_HOME=%~dp0
cscript /NoLogo %TOOLS_HOME%\Today.vbs >%TEMP%\SetToday.bat
call %TEMP%\SetToday.bat
Today.vbs:
Dim d
d = Now
WScript.Echo "SET TODAY=" & Get2Digit(Year(d)) & Get2Digit(Month(d)) & Get2Digit(Day(d))
Function Get2Digit(value)
if 10 > value Then
Get2Digit = "0" & value
Else
Get2Digit = Right(value, 2)
End If
End Function
However given Today.vbs generates today date in form YYMMDD. From my experience such suffixes are much more useful, you could just sort you files by name to find specific date
In PowerShell something like this should work:
$folders = 'C:\path\to\Directory1', 'C:\path\to\Directory2', ...
$refDate = (Get-Date '2013-05-27').Date
$recurse = $false
foreach ($d in $folders) {
$output = Join-Path $d 'filelist.txt'
Get-ChildItem $d -Recurse:$recurse | ? {
-not $_.PSIsContainer -and $_.LastWriteTime.Date -eq $refDate
} | % { $_.Name } | Out-File $output
}
If you want to recurse into the subfolders of the scanned folders you need to change $recurse to $true and perhaps change $_.Name to $_.FullName, so you get the filename with the full path instead of just the filename.

Resources