Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Korlac
Nov 16, 2006

A quintessential being known throughout the Realm as the 'Dungeon Master'. :rolldice:
So I've been using powershell for the last 2 years and I've been getting progressively better at it. Helped that Exchange 2010 had a major bug that prevent you from using the console if you authenticated to the machine with creds in a cross forest (The console is built on top of powershell and up until RU6 for Exchange 2010, the console didn't know to check cross forest DCs for authentication).

I've written fairly simple scripts to perform certain functions like MRM policy migrations that perform a pre and post migration report.

code:
$date = get-date -format yy-mm-dd
$inputfile = read-host -prompt "input file name in .txt format"
$Data = gc $inputfile
$location = read-host -prompt "Please type user location, either 'EU' or 'NA'"
$beforefile = $date + $location + "before.txt"
$afterfile = $date + $location + "after.txt"
$Data | Get-Mailbox | FT -au displayname,alias,*primary*,*managed* >$beforefile
$Data | Set-Mailbox -ManagedFolderMailboxPolicy "GSKDefaultMailboxPolicy" -ManagedFolderMailboxPolicyAllowed
$Data | Get-Mailbox | FT -au displayname,alias,*primary*,*managed* >$afterfile
But I've got to the point where I want to take my powershell scripting to the next level, yet I'm having a hard time wrapping my head around the { } parts of most scripting, particularly how I should be using them. I know what they do, but there's a logical way they are being used that's not sticking to my grey matter. For example...

code:
Function Example 
    {
    write-host "Function has been run"
    }

Example
I understand logistically that this will create a functional command called "Example" and once you actually use the command "Example" it will then write "Function has been run" onto the screen. But, I'm not certain I understand why the {}'s are separated out to their own lines. Are they just logically separating out the command? I ask because this is how I normally do this stuff?

code:
Function Example2 { write-host "Function has not been run!" }
So I don't know if I'm just making life harder on myself or not.

Adbot
ADBOT LOVES YOU

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
The {} are there, because a function can contain multiple lines, and PS needs a way to delimit this.

code:
Function Example2 { write-host "Function has not been run!" }
works exactly the some as
code:
Function Example2 
   { 
   write-host "Function has not been run!"
   }
With one line, the benefits aren't apparent. But, if you have multiple lines and indent like that, it is easier to see where a function ends. I personally like to put the brackets at the same amount of indentation, some people put the opening bracket right after the function, like so:

code:
Function Example2 { 
   write-host "Function has not been run!"
   }
It gets even more interesting when things are nested
code:
Function Example2 { if ($Statement -eq $true)   {write-host "Function has not been run!"} else {write-host "Error"}   }
is less readable than
code:
Function Example2 
   { 
   if ($Statement -eq $true)   
       {
       write-host "Function has not been run!"
       } 
   else 
       {
       write-host "Error"
       }
   }
e: Can't spell. Also, if you are really interested, read http://en.wikipedia.org/wiki/Indent_style. Pick one that seems clear to you and stick with it.

Jelmylicious fucked around with this message at 17:23 on Jan 5, 2012

Wicaeed
Feb 8, 2005

Mario posted:

What if the 1st is on a Monday? This would match Tuesday and Wednesday as well.

How about (again, not tested):
code:
$info = Get-Item butts.txt
if(($info.CreationTime.DayOfWeek -ne "Saturday" -and $info.CreationTime.DayOfWeek -ne "Sunday" -and $info.CreationTime.Day -eq 1)
-or
($info.CreationTime.DayOfWeek -eq "Monday" -and $info.CreationTime.Day -lt 4))
{
    # it was the first weekday of the month
}

Awesome, I was able to make that work with 99% accuracy. Actually all of that Saturday/Sunday stuff I don't even need since the folders are only created on Weekdays.

If I wanted to return a list of folders, but exclude any created in the previous month, how could I do that? I know I can filter my results with something like

code:
Where-Object {$_.CreationTime -lt (get-date).adddays(-31)}
But that would exclude the previous 31 days of results, which if it's halfway through the month, will start pruning records I don't want pruned.

Any ideas?

Wicaeed fucked around with this message at 18:12 on Jan 5, 2012

Korlac
Nov 16, 2006

A quintessential being known throughout the Realm as the 'Dungeon Master'. :rolldice:

Jelmylicious posted:

:words:

Thanks, that's probably the best explanation I've received thus far about this. I kind of knew it was for an organizational purpose, but for some reason it just wasn't clicking properly.

Quebec Bagnet
Apr 28, 2009

mess with the honk
you get the bonk
Lipstick Apathy

Wicaeed posted:

Awesome, I was able to make that work with 99% accuracy. Actually all of that Saturday/Sunday stuff I don't even need since the folders are only created on Weekdays.

If I wanted to return a list of folders, but exclude any created in the previous month, how could I do that? I know I can filter my results with something like

code:
Where-Object {$_.CreationTime -lt (get-date).adddays(-31)}
But that would exclude the previous 31 days of results, which if it's halfway through the month, will start pruning records I don't want pruned.

Any ideas?

code:
Where-Object {$_.CreationTime.Month -ne ([System.DateTime]::Now.AddMonths(-1).Month -and $_.PsIsContainer }
Will list all objects that are directories (PS calls those containers for various reasons) whose creation time month is not equal to the current date's month-1. That also wraps around January->December correctly. Remember that Where-Object needs to be a filter, e.g. Get-ChildItem | Where-Object {...} | Format-List

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!

i barely GNU her! posted:

code:
Where-Object {$_.CreationTime.Month -ne ([System.DateTime]::Now.AddMonths(-1).Month -and $_.PsIsContainer }
Will list all objects that are directories (PS calls those containers for various reasons) whose creation time month is not equal to the current date's month-1. That also wraps around January->December correctly. Remember that Where-Object needs to be a filter, e.g. Get-ChildItem | Where-Object {...} | Format-List

When you run this in januari, it will filter out anything made in december of any year. So this doesn't work as asked, when you span multiple years. Also, it doesnt filter out the current month (which I also needs to be filtered out).
e: And that is exactly what he asked, but I already made this, so here you go, getting to first day of that month:

code:
$Today = get-date
$CleanedDate = $today.addDays(1-$today.day) #1 extra to get to first day of the month, not last day of previous month
$CleanedDate = $CleanedDate.Addmonths(-1)
And, depending on how close to midnight you need to come, you can do:
code:
$CleanedDate = $CleanedDate.Addhours(-$CleanedDate.hour)
$CleanedDate = $CleanedDate.Addminutes(-$CleanedDate.minute)
$CleanedDate = $CleanedDate.Addseconds(-$CleanedDate.second)
$CleanedDate = $CleanedDate.AddMilliseconds(-$CleanedDate.millisecond)
Now you can filter with a -lt $CleanedDate. Want it modular (first of however many months ago)?
code:
function FirstOfMonth
    {
    param ($Months = 0)
    $Today = get-date
    $CleanedDate = $today.addDays(1-$today.day) #1 extra to get to first day of the month, not last day of previous month
    $CleanedDate = $CleanedDate.Addmonths(-$Months)
    $CleanedDate = $CleanedDate.Addhours(-$CleanedDate.hour)
    $CleanedDate = $CleanedDate.Addminutes(-$CleanedDate.minute)
    $CleanedDate = $CleanedDate.Addseconds(-$CleanedDate.second)
    $CleanedDate = $CleanedDate.AddMilliseconds(-$CleanedDate.millisecond)
    return $CleanedDate
    }
This function will return first of the month of the current month if you just invoke FirstOfMonth, and will subtract extra months if you add a parameter, so:
FirstOfMonth(1) will give you: donderdag 1 december 2011 0:00:00 (Locale is set to Dutch, but you get the gist).

If anyone is interested in forcing the locale so you can read the date, no matter what language the server is set to, let me know. (Very important if you work with the currency object, because it would display as 100 dollars, or pounds, or Rupee depending on the locale, without converting the currency)

Jelmylicious fucked around with this message at 17:01 on Jan 6, 2012

Wicaeed
Feb 8, 2005
Hmm good point.

Is there any way to format the value of the file/folder CreationTime to match a get-date format of Myyyy (January 2012 would be 12012, November 2011 would be 112011, and so on).

I honestly don't really need the date, I'm just filtering out files the current and prior month, the date filtering comes later:

http://pastebin.com/WqUp6gxF

Edit:

Actually I came across a realization. I don't need to filter on the first weekday of the month, but the first Friday of the month, and this makes it much easier simple because The year doesn't matter, because at the point where this script has been running that long I will already have the only backup I want for that time period (the first Friday in the month), and the first Friday will always fall within the first 7 days of the month.

code:

Function Findfilescreatedonfirstweekdayofmonth{
   $global:MonthOldbackupfiles = gci "E:\Backups" | Where-Object {$_.CreationTime.Month -ne ([System.DateTime])::Now.Addmonths(-1).Month -and $_.CreationTime.Month -ne ([System.DateTime])::Now.Month}
    Foreach($item in $Montholdbackupfiles){
    if(($item.CreationTime.DayOfWeek -eq "Friday"  -and $item.CreationTime.Day -le 7))
    {
        write-host "$item was the first Friday of the month"
    }
    Else
    {
        write-host "$item was not created during the specified time period"
    }
    }
}
Bazing!

Tell me I'm not wrong in my assumption :ohdear:

Wicaeed fucked around with this message at 18:21 on Jan 6, 2012

Siets
Sep 19, 2006

by FactsAreUseless
Rather than a giant megapost of questions for the current script I'm working on, I'll ask bit-by-bit (:haw:) instead:

Need some help with regular expressions. I am trying to match large integers that are formatted with commas every three numbers in a string. So there's anything from 24 all the way up to 34,127 or 156,870,240 and beyond. What would a [regex] matching rule look like to account for potentially "n" instances of \d{1,3}\, ending with \d{1,3} is what I'm thinking the question is.

Mario
Oct 29, 2006
It's-a-me!
How about \d{1,3}(\,\d{3})*

1 to 3 digits at the start, followed optionally by any number of ,ddd groups.

Siets
Sep 19, 2006

by FactsAreUseless
Looks good, but do sub-expressions work inside of other sub-expressions? So I would ultimately have:

[regex]"(?<Amount>\d{1,3}(\,\d{3})*)"

edit: Removing instances of 134,04 and the like!
edit2: Crap, is ?<Amount> a Powershell-only syntax for use with -match, and doesn't work inside of .NET [regex]'s?

Siets fucked around with this message at 17:26 on Jan 9, 2012

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!

Siets posted:

Looks good, but do sub-expressions work inside of other sub-expressions? So I would ultimately have:

[regex]"(?<Amount>\d{1,3}(\,\d{3})*)"

edit: Removing instances of 134,04 and the like!
edit2: Crap, is ?<Amount> a Powershell-only syntax for use with -match, and doesn't work inside of .NET [regex]'s?
That's the standard .NET syntax for a named capture group, not just PowerShell. Is it giving you problems?

Siets
Sep 19, 2006

by FactsAreUseless

Jethro posted:

That's the standard .NET syntax for a named capture group, not just PowerShell. Is it giving you problems?

Yeah, I'm really bad at this. Alright, let me expand a little on what I'm trying to do. I'm pretty good at AD filesystem Powershell stuff, but regular expressions are new ground for me.

I essentially have a table that I am trying to parse and keep the relationship between certain dates and the values that I find on those dates. So the table is like this:

code:
Query 1:

Date          Value1     Value2     Value3
--            --         --         --
2012-01-07    36         34,052     1,458
2012-01-06    458        236,378    4,300

Query 2:

Date          Value1     Value2     Value3
--            --         --         --
2012-01-07    598        54,552     492
2012-01-06    252        136,369    1,240
I was approaching this by trying to write a big regex that grabs Date,Value1,Value2,Value3 each in their own sub-expressions, and that I would name them to make them easier to retrieve once I start manipulating the numbers. Ultimately, I am going to be summing the corresponding values with their dates from all queries to get a "total on this date for Value(X)". Does that make sense?

Would a fancy regex handle all this logic, or do I need to delve into setting up my own array variables and a bunch of rules to keep the Value(X) data related to the Date it was found next to? Thanks for the help guys. I love this thread.

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!
It seems to me that it would make a lot more sense to import this into a database of some sort (assuming it's not already in one) or Excel or something, since those tools are more or less designed to do this sort of thing.

If you really need to do this all in Powershell, I'd suggest you start by creating objects and adding properties see here.

adaz
Mar 7, 2009

http://gskinner.com/RegExr/ is a great site if you're building regex's, but Jethro is right -- you probably want to build your own custom objects.

Siets
Sep 19, 2006

by FactsAreUseless
^^ That is incredibly handy!

I'm pulling around ~1,800 queries of HTML page source, each from a different URL (which takes time to load). I timed it out at around 100 seconds each query for me to manually copy it down and format it to be usable, which means... well... a lot of manual labor unless Powershell does all of that for me. :shobon:

I think it is going to come down to creating objects to hold the arrays for later processing.

The pseudocode is basically:

While (there is another row) {
Find and store a line based on a RegEx which finds everything between <tr></tr>
Parse data out of each line by splitting the array at each <td></td>'s
Store the parsed data in a new object
Type the data
}

...but I'm not really sure exactly how all this would look in Powershell. For instance, how would I set up a flag condition to end the While after the last <tr> line was found?

Siets fucked around with this message at 19:49 on Jan 9, 2012

Wicaeed
Feb 8, 2005
Quick question, made harder because I don't know what the actual name is for it:

I'm using the following script to get the Month number (1 = January, etc) so that I can include/exclude files from backup based on if they are the current are prior months, without having to worry about the date.

code:
$global:MonthOldbackupfiles = gci "E:\Backup" | Where-Object {$_.CreationTime.Month -ne ([System.DateTime])::Now.Addmonths(-1).Month -and $_.CreationTime.Month -ne ([System.DateTime])::Now.Month}
It works fairly well.

I'm trying to do something similar for whatever a files creation time was for the week , however I can't find a way get the date/time in that format with the pipeline object $_.CreationTime.

I've been reading this Powershell Blog on how the author goes about adding a membertype to the get-date method: http://blogs.technet.com/b/heyscriptingguy/archive/2011/07/08/use-powershell-to-get-the-number-of-the-week-of-the-year.aspx

I was wondering if you could do something similar

code:
get-date -Uformat %V
Which returns just a '2' right now, but as I said I can't find a way to use it with $_.CreationTime.WeekofYear or something similar, because it simply doesn't exist.

Can you manually add a method to do that type of math?

I hope I'm making sense, I'm still pretty new to this and a lot of the terminology isn't there :ohdear:

Jethro
Jun 1, 2000

I was raised on the dairy, Bitch!
The Get-Date cmdlet can take a date, so something like [int](Get-Date -UFormat %V -Date $_.CreationTime) would get you what you want.

Add-Member wouldn't really help you here, because it only adds the member to a particular object instance.

Jethro fucked around with this message at 18:54 on Jan 11, 2012

Sab669
Sep 24, 2009

I had no idea there was a PowerShell thread, heh. I think I might've posted this over in the General Porgramming thread but didn't get too much insight.

I'm a lowly intern whose never done any sort of scripting in my life, but I need to learn and quick. I have ~2,200 different files in different folders. Based upon the folder a file is in, I need to attach a certain prefix to the file name.

For example, I have a bunch of documents that have a company's name, and I need add EAA or SCA before the company name.


I've scoured Google for various renaming scripts, but I can't make heads from tails from what I'm looking at to alter it to fit my needs. I also can't seem to find any remotely decent tutorial sites, either. Any goons have any recommendations?

adaz
Mar 7, 2009

Conceptually Sab you'd want to do something like this.


Create a hashtable or something with a key/value of folder + prefix
iterate through your folders/files if they match your hash table
rename-item with new prefix

So as an example let's say I have two folders in my C:\temp - Something and Awful:


Inside each folder I have a single text document. Code wise I'm going to say I want to prefix everything in the something folder with xxx and everything in the awful folder with yyy. So here's my hashtable (for more on hashtables check out this hey scripting guy post: http://blogs.technet.com/b/heyscriptingguy/archive/2011/10/15/automatically-create-a-powershell-hash-table-10-15-11.aspx):

code:
$tableVals  = @{
"something"="xxx"
"awful"="yyy"
}
Now we need to iterate through our folder & files. If the folder name matches a key in the hash table retrieve all the files in that folder and rename them.

code:
$Folders = Get-Childitem C:\temp
foreach($folder in $folders){ 
  if($tableVals.ContainsKey($folder.name)) {
      $prefix = $tableVals.item($folder.name)
      $files = Get-ChildItem $folder
      foreach($file in $fileS) {
          rename-item $file.fullName -newName "$prefix$($file.name)"
     }
  }
}

adaz fucked around with this message at 18:04 on Jan 12, 2012

Sab669
Sep 24, 2009

Interesting, thank you for the indepth response.

greth
Nov 12, 2008

Does it trouble your mind the way you trouble mine?
Adaz -- I know this is from like a month ago but thanks for pointing out "trace-command". I'm actually working on writing a Module in C# right now to act as a CLI for some software I work with, and that is going to save me a lot of headache.

ThatSlacker
May 25, 2004
You can switch the SQL function in the OP to use PowerShell 2.0 parameter syntax:

<#
.SYNOPSIS
Fill a datatable from a SQL DB
.DESCRIPTION
Connects to a given SQL server and fills a datatable
.PARAMETER ServerName
The SQL server to connect to
.PARAMETER DatabaseName
The database to connect to
.PARAMETER Query
The query to run
.EXAMPLE
get-datatable -ServerName myserver -DatabaseName TestDB -Query "Select * from TestTable"
.NOTES
Additional information about the function goes here.
.LINK
about_functions_advanced
#>

param(
[parameter(Mandatory=$true)]
[string] $ServerName,
[parameter(Mandatory=$true)]
[string] $DatabaseName,
[parameter(Mandatory=$true)]
[string] $Query
)

That way instead of getting an exception when you launch the script you'll get a prompt for the parameter. Makes it easier when the user doesn't know the parameters. If you add the help block in front of it users can run Get-Help for your script as well.

Loves me some PowerShell. Thanks for the thread. Good stuff here.

Thrawn200
Dec 13, 2008

"Sometimes I think the surest sign that intelligent life exists elsewhere in the universe is that none of it has tried to contact us." - Calvin & Hobbes
Working on teaching myself PowerShell to use at work, still VERY new to it so I'm going to guess this has a simple answer. However my Google skills are failing me on it.

What is the PowerShell equivalent of %~dp0?

For example if I want to Start-Process something but use the full path of wherever it is located in the file and any apply the same to anything in arguments.

An simple example of an old .bat that I'm working with might be -

MSIEXEC.EXE /i "%~dpProgram.msi" TRANSFORMS="%~dp0Program.mst" /qb

I've learned how to do almost all of that simply in PowerShell, just getting caught up on the path.

*edit* Hm, I think I got it right after posting this of course. Seems to work, any issues with this? -

code:
$path = Get-Location
Start-Process -FilePath "$path\Program.msi" -ArgumentList '/t "$path\Program.mst" /qb'

Thrawn200 fucked around with this message at 21:56 on Jan 23, 2012

adaz
Mar 7, 2009

Nope that is a fine way to do it, you can also bind the arguments to a variable and pass it that way as well, or quote everything in string literals - a single quote ' ' - and do it that way.


ThatSlacker, you're right I should re-write all those in V2 style advanced functions, as I've actually done that for a few of them already. I'll finish that up soon :toot:

Thrawn200
Dec 13, 2008

"Sometimes I think the surest sign that intelligent life exists elsewhere in the universe is that none of it has tried to contact us." - Calvin & Hobbes

adaz posted:

Nope that is a fine way to do it, you can also bind the arguments to a variable and pass it that way as well, or quote everything in string literals - a single quote ' ' - and do it that way.

Well, after looking some more, it mostly works. I can't seem to get the transform to apply to the install no matter what way I write it. Doesn't seem to be a lot of documentation for doing silent installs using PowerShell around. :(

*edit* Bah, I need to be able to delete my posts. That's twice I got stuck on something for like an hour plus only to figure it out seconds after I post about it. I just needed to use ` on the quotes around the transform file name.

Thrawn200 fucked around with this message at 17:18 on Jan 24, 2012

FISHMANPET
Mar 3, 2007

Sweet 'N Sour
Can't
Melt
Steel Beams
Is there any hope of me understanding this moonspeak if I "think" in Python already? This is mostly just indecipherable garbage, and it didn't help that by default PowerShell won't run PowerShell scripts

I'm trying to run a command on a bunch of files in a directory, this is the command, as outputed by the mkvmerge gui:

quote:

"C:\Program Files (x86)\MKVToolNix\mkvmerge.exe" -o "C:\\Users\\me\\Desktop\\bad folder\\file.mkv" "--default-track" "0:yes" "--forced-track" "0:no" "--display-dimensions" "0:1280x720" "--language" "2:eng" "--default-track" "2:yes" "--forced-track" "2:no" "-a" "2" "-d" "0" "-S" "-T" "--no-global-tags" "--no-chapters" "C:\\Users\\me\\Desktop\\good folder\\file.mkv" "--track-order" "0:0,0:2"

It's big, and it's nasty, and for some reason it's got double quotes puked out all over it. What I'm doing is removing a language track from each mkv, then outputing it to a new directory with the same name. I could write a script to generate all 24 mkvmerge commands in Python in the time it's taken me to try and figure this out, but since I guess I'm a Windows admin I should at least try and figure this out the "right" way?

adaz
Mar 7, 2009

String Literals are your friend ' ' they will cause powershell to ignore all that mess and just pass the entire thing where it needs to go. Otherwise you can use another " to escape out the quotations and ` to escape out the -- but I wouldn't advise it

adaz fucked around with this message at 22:19 on Jan 28, 2012

Doctor Malaver
May 23, 2007

Ce qui s'est passé t'a rendu plus fort
I'm looking for reviewers for the book Powershell in Depth. Please see this thread for details:

http://forums.somethingawful.com/showthread.php?threadid=3463489

RICHUNCLEPENNYBAGS
Dec 21, 2010

FISHMANPET posted:

Is there any hope of me understanding this moonspeak if I "think" in Python already? This is mostly just indecipherable garbage, and it didn't help that by default PowerShell won't run PowerShell scripts

Surely Powershell is more analogous to bash than to Python.

Siets
Sep 19, 2006

by FactsAreUseless
So I know you can use Get-Member to determine what all possible methods there are based on the object you are working with. Often, what is returned though are a bunch of cryptic methods that aren't exactly clear on how you use them. What is a good way to determine how to use a method or how it works when I find out what is available to me? (Other than Googling of course?)

adaz
Mar 7, 2009

Can you give an example siets? The best bet is to find out the type of the object (listed at the top when you do | get-member or calling GetType() on the object) and look it up on MSDN.

So for instance

code:
$files = get-item *
$files | gm
This tells me the object is a FileInfo Class. If I look that up on MSDN I get each method/attribute listed there and what parameters they take: http://msdn.microsoft.com/en-us/library/system.io.fileinfo.aspx . Now part of the problem you're probably having is that all the examples are listed in VB.NET or C#, which is kind of a pain I admit. I should say nearly every property/method. Powershell on occasion adds its own or subtracts some and hides them in .psbase

However as an example if I look at the CopyTo method on that class (http://msdn.microsoft.com/en-us/library/5axsfwbc.aspx) I can see it takes String for the first parameter as the file location to copy to, and a boolean as the second value indicating if I should over write it or not. Don't worry too much about the syntax presented there as it'll be in VB/C# but look instead into what exactly each parameter each method, etc is taking and what they do.

As another example, look at equals on the fileInfo class (http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx). Equals never works the way people think it does, it's usually reference equality not value equality and that page does a fair to middling job of explaining that.

Note also that powershell 2.0 is using the 2.0 .NET Framework I believe unless you force it to the 4 framework (see: http://stackoverflow.com/questions/2094694/how-can-i-run-powershell-with-the-net-4-runtime) so make sure you have the correct one selected at the top of the page

adaz fucked around with this message at 18:49 on Jan 31, 2012

Siets
Sep 19, 2006

by FactsAreUseless
Ok thanks! I just know that in order to get the maximum benefit out of a object-based tool, you have to be able to leverage the methods available to you. Seems pretty obvious, but for as nice as Powershell makes it to learn about your object, it sorta stops there.

I'm trying to chop up some HTML to get to a number in a column, then add that to a variable. Here's one row of many rows exactly like it

code:
<tr><td>2012-01-29</td><td>324,200</td><td>257,646</td><td>145,955</td><td>12,435</td><td>823</td></tr>
I want to first match the row based on a date that I give, so I put in "2012-01-29" into $Date, and then feed that to a query that finds the specific row I need (example above.) Then, I need to chop out everything until I get just the number in the fifth column (in this case, it would be 12,435.) That number then gets added to another variable I'm storing, we'll just call it $Total for right now.

I feel like string manipulation should be easier. :sigh: Maybe I'm making this too hard in my own head?

e: Ack, sorry for table-breaking. I can fix that if need be.

adaz
Mar 7, 2009

There are a lot of different ways of doing this,but I like regular expressions because they are sweet. I'm going to use the sweet RegExr website to generate the following regex http://regexr.com?2vsgn and replace all that junk with * or something.

As an example:
code:
$strTest = "<tr><td>2012-01-29</td><td>324,200</td><td>257,646</td><td>145,955</td><td>12,435</td><td>823</td></tr>"
$strTest = $strTest -Replace("(<tr>)|(<td>)|(</tr>)|(</td>)","*")

So now I get this:


So I assume you have this entire webpage in some variable, we'll call it $webPage. Now this is again just one way to do it but I would replace all that junk with *, iterate through each line, run a regex match, split the line on ** and grab the value from the new array based on the split. You could make it much fancier by loading each line into its own custom object, and adding to a multidimensional array as a series of properties, and doing the math off that. But if it's a one time thing and the data is consistent I probably wouldn't bother

If you expect possible blank values or some rows might not have same number of columns you will need to tweak the logic.

code:
$webPage= $webPage -Replace("(<tr>)|(<td>)|(</tr>)|(</td>)","*")
foreach($line in $webPage) {
     $strDateMatch = $date.ToString("yyyy-MM-dd")
     if($line -match "($strDateMatch)" {
         $splitVal = $value.Split("**")
         $total = $total + $splitVal[10]
    }
}

adaz fucked around with this message at 19:54 on Jan 31, 2012

Siets
Sep 19, 2006

by FactsAreUseless
Wow, thanks so much for this.

Is $line a predefined variable, or something you just threw in so the foreach would work? I know Powershell is smart enough to let you both define and initialize the variable in the foreach, but I'm just hazy on how that code snippet is doing what it's doing. There's other HTML on the page that I omitted, and I'm just curious how $line knows its way around all that other junk when doing -match.

Also, if I want to make multiple -replace changes to $strTest, how would I go about doing that? Powershell keeps throwing errors when I try to pipe the resultant string after the first replace to another replace. Basically, I tried:

code:
$strTest -replace ('(<font size="-2" color="#000000">)|(</font>)',"") | $_ -replace ('(<tr>)|(<td>)|(</tr>)|(</td>)',"*")
...and it didn't work. I suppose I could just use multiple variables like $strTest1 and $strTest2 to hold the modified strings in order, but that seems messy and un-Powershell-ish to me (if that makes sense.)

Siets fucked around with this message at 20:48 on Jan 31, 2012

adaz
Mar 7, 2009

$line is just the iteration variable I picked. You can call it whatever you want, it's the unique variable for each run through the loop. If you want to see what it contains do a
code:
foreach($line in $webpage) {
   write-host "This is my iteration: $line"
}
If you want to make multiple replace changes you can either change the Regex to include more word matches(correct way), check out that website I linked earlier, OR (ghetto) run it multiple times like you were saying.

adaz fucked around with this message at 22:25 on Jan 31, 2012

Wicaeed
Feb 8, 2005
What is the most simple way to save the result of each iteration of a Foreach loop?

For example I have the following loop:
code:
 Foreach ($Item in $FridayBackups[0..5]){
        $script:robosrc = "E:\Data\Prodbackups\Confluence_Backup\$item"
        $script:robodest = "G:\Confluence_Backup\$item"
        $script:robocopy = "robocopy.exe $robosrc $robodest /S /DCopy:T"
        If ((test-path G:\Confluence_Backup\$item) -eq $false){
            Invoke-Expression -Command $robocopy
            Write-Host "$item was transfered to DFS Folder"
            }
        Else{
            Write-Host "$item already exists"
            }
        }
I want to take the result of what was copied to the folder and save each iteration to send in an email.

Right now the write-host lines are there simply so I can see what is going on at the command line.

I don't quite understand hash tables or arrays yet, which is what I think I need to properly get this working.

Any tips?

Glans Dillzig
Nov 23, 2011

:justpost::justpost::justpost::justpost::justpost::justpost::justpost::justpost:

knickerbocker expert
So, two stupid things I'd like to learn how to do. Honestly, at this point, I'd like to know if they're even possible. I can (hopefully) figure out the actual mechanics. I've only ever had experience scripting with batch files, so for now, I'd like to just use those. If not, hey, always good to learn something new, right?

1) Is there a way to specify a file by the Date Modified column? I basically want to have a script that auto-deletes the oldest file in a certain folder when I run it.

2) How do you check to see if a program is already running? I'm trying to write a script that checks to see if a couple different programs are running, and if not, to start them.

Like I said, I've only ever used simple batch files. If it can't be done with those, oh well. Guess I'd better learn some languages, hahaha. Thanks in advance, goons.

Wicaeed
Feb 8, 2005

Walter_Sobchak posted:

So, two stupid things I'd like to learn how to do. Honestly, at this point, I'd like to know if they're even possible. I can (hopefully) figure out the actual mechanics. I've only ever had experience scripting with batch files, so for now, I'd like to just use those. If not, hey, always good to learn something new, right?

1) Is there a way to specify a file by the Date Modified column? I basically want to have a script that auto-deletes the oldest file in a certain folder when I run it.

2) How do you check to see if a program is already running? I'm trying to write a script that checks to see if a couple different programs are running, and if not, to start them.

Like I said, I've only ever used simple batch files. If it can't be done with those, oh well. Guess I'd better learn some languages, hahaha. Thanks in advance, goons.

Yay I finally get to help someone! I'm doing pretty much this exact thing with a backup script
You can sort objects oldest to newest by creationtime with the following:
code:
gci | Sort-Object -property Creationtime -Ascending
To check for running processes you can use the Get-WMI cmdlet, or simply get-process if you're running the script locally. From there it's just a simple task of filtering the results vs what you are looking for. As a fellow noob, this is exactly what the powershell pipeline is for. If you know what the process name is when the program is running, you could use something like:

code:
$MyProcess = Get-Process processname
if($MyProcess -eq $null)
{
Start-Process -Filepath <pathtoexe> -otherarguments
}
Else
{
Do something else
}
There's really a huge number of different ways you can accomplish that.

Wicaeed fucked around with this message at 16:05 on Feb 2, 2012

adaz
Mar 7, 2009

Wicaeed posted:


Any tips?

There are quite a few ways of doing this. You can absolutely use an array or hashtable, but since this is just going to be outputted to an email as a string anyway I'm going to use StringBuilder (http://msdn.microsoft.com/en-us/library/system.text.stringbuilder.aspx) to store the results and then another function to send yourself an email. So, first things first we create our StringBuidler

code:
$sb = new-object System.Text.StringBuilder
Now we add the necessary code inside your loop that will store our results in the string builder using the AppendLine method (http://msdn.microsoft.com/en-us/library/cb3sbadh.aspx)

code:
$sb = new-object System.Text.StringBuilder
 Foreach ($Item in $FridayBackups[0..5]){
        $script:robosrc = "E:\Data\Prodbackups\Confluence_Backup\$item"
        $script:robodest = "G:\Confluence_Backup\$item"
        $script:robocopy = "robocopy.exe $robosrc $robodest /S /DCopy:T"
        If ((test-path G:\Confluence_Backup\$item) -eq $false){
            Invoke-Expression -Command $robocopy
            $result = "$item was transferred to DFS Folder"
            Write-Host = $result
            $sb.AppendLine($result)
            }
        Else{
            $result = "$item already exists"
            Write-Host $result
            $sb.AppendLine($result)
            }
        }

For the last part of sending an email I made this powershell V2 style send-email function awhile back that you can feel free to steal it comes in pretty handy. So the full script would look like:

code:
Function Send-Email
{
param(
[parameter(Mandatory=$true,HelpMessage="Mail server")]
[string]$smtpserver,

[parameter(Mandatory=$true,HelpMessage="Message to")]
[string]$to,

[parameter(Mandatory=$true,HelpMessage="Message from")]
[string]$from,

[parameter(Mandatory=$true,HelpMessage="Message subject")]
[string]$subject,

[parameter(Mandatory=$true,HelpMessage="Message body")]
[string]$body,

[parameter(Mandatory=$false,HelpMessage="Message attachments")]
[Net.Mail.Attachment]$att
)

$client = new-Object System.Net.Mail.SmtpClient $smtpserver
$Client.EnableSSL = $false
$client.UsedefaultCredentials = $true

$Email = new-Object System.Net.Mail.MailMessage
$Email.To.Add($To)
$Email.From = $from
$Email.Subject = $Subject
$Email.Body = $Body
$Email.IsBodyHtml = $true
if ($att.name) {
    $Email.Attachments.Add($attachment)           
}
    $Client.Send($email)
}

# Main Script starts here

$sb = new-object System.Text.StringBuilder
 Foreach ($Item in $FridayBackups[0..5]){
        $script:robosrc = "E:\Data\Prodbackups\Confluence_Backup\$item"
        $script:robodest = "G:\Confluence_Backup\$item"
        $script:robocopy = "robocopy.exe $robosrc $robodest /S /DCopy:T"
        If ((test-path G:\Confluence_Backup\$item) -eq $false){
            Invoke-Expression -Command $robocopy
            $result = "$item was transfered to DFS Folder"
            Write-Host = $result
            $sb.AppendLine($result)
            }
        Else{
            $result = "$item already exists"
            Write-Host $result
            $sb.AppendLine($result)
            }
        }
# replace values with appropriate for your environment,

send-Email -smtpserver your.mailserver.com -to youremail -from youremail -subject "Result of something" -body $sb.ToString()
Also a general note that you don't need to keep setting the $script: inside the loop. Move them to the outside of the foreach loop and they'll be fine. Check out more on Scope in powershell here (http://technet.microsoft.com/en-us/library/dd315289.aspx)

adaz fucked around with this message at 16:20 on Feb 2, 2012

Adbot
ADBOT LOVES YOU

Jelmylicious
Dec 6, 2007
Buy Dr. Quack's miracle juice! Now with patented H-twenty!
Since you seem interested in finding stuff out for yourself, let met introduce you to get-command, get-help and get-member

Walter_Sobchak posted:

1) Is there a way to specify a file by the Date Modified column? I basically want to have a script that auto-deletes the oldest file in a certain folder when I run it.

You are working with files, command for that is get-childitem (or ls for short). Since you want to know what prperties you get with this, you pipe it to get-member (or gm for short):

code:
ls | gm
Output is a wall of properties and functions for System.IO.FileInfo, one of which is:
CreationTime Property System.DateTime CreationTime {get;set;}
So yes, it is possible.
To get the oldest file, try something like this:
code:
$FileList = LS
$oldest = $FileList[0]
foreach ($file in $filelist)
    {
    if ($file.creationtime -lt $oldest.creationtime) 
        {
        $oldest = $file
        }
    }
$oldest.delete()
So, we take the first entry to have something to compare to, and just keep the oldest file so far in $oldest, then delete it. Now on to your next question:

Walter_Sobchak posted:

2) How do you check to see if a program is already running? I'm trying to write a script that checks to see if a couple different programs are running, and if not, to start them.

For this, you are looking for a process that is running. Let's see if powershell has any functions with process in the name, using get-command (gcm for short)
code:
gcm *process*
one, of the entries returned is get-process, sounds promising. Now, how do i use it? Let's ask powershell:

code:
get-help get-process
Tells me: Gets the process that are running on the local computer or a remote computer. Sounds promising, now, how do we use it?
code:
get-help get-process -examples
Whoa, wall of text! But we see a few examples and can start experimenting:

get-process explorer

returns:
code:
Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)     Id ProcessNam
-------  ------    -----      ----- -----   ------     -- ----------
   1388      84    85024      48024   442 1,052.94    708 explorer
Yay, explorer is running. Now, let's try get-process Jelmylicious

returns a big red error. So I am not running, it seems. But we don't want that error. So, let's pipe to a filter:

code:
get-process | Where {$_.name -eq "Jelmylicious"}
Returns nothing, while the same, but with explorer it returns a value. Now, to make it conditional so you can do something based on whether the process is running or not:
code:
$ProcessRunning = (get-process | Where {$_.name -eq "Jelmylicious"}) -ne $null
Now we have a boolean $ProcessRunning that will equal $true when process is running and $false when it is not running.

E: The sort option two posts before me is more elegant, I was too hung up on explaining how to find info...

Jelmylicious fucked around with this message at 08:38 on Feb 3, 2012

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply