views:

211

answers:

2

I often want to process a file resulting in a modified output file. I can't seem to find the PowerShell way to do this - it always ends up looking like code in a one-liner, IYSWIM. This is an example.

I have an LMHOSTS file. Don't ask why! I want to get a new LMHOSTS file that only contains servers that respond to pings. I also want to pass through any comment lines.

Comment lines begin with a #.

Data lines are something like this (space-separated):

10.123.177.1    SVRDS1 #PRE #DOM:DOM1

or this:

10.241.177.30   SVRDS30 #PRE

This is what I've got (with thanks to Luke for help with Ping-Host):

gc 'C:\work\lmhosts.txt' | % { if ($_ -like '#*') { $_ | out-file 'C:\work\lmhosts2.txt' -append } else { if ($(Ping-Host $_.Substring(0, $_.indexof(' ')) -count 1 -timeout 10).received -eq 1) { $_ | out-file 'C:\work\lmhosts2.txt' -append } } }

It works but it's not nice. I've taken a programmer's approach but done it as a one-liner. Is there a better way of doing it, that's more 'shell' and less 'code'?

+1  A: 

Whitespace is your friend! In Powershell scripts I write that aren't immediately thrown away, almost every '|' is followed by a newline + indent.

Richard Berg
Ah, but then it wouldn't be a one-liner. :-)
serialhobbyist
Ok, I think I misunderstood what kind of "style" you were hoping to improve.
Richard Berg
+3  A: 

In this particular example, you are filtering the contents of 1 file and outputting it to another. Where-Object is usually a good choice for this. You can then simplify your one-liner a bit:

gc 'C:\work\lmhosts.txt' | 
    ?{ ($_ -like '#*') -or 
       ($(Ping-Host $_.Split(' ')[0]) -count 1 -timeout 10).received -eq 1) } > 
    'C:\work\lmhosts2.txt'

Some notes:

  • "?" is an alias for Where-Object
  • The -or operator will short circuit, that is, if the first operand results in True, the second will not bother executing.
  • You can use the redirection operator ">" instead of "| Out-File".
  • I replaced Substring with Split, it seemed slightly simpler, not sure if works in general with your input.
zdan
I like this approach. I would just replace the Ping-Host usage with Test-Connection -quiet if you're on PowerShell 2.0. BTW if you want to maintain the original encoding, I would drop back to Out-File -enc ascii.
Keith Hill
Oh yeah, same comment about the brittleness of -like versus -match as I mentioned on the question post.
Keith Hill
Ah, yes. That looks better than mine. Thanks a lot.
serialhobbyist