Adding Lines to the Beginning and Ending of Files Using PowerShell

Stack Overflow PowerShell question

In dealing with an application with which I had little familiarity, I discovered that troubleshooting an app (in this case developed using ColdFusion) where many of the source code files are “included” dynamically can be difficult – especially when JavaScript scripts are combined that have functions with identical names.

Because so many of the functions were not only identically named, but also similarly constructed, I decided to add commented identifiers to the beginning and ending of each JS file.

This could be done in a number of ways, but given my recent forays into using PowerShell scripts, I chose that route again.

Stack Overflow PowerShell question
Stack Overflow and Google – developers’ best friends.

After some Googling and a brief visit to Stack Overflow, I learned about the two PowerShell “cmdlets” that would be most useful here: Set-Content and Add-Content.

The text of the script is below. What this script does is recursively search through the file path in line 1, choosing only files that have the extension after the filter switch (.js in this case). Looping through these file names, each file is read into memory (using Get-Content). The root of the file is then removed with the first replace command, and for aesthetics, I chose to change backslashes into forward slashes with the second one (lines 4 and 5). Notice that backslashes must be escaped by using an extra backslash, whereas forward slashes are not escaped. Also, in case you’re wondering, the backtick-n (`n) is the newline character in PowerShell. The Set-Content cmdlet is used to add the “$newline” string and then the original file content. The Add-Content cmdlet is then used to add the line marking the end of the file.

Get-ChildItem "C:\Folder\Subfolder" -recurse -Filter *.js | `
Foreach-Object{
    $content = Get-Content $_.FullName
    $fullname = $_.FullName -replace "C:\\Folder\\Subfolder", ""
    $fullname = $fullname -replace "\\", "/"

    $newline = "// file: " + $fullname + " `n"
    Set-Content $_.FullName -value $newline, $content
    $endline = "// end of file: " + $fullname + " `n"
    Add-Content $_.FullName $endline
}

Adding a User to Multiple Exchange Distribution Lists Using Windows PowerShell

PowerShell command line window

Adding a user to multiple distribution lists via Outlook can be a tedious process if many lists are involved. For today’s problem, I had to add a user to many lists that have a similar prefix. Instead of spending a an hour or more of adding the user to the DLs through the Global Address Book, I decided to use PowerShell.

This script, which I call “addtodl.ps1”, receives three parameters: the user’s email address, the name of the distribution list – which can include a wildcard character (*) to get multiple names, and the Exchange Server FQDN.

Param(
	[string] $UserName,
	[string] $DLName,
	[string] $Exchange
)

$exch = "http://" + $Exchange + "/PowerShell/?SerializationLevel=Full"

$Credentials = Get-Credential
$ExSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exch -Credential $Credentials -Authentication Kerberos
Import-PSSession $ExSession

$distlists =  Get-DistributionGroup $DLName

foreach ($distlist in $distlists) {
	Add-DistributionGroupMember -Identity $distlist.PrimarySmtpAddress -Member $UserName
	#$ManagedBy = $distlist.ManagedBy
	#foreach ($owner in $ManagedBy) {
	#	echo $owner
	#}
}

Exit-PSSession
Remove-PSSession -ID $ExSession.ID
[GC]::Collect()

By running this at the PowerShell command line with the parameters, you will be able to add the user to all distribution lists in the query that you manage. Those that you do not have access to will cause an error that will not halt the script. A dialog box asking for your username and password will appear first.

PowerShell command line window

When I have time, I intend to revisit this issue to get more useful information such as owner email addresses. Currently, if you uncomment the lines inside the foreach statement, the owners of each DL will be printed on the screen as well. It’s not too useful yet – which is why I still have it commented here.

Using the Bing API to Send Pings to Ping-O-Matic

bing basic search before

Those who use WordPress may be familiar with Ping-O-Matic for keeping blog search engines updated whenever a new post is written.

I discovered that Bing had more of my posts indexed than any other search engine. I could manually submit the link for each post to Ping-O-Matic, but that would be no fun. Instead, I decided to automate the process.

The Bing Search API is free for use up to 5000 queries per month. All you need is a Microsoft account to get a key from the Microsoft Azure Marketplace.

Once you have the key, you can set up two files: the first is called bing_basic.html, and the second is bing_basic.php. These files are a mashup from the Bing Search API guide and a function created for sending pings to Ping-O-Matic.

bing_basic.html:

<html>
	<head>
		<title>Bing Search Tester (Basic)</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
	</head>
	<body>
		<h1>Bing Search Tester (Basic)</h1>
		<form method="POST" action="bing_basic.php">
			<label for="service_op">Service Operation</label>
			<input name="service_op" type="radio" value="Web" CHECKED /> Web <input name="service_op" type="radio" value="Image" /> Image 
			<label for="query">Query</label>
			<input name="query" type="text" size="60" maxlength="60" value="" />
			
			<input name="bt_search" type="submit" value="Search" />
		</form>
		<h2>Results</h2> {RESULTS}
	</body>
</html>

bing_basic.php:

<?php

/*
--------------------------------------------
 $title contains the title of the page you're sending
 $url is the url of the page
 $debug true print out the debug and show xml call and answer
--------------------------------------------
 the output is an array with two elements:
 status: ok / ko
 msg: the text response from pingomatic
--------------------------------------------
*/
function pingomatic($title,$url,$debug=false) {
    $content='<?xml version="1.0"?>'.
        '<methodCall>'.
        ' <methodName>weblogUpdates.ping</methodName>'.
        '  <params>'.
        '   <param>'.
        '    <value>'.$title.'</value>'.
        '   </param>'.
        '  <param>'.
        '   <value>'.$url.'</value>'.
        '  </param>'.
        ' </params>'.
        '</methodCall>';

    $headers="POST / HTTP/1.0\r\n".
    "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1) Gecko/20090624 Firefox/3.5 (.NET CLR 3.5.30729)\r\n".
    "Host: rpc.pingomatic.com\r\n".
    "Content-Type: text/xml\r\n".
    "Content-length: ".strlen($content);

    if ($debug) nl2br($headers);

    $request=$headers."\r\n\r\n".$content;
    $response = "";
    $fs=fsockopen('rpc.pingomatic.com',80, $errno, $errstr);
    if ($fs) {
        fwrite ($fs, $request);
        while (!feof($fs)) $response .= fgets($fs);
        if ($debug) echo "<xmp>".$response."</xmp>";
        fclose ($fs);
        preg_match_all("/<(name|value|boolean|string)>(.*)<\/(name|value|boolean|string)>/U",$response,$ar, PREG_PATTERN_ORDER);
        for($i=0;$i<count($ar[2]);$i++) $ar[2][$i]= strip_tags($ar[2][$i]);
        return array('status'=> ( $ar[2][1]==1 ? 'ko' : 'ok' ), 'msg'=>$ar[2][3] );
    } else {
        if ($debug) echo "<xmp>".$errstr." (".$errno.")</xmp>";
        return array('status'=>'ko', 'msg'=>$errstr." (".$errno.")");
    }
}

/****
* Simple PHP application for using the Bing Search API
*/
$acctKey = 'YourAccountKey';
$rootUri = 'https://api.datamarket.azure.com/Bing/Search';

// Read the contents of the .html file into a string.
$contents = file_get_contents('bing_basic.html');
if ($_POST['query'])
{
// Here is where you'll process the query.
// The rest of the code samples in this tutorial are inside this conditional block.
// Encode the query and the single quotes that must surround it.
$query = urlencode("'{$_POST['query']}'");
// Get the selected service operation (Web or Image).
$serviceOp = $_POST['service_op'];
// Construct the full URI for the query.
$requestUri = "$rootUri/$serviceOp?\$format=json&Query=$query";
// Encode the credentials and create the stream context.
$auth = base64_encode("$acctKey:$acctKey");
$data = array(
'http' => array(
'request_fulluri' => true,
// ignore_errors can help debug โ€“ remove for production. This option added in PHP 5.2.10
'ignore_errors' => true,
'header' => "Authorization: Basic $auth")
);
$context = stream_context_create($data);
// Get the response from Bing.
$response = file_get_contents($requestUri, 0, $context);
// Decode the response.
$jsonObj = json_decode($response);
$resultStr = '';
// Parse each result according to its metadata type.
foreach($jsonObj->d->results as $value) {
	$pingresults = pingomatic($value->Url,$value->Description);
	//$arrlength = count($pingresults);
	//$resultStr .= $arrlength . "
\n";
	//for($x = 0; $x < $arrlength; $x++) {
	$resultStr .= $pingresults['status'] . "
\n";
	$resultStr .= $pingresults['msg'] . "
\n";
	//}
	switch ($value->__metadata->type) {
		case 'WebResult':
			$resultStr .=
				"<a href=\"{$value->Url}\">{$value->Title}</a>
{$value->Description}
";
				 break;
				 case 'ImageResult':
				 	$resultStr .=
				 		"<h4>{$value->Title} ({$value->Width}x{$value->Height}) " . "{$value->FileSize} bytes)</h4>" . "<a href=\"{$value->MediaUrl}\">" . "<img src=\"{$value->Thumbnail->MediaUrl}\"></a>
";
				 		 break;
				 	}
				 }
// Substitute the results placeholder. Ready to go.
$contents = str_replace('{RESULTS}', $resultStr, $contents);
}
echo $contents;
?>

Put both files into the same folder on your website. When you open bing_basic.php in your browser, it should look something like this:

bing basic search before

In my case, I wanted to get all the pages that Bing had indexed. I entered “site:deepinthecode.com” into the Query box and clicked Search.

After a moment, the page refreshed with the following results:

bing basic after searching

The “ok” status shows that the ping was received properly and the line below shows the status message. Following those lines are the hyperlinked post titles and the beginning of each post.

Had the ping not been received, the status would be “ko”, and the message would (hopefully) be descriptive of why the ping did not take.

The function in the PHP file can be modified for use with APIs other than the one for Ping-O-Matic, though I haven’t had time to make any changes there yet to see how well it will work with other sites.