Script Multiple SQLServer Studio Manager Jobs and Divide Into Scripts Via Powershell

SQL Server Management Studio can script jobs.  Recently a colleague asked if we could script all the jobs at once.  Actually, using SSMS, we can script all the jobs or even selected jobs.

You may have noticed that SSMS does not allow selecting multiple jobs within the expanded jobs section of Object Explorer.  You may select jobs, however, within the Object Explorer Details view.  Select Object Explorer Details.  When the Object Explorer Details window opens.  Select the desired jobs.  You can then right-click and chose to script the selected jobs to a new Query Editor Window, a file or the clipboard.

Once you have a file with the combined jobs in it, you can split the job scripts into separate files via a PowerShell script by keying on the comment “/***** Object: Job”.  The file names can even be picked up from the comments.

 

PowerShell Script to Break Up the Files

# Split file into multiple files when you see the comment “/****** Object: Job” followed by the job name in square brackets

$filename=”C:\Users\andrea\Documents\JobScripts\All-Jobs-Dev.sql”

$OutputDirectory= “C:\Users\andrea\Documents\JobScripts\” # Notice the trailing slash

# Create the path if necessary. You can force (-force) the creation if you want to create additional directory layers. But -force may recreate existing directories.

if (! (Test-Path $OutputDirectory)) {
New-Item -Path $OutputDirectory -ItemType Directory
}

# Our job scripts have embedded server names. Sometimes more than one version we might need to change.

$existingServer=@(‘dev-dbsrvr.mydomain.edu’,’dev-host.cri.mydomain.edu’)
$newServer=’qa-dbsrvr.mydomain.edu’

# And SSIS package environment references change between environments.

$existingenvref=@(‘ENVREFERENCE 16′,’ENVREFERENCE 15’)
$newenvref=@(‘ENVREFERENCE 1’, ‘ENVREFERENCE 2′)

# Read in the file with all the jobs.

$filecontent= get-content $filename

# The first file will just contain USE MSDB from the top of the file.

$newfile=$outputDirectory+’UseMSDB.sql’

# Loop over all the lines in the file replacing server names and environment references as needed.

ForEach ($line in $filecontent) {

for ($i=0; $i -le 1; $i++) {
$line=$line.replace($($existingServer[$i]),$newServer)
}

for ($i=0; $i -le 1; $i++) {
$line=$line.replace(“$($existingenvref[$i])”,”$($newenvref[$i])”)
}

# When you see “***** Object: Job” a new job is starting.
If ($line -match “^/\*\*\*\*\*\* Object: Job “) {

$JobName = [regex]::match($line,’\[([^\)]+)\]’).Groups[1].Value

# Create a new file named CreateJob_ Followed by the job name and ending in .sql

$newfile = $outputDirectory+”CreateJob_”+$JobName+”.sql”

Write-Host Creating file $newfile
‘USE MSDB’ | Out-File -Append $newfile
‘GO’| Out-File -Append $newfile
$line | Out-File -Append $newfile
}
Else {
$line | Out-File -Append $newfile
}
}

Linux Cron Entry to Restart after the 4th Tuesday of the Month

The crontab entry below executes the script  BounceORACLE_SID.ksh after the 4th Tuesday of the month.

01 00 23-29 * * eval [ `date +\%w` -eq 3 ] && ${HOME}/_DBA/SCRIPTS/START_STOP_LSNR_and_DB/BounceORACLE_SID.ksh ORAPRD > ${HOME}/_DBA/SCRIPTS/START_STOP_LSNR_and_DB/LOGS/BounceORACLE_SID_$$_`date +\%Y.\%m.\%d..\%H.\%M.\%S`.log 2>&1

Unpacked the entry says:

Test, [], the evaluation, eval, of the value returned after execution of the command date +%w in the grave accents (“ with an escaped percent sign) 01 minute after 00 (midnight) on the 23rd to the 29th day of every month.  If the day is Wednesday, that is, the return value is 3, the second part of the command string (following the double ampersands, &&) will be executed.

The earliest 4th Tuesday would occur if the first Tuesday is the first.  The next two Tuesdays would then be the 8th and the 15th.  The fourth Tuesday would occur on the 22nd.  The bounce occurs at midnight after the (potential) maintenance period.  The latest Tuesday would occur if Wednesday was the 1st.  Then, the Tuesdays would be the 7th, the 14th, the 21st and the 28th.  So every month the fourth Tuesday is between the 23rd and the 29th.  And only one of those dates is a Wednesday.

Bye,

Find listeners in listener.ora from ORACLE_HOME

I wrote my command line processing ksh script as part of a generalized adrci error checker.  One of the things I would like my error checker to do is check for listeners. I have a variety of ways I can do this:  use ps -elf | grep lsnr to find running listeners.  Use adrci to look for listeners under the ORACLE_BASE of my ORACLE_HOME directories (from /etc/oratab or the Oracle inventory).

I want to allow a specific listener to be chosen.  This is a bit of a problem for me.  I have three listeners and they run from three different Oracle homes.  I can probably deal with this.  I should have a list of available Oracle homes in /etc/oratab.  If I input a listener name, I can check all the listed homes until I find the right one for the listener.  Or I can check all the homes to find all the listeners.

But I have to grep $ORACLE_HOME/network/admin/listener.ora to find the listeners.  And I have to be careful. For example, I want

  • LISTENER=
  • LISTENER[TAB] [TAB]  =
  • LISTENER_ABC=
  • [TAB]LISTENER_ABC = # My LISTENER_ABC comment

etc.

But I do not want

SID_LIST_LISTENER_ABC =

# LISTENER=

or their kin.

I tried a lot of things that did not work and eventually went with the following.

$ grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=” $ORACLE_HOME/network/admin/listener.ora
LISTENER_ABC =

The regular expression means find all lines that start with listener followed by an optional underscore, then optional alphabetic characters, then optional blanks (tab or spaces) and an equal sign.

Here are some additional tests.

echo “LISTENER = ” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER =

$ echo “#LISTENER_ABC=” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”

$ echo “LISTENER_ABC=” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC=

$ echo “LISTENER_ABC =” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC =

$ echo ” LISTENER_ABC =” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC =

$ echo ” LISTENER_ABC =” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC =

$ echo ” LISTENER_ABC = # My LISTENER_ABC comment” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC = # My LISTENER_ABC comment

$ echo “SID_LIST_LISTENER_ABC = ” | grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=”

I should now be able to extract a list of listeners from my listener.ora files.

I made a fake file (because my listener.ora files all have one listener).

LISTENER_ABC =
LISTENER_XYZ =
LISTENER_N =

Notice that the file contains some tabs.

$ grep $’\t’ listener.ora
LISTENER_XYZ =
LISTENER_N =

The command below is actually all I need to create an array (in ksh) of listeners from a listener.ora file.

$ set -A LISTENERS `grep -i “^[[:blank:]]*listener_*[[:alpha:]]*[[:blank:]]*=” listener.ora | cut -d’=’ -f1`
$ echo ${LISTENERS[*]}
LISTENER_ABC LISTENER_XYZ LISTENER_N

Notice that as a bonus I lost the blank characters.  (Index 2 in the array is the third element LISTENER_N).

$ echo ${LISTENERS[2]} | grep $’\t’

I tried to use word boundaries in grep (/b is the start of a word), but could not get the “not proceeded by a #” into the word.  I had trouble with the trailing equal sign with word boundaries too.  My word got too long and had non-word characters.  Since I did not want to specify a word boundary at the end of the word, it was challenging.

Arguably, I could look for just 0 or 1 underscores, but I am not certain if Oracle lets me start a listener name with an underscore.

$ echo “LISTENER_ABC=” | grep -i “^[[:blank:]]*listener_\{0,1\}[[:alpha:]]*[[:blank:]]*=”
LISTENER_ABC=
$ echo “LISTENER__ABC=” | grep -i “^[[:blank:]]*listener_\{0,1\}[[:alpha:]]*[[:blank:]]*=”

I really need to use use the alphanumeric character class and possibly allow additional underscores in the name.

$ echo “LISTENER_ABC_123=” | grep -i “^[[:blank:]]*listener_\{0,1\}[[:alnum:]_]*[[:blank:]]*=”
LISTENER_ABC_123=

I added LISTENER_123_B to my fake listener.ora file to show that the revised search string matches numeric listeners with additional underscores.

$ set -A LISTENERS `grep -i “^[[:blank:]]*listener_\{0,1\}[[:alnum:]_]*[[:blank:]]*=” listener.ora | cut -d’=’ -f1`
$ echo ${LISTENERS[*]}
LISTENER_ABC LISTENER_XYZ LISTENER_N LISTENER_123_B

Bye.

Ksh command line processing

I have written a lot of ksh scripts as a DBA.  I have always wanted to process the script parameters as keyword pairs (like Oracle commands allow).  But I hate to give up allowing a simple parameter or two in a specified order.  The following script actually assigns values to parameters based on either keywords or in a default order.  You can even mix keywords pairs with the default order.

eval lets me assign the keyword pairs. I love that.  Notice that debug=TRUE is used in the sample.  Even though debug is not specified in the order list, debug can be set on the command line.  Any keyword=value on the command line will set the environment variable keyword to the specified value.

We also need to work with some ksh arrays.  I tried hard to delete the keyword from parmNames but could not make that work.  I used a new array.

Even if the script is overkill for my little scripts, it uses a variety of techniques which might be interesting.

In this example usage, the default order of parameters is x y z.  But y is set explicitly as a keyword pair in the first position.  It is removed from the ordered list and x and z are then set to the next two parameters.  And debugging (yes, it is pitiful) is turned on with debug=TRUE.  The debugging text 0: x 0 and 2: z 0 shows that x and z are not keyword pairs while y (1: y 1) is a keyword pair.  Y is removed from the ordered parameter list leaving x and z to be set.  Argument 2 (2nd position on the command line) is set to x, and Argument 3 on the command line is set to z.  The final debugging text dumps x, y, and as 2 1 3.

Sample call

 

Code:

#! /bin/ksh
#set -x

main() {
# Create a list with the default option order

typeset -a parmNames=(x y z)
typeset -a keepList=()

allArgs=”$@”

processCommandLine
}

processCommandLine() {
# set allArgs to the command line parameters with allArgs=”$@”
# And set pramNames to an ordered list of allowed parameters with parmNames=(x y z)

# Command line parameters are expected to be in the format x=123, y=ABC, 123 or ABC
# Spaces may not be tolerated even when quoted as subsequent commands may remove the quotes.

# The default behavior is to create variables from the argument list in the order specified in parmNames
# But keyword pairs x=1, y=abc, z=1.2 can be embedded in the argument list as well
# If you mix keyword pairs with ordered parameters, the parameters will be populated from the non-keyword-pair values
# in the order specified once all keyword pair parameters are removed.

# Check all command line arguments to see if debug is set.

for arg in $allArgs
do
debugCheck=`echo $arg | grep -i debug= | wc -l`
if [ debugCheck -gt 0 ]
then
debug=`echo $arg | cut -d’=’ -f2`
typeset -u debug
else
debug=FALSE
fi
done

if [ $debug != FALSE ]
then
echo “debugging $debug”
echo Looping over parms…
fi

i=0

# Look over all the parameter names to find those that are part of a keyword pair on the command line (must have an = sign and text on either side of the = sign.

for parmName in ${parmNames[*]}
do
# Check if the command line contains the current parameter name

specifiedCount=`echo $allArgs | grep “${parmName}=” | wc -l`

if [ $debug != FALSE ]
then
echo $i: $parmName $specifiedCount
fi

# If the current parameter name is not part of a keyword pair, keep it as an ordered parameter.

if [ specifiedCount -eq 0 ]
then
keepList+=(${parmName})
fi

i=`expr $i + 1`

done

if [ $debug != FALSE ]
then
echo Ordered parameter list is ${keepList[@]} after removing keyword pairs.
fi

# Restart the index on the command line arguments

i=0

for arg in $allArgs
do
if [ $debug != FALSE ]
then
echo processing $arg …
fi

keyword_check=`echo $arg | awk -F= ‘{print NF}’ -`

if [ $debug != FALSE ]
then
echo keyword check: $keyword_check
fi

if [ $keyword_check -gt 1 ]
then
# Assign the option
eval $arg
else
if [ $debug != FALSE ]
then
echo argument: $arg assign to parmName ${keepList[$i]}
fi

eval ${keepList[$i]}=$arg

i=`expr $i + 1`

fi

done

if [ $debug != FALSE ]
then
for parmName in ${parmNames[@]}
do
eval echo \$$parmName
done
fi

}

# start script
main “$@”

 

$  . ./test.ksh y=1 2 3 debug=TRUE
debugging TRUE
Looping over parms…
0: x 0
1: y 1
2: z 0
Ordered parameter list is x z after removing keyword pairs.
processing y=1 …
keyword check: 2
processing 2 …
keyword check: 1
argument: 2 assign to parmName x
processing 3 …
keyword check: 1
argument: 3 assign to parmName z
processing debug=TRUE …
keyword check: 2
2
1
3

I use allArgs instead of continuing to use $@ throughout the script as some commands replace $@.

Bye.