I created a repository with some modules / scripts, that could be useful for others (or only for me ;) If you are interested, look at http://github.com/stej/PoshSupport.

Currently added modules:

I blogged about them here some time ago, so you can find some more info here at my blog.

Meta: 2010-08-24, Pepa

Usually when I need to select some lines from a text, I use -match operator. It's easy and it fills $matches variable for free when you pass scalar value in.

Today I wanted to use Select-String because of its ability to show me the context (several lines around). So I copied some text from email to clipboard and tried:

[0] Select-String -InputObject ((clip) -split "`r`n") -Pattern 'Runspace' -Context 2
> $PsHome  Evaluates to the full path of the installation directory 
for Windows PowerShell. Sample outputs:  C:\Windows\system32\ WindowsP
owerShell\v1.0\  C:\Windows\SysWOW64\WindowsPowerShell\v1.0\  Note tha
t even if you have PowerShell version 2, the dir path is still “v1.0”,
 because PowerShell 2 is meant to be compatible with version 1. $Host  
 Eval to a object that represents the.....

Garbage! Wow? Where does it come from? Ok, I'll try to pipe it.

[1] (clip) -split "`r`n" | Select-String -Pattern 'Runspace' -Context 2
  CurrentUICulture : en-US
  PrivateData      :
> IsRunspacePushed : False
> Runspace         : System.Management.Automation.Runspaces.LocalRunspace

  If you are running PowerShell in Windows Console or emacs, the name line may be:

Hmm, that's exactly what I expected. So, where is the problem?
I'll make the story short. Parameter -InputObject is of type PsObject. After looking through Reflector to the code, I found this line:

operandString = (string) LanguagePrimitives.ConvertTo(
                                operand, 
                                typeof(string), 
                                CultureInfo.InvariantCulture);

This code will be executed in method doMatch of class SelectStringCommand. The only possibility how to convert array of strings to one string is to join them (probably via $ofs) and that's exactly what happened.

IMO that's a bug. I would expect it is possible to pass object via pipeline or via parameter and the cmdlet should behave the same. Going to connect to create one.

Bug reported: You can vote if you consider it a bug.

Meta: 2010-07-19, Pepa

Tags: PowerShell bug

Twitter is going to stop supporting basic authentication and will use only OAuth. That's why I tried again how to connect to Twitter via OAuth and found out that there were some changes during last year.

After some minor changes in code OAuth via PowerShell is working again. Check my updated article How to use OAuth to connect to Twitter in PowerShell.

Meta: 2010-07-10, Pepa

Update 2010-07-10: added support for PIN and link for library download, because there is no new release at DevDefined home page.

When I was working on a Microblog reader for Twitter and Identi.ca, I was thinking about using OAuth for authorization purposes. Recently I have started again. Now I can show you full working example from how to register an application up to how to get the data.

Register your application

If you have an application that will request some data from a service and want to use OAuth for authorization, you have to register it first, so that the service knows about the application. In my case service will be Twitter and the application (consumer in OAuth terminology) will be PowerShell.

First go to Twitter and log in using your standard credentials and browse to Settings–>Connections. In the right column go to the Developers section and click on the link that points to http://twitter.com/oauth_clients. On this page you can see all applications you have registered so far. To create new application (probably your first one), click on Register a new application and fill the info.

First part of registration Second part of registration

Registered After you submit the form, you will receive your applications key and secret (consumer key / consumer secret). This two hashes are used by your application when trying to get authorization key from the service (Twitter). Ok, it's time to play with them.

Authorize and request data

You can implement OAuth protocol on your own or you can use existing implementations. First time I tried an implementation by Shannon Whitley. It didn't work as expected (some problems with token expiration). Then I downloaded DevDefinedOauthPreview1-ReleaseBing.zip from Google code / DevDefined, that worked without problems.

Update 2010-07-10: there is no new release available, although the code is still alive. You have to either download the project and compile it or download the library I compiled for you. The old version worked fine, but some time later Twitter introduced PIN. The source code reflects new changes, but no release has been issued so far..

[3] Add-Type -Path C:\OAuthDevDefined\DevDefined.OAuth.dll
[4] $cons = New-Object devdefined.oauth.consumer.oauthconsumercontext

Use the keys provided by Twitter and set the signature method.

[5] $cons.ConsumerKey = '6NoGCtBEDdZGZtKe7JWdw'
[6] $cons.ConsumerSecret = 'lPkVk1PUCdNe7yXrGBI5fGO1UNjyU4rXOUzHt2SdvE'
[7] $cons.SignatureMethod = [devdefined.oauth.framework.signaturemethod]::HmacSha1

