Photographer: dan http://www.freedigitalphotos.net/images/Workshop_and_DIY_g191-Woodplane_p7810.htmlOne of the best things in shells is that you can do some repetitive tasks quickly, efficiently and painlessly. For example, you have to replace some string with another string in multiple files scattered through different directories. You can do that using GUI editors, but this proces is not quick, it is dubiously efficient and most certainly is not painless. Since this is task most administrators and testers will often have, it is useful to have a boilerplate script or function which will handle it and which you can call from your other scripts or directly depending on situation.

In bash, I would do something like this


#!/bin/bash
find="$1"
replace="$2"
path="$3"
find $path | xargs grep -ls $find > matchingfiles.txt
LIST=`cat matchingfiles.txt |tr \n" " "`
for i in $LIST
do
sed "s|$find|$replace|g" $i > $i.new
mv $i.new $i
done

Script should be called with 3 parameters. 1st – the string to replace, 2nd – replacement string, 3rd – root directory to start search in.In order to make things short and easy readable, this example lacks some of good parts – usage guide, check existence of files… However, example works fine for the purpose.

Powershell

How to do similar thing in powershell? Here is the barebone equivalent:

$find=$args[0]
$replace=$args[1]
$path=$args[2]
(get-childitem $path -Recurse | where { ! $_.PSIsContainer }) | select-string -pattern $find | % {$_.Path} > matchingfiles.txt
$FILESARRAY = get-content matchingfiles.txt
foreach ($FILE in $FILESARRAY)
{
(get-content $FILE ) |foreach-object {$_ -replace $find, $replace} | set-content $FILE
}

Differencies? There are many. Apart from different syntax, there are fundamental things:

1. Finding things. “Get-Childitem” cmdlet is much more powerfull than “find”. You can use it also for traversing through registry and a certificate store, not only directories.2. Selecting only files. In bash we used “-l” switch for grep command. In powershell the easiest way I know is that we say that we do not want directories with “where { ! $_.PSIsContainer}”

3. After we formatted output (list of files), we now look for the string we want to replace. Select-String used here is similar enough to grep.

4. To be sure that pipeline will output only filenames we added a “foreach {$_.Path}” to the end identifying that we want only that member of the returned object. In bash, “grep –l” was enough.

5. In powershell script we added a content of the file in array, not in list as in bash. We could use an array in bash but this would broke the compatibility with sh.

6. We used pipeline inside foreach loop to modify files as oppose to straight editing with sed. Again a bit of overhead in processing.

7. With using brackets parenthesis arround (get-content $FILE) we assured rest of the pipeline will execute after this expression is completed, so we could write to the same file not using temp one we used with bash.

8. Big difference from bash is object orientation of powershell. More about that in following chapter.

Too much power for shell?

One thing for sure, a learning curve for powershell is a bit steeper than for bash. In bash you more or less dig through information in quick and dirty manner. Everything you get as output of your commands is pure text. You do not have to worry about objects and their members. Btw, try this in your PS command prompt:

PS> ls | Get-Member

You will see that powershel will display object members of every distinct object returned by “ls” command. This command we used as a tool to snatc those members we operated on in the above script. “.PSIsContainter” – which we evaluated to see is thephoto by Patrick Q returned object directory or “.Path” which was called get the path of returned file objects. Remember “Get-Member” command. You will find it useful. Shorthand for it is “gm.”

There is practically no way to write a script even as simple as this one without using objects. This can seem as a huge overhead for simple scripting tasks especially for someone who is not used to object oriented programming languages or someone who prefer quick and dirty or just simple approach.

So, does object orientation means powershell is more powerful than bash? It certainly does not mean that you can do more with less code. However, one of the goals powershell creators had in mind was to provide a way to access all system resources in similar manner. This should make life of admins, testers and other people who do automation task on Windows much easier. We will try to look at this claim in one of next articles in series.

0 comments