Create an OAuth session and request for authorization. PowerShell has to redirect you to Twitter page where you will allow the access for our application.

[8] $session = new-object DevDefined.OAuth.Consumer.OAuthSession `
 $cons,"http://twitter.com/oauth/request_token", `
 "http://twitter.com/oauth/authorize","http://twitter.com/oauth/access_token"
[9] $rtoken = $session.GetRequestToken()  #unique token just for authorization
[10] $authLink = $session.GetUserAuthorizationUrlForToken($rtoken, 'anything'); $authLink
http://twitter.com/oauth/authorize?oauth_token=RMmhALQzA1glYlR....&oauth_callback=anything
[11] [diagnostics.process]::start($authLink)  #redirection to Twitter

Prompt to allow access Access granted, PIN issued. A browser window should appear and you are requested to allow the access. After that you will see a PIN that you have to pass as the request parameter. We will request for access token that will identify our powershell client and then we will download last 5 statuses and parse user names from them.

[12] $pin = read-host -prompt 'Enter PIN that you have seen at Twitter page'
[13] $accessToken = $session.ExchangeRequestTokenForAccessToken($rtoken, $pin)
[14] $accessToken | Export-CliXml c:\temp\myTwitterAccessToken.clixml

Now you got your access token that contains keys for later use. The token is stored in a xml file. Currently the Twitter's access token doesn't expire, so you just need to store it and use later.
Ok, "use", but how? It is very similar to what you have seen so far:

[1] Add-Type -Path C:\OAuthDevDefined\DevDefined.OAuth.dll
# create context (provide correct keys)
[2] $cons = New-Object devdefined.oauth.consumer.oauthconsumercontext
[3] $cons.ConsumerKey = '6NoGCtBEDdZGZtKe7JWdw'
[4] $cons.ConsumerSecret = 'lPkVk1PUCdNe7yXrGBI5fGO1UNjyU4rXOUzHt2SdvE'
[5] $cons.SignatureMethod = [devdefined.oauth.framework.signaturemethod]::HmacSha1
# create session; I pass $null and not full urls. It looks weird, maybe 
# there is a more elegant way how to create the session
[6] $session = new-object DevDefined.OAuth.Consumer.OAuthSession $cons, $null, $null, $null
# create access token and fill its data
[7] $accessToken = new-object DevDefined.OAuth.Framework.TokenBase
[8] $at = import-cliXml C:\temp\myTwitterAccessToken.clixml
[9] $accessToken.ConsumerKey, $accessToken.Realm, $accessToken.Token, $accessToken.TokenSecret = `
  $at.ConsumerKey, $at.Realm, $at.Token, $at.TokenSecret
# finally, create request and read response
[10] $req = $session.Request($accessToken)
[11] $req.Context.RequestMethod = 'GET'
[12] $req.Context.RawUri = [Uri]'http://api.twitter.com/1/statuses/friends_timeline.xml?count=5'
[13] $res = [xml][DevDefined.OAuth.Consumer.ConsumerRequestExtensions]::ReadBody($req)
[14] $res.statuses.status | % { $_.user.Name }
Michal Těhník
Jeffery Hicks
Jon Skeet
Martin Hassman
David Grudl

Download

More info

Meta: 2010-07-10, Pepa

Hi Craig,

what can I say about SG2010?

I have been using PowerShell for some time, so I'm quite familiar with the language. That's why I didn't learn much in SG, but it was fun. I liked the tasks, because they were interesting.
Every challenge is great – people are curious how many stars the task will get. Competition is imho better than collaboration (as in SG2009).

In general there are some pros and cons:

  • Pro: SG is good idea
  • Pro: competition over collaboration
  • Pro: randomly select award winners is very pleasant part of SG (I would say that even if I didn't win anything)
  • Con: poshcode problems, that's pretty obvious point
  • Con: I didn't know why my score was low. It would be better to at least leave a comment for me
  • Con: Some scripts got 5 stars even if the scripter wasn't aware about powerShell capabilities (built-in help support, test-path for registry, …). Jaykul pointed to one of them at twitter.
  • Con: You should encourage all participants (or at least people solving advanced scenario) to use PowerShell features. For example why to use remote access to registry via some .NET class when there are cmdlets? I know that it is possible to use .NET (I'm a developer), but imho cmdlets is the standard way.

Suggestions:

  • Every user can solve advanced and beginner scenario, but the stars should be counted only from one of them.
  • Previous point means that there should be really 2 winner categories – adv & beginners. It's not fair if advanced people solve adv & beginners and get points from both. Beginners are less motivated then, they can not win.
  • Style points for documentation is wasted time. I saw solutions where doc was very long, maybe half of the script or more. Is it needed? I don't think so. Documentation is boring. It is much more entertaining to make the code better than spent time with documentation.

I hope it doesn't sound like a criticism too much :)
Just wanted to point out some points, because I have read only positive feedback so far.

Thank you for SG2010!
stej

Meta: 2010-06-11, Pepa

Tags: PowerShell

This is translation of my article about some PowerShell tips & tricks not only for developers. I will split the article into more parts, so you won't get tired too early ;)

Operators and variables – Variables $$, $^

$$ and $^ are automatic variables that can be useful when using shell interactively. You will need it only rarely, but it could be worth knowing it. What's all that about can be seen in an example:

[0] Get-ChildItem -rec c:\temp\powershelltest\version1
... some files
[1] $^
Get-ChildItem
[2] $$
c:\temp\powershelltest\version1

The variables contain first and last token from the previous row. More accurately: Contains the first token in the last line received by the session and Contains the last token in the last line received by the session.
They can save your time when typing long paths passed to Get-ChildItem, Get-Item etc.

Have a look at StackOverflow that will prove that some people really use it.

Meta: 2010-05-14, Pepa

This is translation of my article about some PowerShell tips & tricks not only for developers. I will split the article into more parts, so you won't get tired too early ;)

Operators and variables – chain of operators

This technique is not used very often, but it can sometimes help you make your code more readable and you can avoid calling some cmdlets.
Just for this demo suppose that our directory contains these files:

ch01-2010-03-01.txt
ch01-2010-03-02.txt
ch02-2010-03-01.txt
ch02-2010-03-02.txt
ch03-2010-03-01.txt
ch04-2010-03-01.txt

We would like to get only those that begin with ch01. After that we want to parse only part of the file name that can be converted to [datetime]. You would probably do it like this:

[0] Get-ChildItem c:\temp\aa\ | 
   select -exp Name | 
   ? { $_ -like 'ch01*'} | 
   % { $_ -replace 'ch01-|\.txt','' } 

Looks pretty familiar, right? However, you can also use other approach.
Note that the code select -exp Name is not necessary in this case because [FileInfo] is converted to string. During this conversion file name is returned.
Update: [Fileinfo] is sometimes converted to full path. It depends on how the object was constructed. However, I consider this behaviour rather buggy..

[1] (Get-ChildItem c:\temp\aa\ | select -exp Name)  `
   -like 'ch01*'  `
   -replace 'ch01-|\.txt',''

Explanation

There are two things to highlight:
First – operators can be chained. So, operator replace could be split to two for the sake of readability. The code would then look like this: ...-replace 'ch01-','' -replace '\.txt',''. The result of first operator was used when evaluating the second operator. I haven't seen that documented anywhere. Maybe I wasn't searching hard enough. In case you will find some info about it, please let me know.

Second – some operators work with scalars and some with arrays. That's why we could use arrays as left operand for like and replace and the result were correct values.

Lets go even further with our example. Lets convert values that represent dates to datetime.
This is wrong: ... -replace 'ch01-|\.txt','' -as [datetime]. Why? In this case the operator tries to convert input objects (array) to date and that can't succeed. Anyway, we will change the code only a little and it behave correctly.

Compare:

Get-ChildItem c:\temp\aa\ | 
	select -exp Name | 
	? { $_ -like 'ch01*'} | 
	% { $_ -replace 'ch01-|\.txt','' } |
	% { $_ -as [datetime] } |
	? { $_ -le '2010-03-01' }
(Get-ChildItem c:\temp\aa\) `
	-like 'ch01*' `
	-replace 'ch01-|\.txt','' `
	-as [datetime[]] `
	-le '2010-03-01'

Any disadvantage?
There is only one: you have know that you will use this chaining and begin the command with parenthesis.

Meta: 2010-05-10, Pepa

This is translation of my article about some PowerShell tips & tricks not only for developers. I will split the article into more parts, so you won't get tired too early ;)

How many bytes is 1kB?

The today's tip will be very quick.
Do you exactly know how many bytes is 1MB or even 1GB? Are you tired of using 1024*1024*.. all the time? Then look at the examples below.
Note that there is no unit for 1 Byte. Just kidding ;)

[0]> 1kb, 1mb, 1gb, 1tb, 1pb
1024
1048576
1073741824
1099511627776
1125899906842624

What does it mean?
When working with files, you can think in terms of kilobytes/megabytes. Just use the size and add appropriate suffix. PowerShell will evaluate it automatically.

Meta: 2010-05-09, Pepa

This is translation of my article about some PowerShell tips & tricks not only for developers. I will split the article into more parts, so you won't get tired too early ;)

From time to time when working with Powershell you will find something interesting or a general pattern that makes your work easy or that helps you to understand some basics of PowerShell. In this article I will show you some of my tips and interesting useful patterns. Note that this article highlights only some of them. I had to select only some.

I won't touch advanced topics, that you will use only sometimes. I aim at everyday usage of PowerShell. Anyway, I hope at least some of them will be new to you.

Arbitrary names of functions

There are two scenarios how PowerShell is used. You have either your code snippets stored in script files and run them when needed (scheduled scripts, tools, modules, etc.). Or you just type in console and run the code immediatelly after you hit Enter.

When working with scripts, it is very important to keep them readable, comprehensible and independent. That's why you should use full cmdlet names and not only aliases (gci vs. Get-ChildItem). Alias gci can mean something completely different on other machine.

When working with console, your requirements change. You use aliases very frequently and type the shortest code, (gci|?{!$_.PSIsContainer}|select -exp Length). The important thing is to reach the goal, no matter how.

PowerShell makes things easy, because you can use pretty interesting names for your functions and aliases. Let's have a look at some examples:

[0]# well known functions ?? a ?:
[1]function ?? { if ($args[0]) { $args[0] } else { $args[1] } }
[2]function ?: { if (&$args[0]) { $args[1] } else { $args[2] } }

[3]?? $null 'default value'
default value
[4]?: {1} 'is 1' 'is not 1'
is 1
[5]?: {get-process nonexisting -ea 0} 'process exists' 'process doesn''t exist'
process doesn't exist

[6]function * { '*'*10 }
[7]function = { Set-Variable -scope Global -Name $args[0] -Value $args[1] }
[8]= a 100; $a
1
[9]$a = 200; $a #still works

Last example is tip by Johannes Rössel. He uses just Ctrl+D, Enter to exit Posh session. No more exit.

[10]# let's define function with name CTRL+D
[11]New-Item -Path "function:$([char][int]4)" -ItemType function -Value { exit }

Meta: 2010-05-04, Pepa

Joel Bennett published a module for logging via log4net. It is something I always wanted to do, but haven't time for it. Besides that, I wouldn't write it in such a quality as he did ;)

However, what I'm missing there is an ability to send a log message to udp port. It is great for application development. You can see in real-time what your application is doing. You can watch on what command it is waiting etc. And – you work with the messages in a much quicker way. No file opening, no search, you can filter the messages. But wait … is there any application that can read the messages?

Yes! I created a form application that can read the udp messages several years ago. Old, but still does its job well. There is an older description written in czech. Maybe automatic translation could help english readers.
In short it is able to show the messages in a grid. You can filter them so that only e.g. level warning and higher is displayed. You can swallow some messages based on regex or show them in a custom color.

Look at it in action how it works with Logger module by Joel.

Edit your Logger module

To get it working, you need to edit the module and add ability to log on a udp port.

  • To Get-Logger add switch
    [Switch]$Udp
  • Edit the Get-Logger function so that it looks like this:
    if($RollingFile) { $AppenderRefs +=  "<appender-ref ref=""RollingFileAppender"" />`n" }
    if($Udp)         { $AppenderRefs +=  "<appender-ref ref=""udpAppender"" />`n" } # new line
  • Add appender to the embedded configuration xml. You have to add it like this:
    <appender name="udpAppender" type="log4net.Appender.UdpAppender">
      <param name="RemoteAddress" value="127.0.0.1" />
      <param name="RemotePort" value="8080" />
      <param name="Encoding" value="utf-8" />
      <layout type="log4net.Layout.XmlLayout">
        <param name="Prefix" value="" />
      </layout>
      <param name="threshold" value="DEBUG" />
    </appender> <!-- new part -->
    <root>
      <level value="DEBUG" />
    </root>
    <logger name="$LoggerName">
      <level value="$LogLevel" />
      $AppenderRefs
    </logger>
    Note that it uses port 8080. The application has to listen on the same port.
  • And of course use it when calling Get-Logger
    $script:Logger = Get-Logger "PowerShellLogger" -Udp

Download

Meta: 2010-04-08, Pepa