Passing string variable to Sharepoint command without desired outcome - powershell

I have a number of fields that I am using to build a string for an extraction of values from a SharePoint 2013 list.
I use this to build the string.
foreach($Column in $StringColumns){
$Fields=$Fields+"`""+$Column+"`""
if($Loop -ne $ColumnCount){
$Fields=$Fields+","
$Loop++}
}
I take the built $Fields [string] variable and pass it to this command.
$SPList.getitems($queryfromsource)[$ItemNumber][$Fields]
The result is that I receive no output from the command. What makes it odd is that I can confirm that $Fields has the appropriate string in it for that command. I have done so by calling it in the console and then copying the output into the SharePoint command directly. When I do that, I receive the output I am looking for.
This seems like it should be incredibly simple but it is driving me insane.

would something like this work?
foreach ($Column in $StringColumns)
{
$Fields+= "`"$($Column)`""
if ($Loop -ne $ColumnCount)
{
$Fields+= ","
$Loop++
# Only use Write-Host for debugging
Write-Host "Column $Loop of $ColumnCount"
}
}
Do you initialize $Loop somewhere, might try having it print the loop number you're on?

You probably should learn a bit about string building techniques in PowerShell. That convoluted construct you have there is not just too much work to create, it's also difficult to debug, as your example proves perfectly. You need to initialize $Loop to 1, not 0 (I assume that's what you are doing, please always post the complete code), or put the $Loop++ at the beginning of the loop, otherwise the string has a comma at the end. You also need to initialize the $Fields variable to an empty string.
Have you checked the string output of your script by printing the actual variable? If you check it in the console, you might have already initialized some variable which you forgot to do in the script.
A much easier way to build the string:
$columns = 'Title', 'Created', 'Description'
$string = "`"$($columns -join '","')`""

Related

Format command output in powershell

I have a output like below from a command
IpPrefix
--------
15.181.232.0/21
142.4.160.136/29
3.2.0.0/24
161.188.154.0/23
Using above output, need write a command in powershell to get format like :
15.181.232.0/21,142.4.160.136/29,3.2.0.0/24,161.188.154.0/23
Basically combine multiple lines to one line with comma separated. Please help.
Welcome to PowerShell! The results of commands are objects. This object you have made has at least 1 property called IpPrefix. If your object is stored in a variable $object, then you can reference the property like this:
$object = (some-command -param something)
$object.IpPrefix
Once you're got the items in just that one column, without the column header, you now have an array of strings. They're still objects, but they are string objects.
The -join operator works against arrays.
$object = (some-command -param something)
$object.IpPrefix -join ','
This will give you what you want in the simplest way possible.
Let's say maybe you don't want to store your data in a variable (aka in Memory). You might have a pipeline or some other situation where storing the data slows you down. You would do that like this:
(some-command -param something).IpPrefix -join ','
Same idea, different syntax. Hope this helps you understand the shell better!

From which interface/class does the Count method come from [duplicate]

I've made a most unfortunate typo costing me quite some precious time:
$errors.Count
This returns "0", even if there are errors, because the variable name should be singular. This does work:
$error.clear() # To ensure a correct repro
Copy-Item asdf fdsa # Will show an error
$error.Count # Will output "1"
However, I now want to know why $errors.Count gave me anything at all, and why it gave me "0". So I went on to do some testing, and got the following results:
$asdf.Count # Will output "0"
$nrOfEinsteinsInSpace.Count # Will output "0"
$a = 0; $a.Count; # Will output "1"
$b = 2; $a.Count; # Will output "1"
$x = 1,2,3; $x.Count; # Will output "3"
And gathering even more data to be able to ask a sensible question here I did:
$true.Count # Will output "1"
$false.Count # Will output "1"
So we have the following different cases:
Array(like) variables, where .Count will output the number of items.
Non-existent variables, where .Count will output "0".
Declared variables, where .Count will output "1".
Built-in variables, where .Count will output "1".
Case 2, 3, and 4 don't make any sense to me (yet). What is going on here? Where is this documented? How does the .Count property work?
Starting in PowerShell V3, the properties Count and Length received very special treatment not related to extended type data (also known as ETS or extended type system).
If the instance has a Count/Length property, then everything continues to work like it did in V1/V2 - the value from the instance is returned.
If the instance does not have a Count/Length property, starting in V3, instead of that being an error, you'll get back 1. And if the instance is $null, you'll get back 0. If you have turned on strict mode, you'll get an error like in V2.
I'll admit this is a bit strange, but it solves a common problem when a cmdlet returns 0, 1, or multiple objects.
Often, you'll iterate through those results via the pipeline or with a foreach statement. For example:
dir nosuchfile* | % { $_ }
foreach ($item in dir nosuchfile*) { $_ }
In the foreach case above, V2 would actually enter the loop if the command didn't return any values. That was changed in V3 to better match peoples expectations, but that also meant that:
foreach ($item in $null) { $_ }
also never enters the loop.
The for statement is another way to iterate through results, e.g.
$results = dir nosuchfile*
for ($i = 0; $i -lt $results.Count; $i++) { $results[$i] }
In this example, it doesn't matter how many objects are in $result, the loop works just fine. Note that in V1/V2, you would have written:
$results = #(dir nosuchfile*)
This ensures $results is an array, but this syntax is ugly and many folks would forget it, hence the change to support Count/Length in a slightly more forgiving way.
To complement Paul's answer, this might be related to extended type data. To quote the relevant part of the documentation:
Extended type data defines additional properties and methods
("members") of object types in Windows PowerShell. You can extend any
type that is supported by Windows PowerShell and use the added
properties and methods in the same way that you use the properties
that are defined on the object types.
And:
There are three sources of extended type data in Windows PowerShell
sessions.
The Types.ps1xml files in the Windows PowerShell installation directory are loaded automatically into every Windows PowerShell session.
If you open that Types.ps1xml file (in $pshome), you'll see this at the beginning of the file:
<Type>
<Name>System.Array</Name>
<Members>
<AliasProperty>
<Name>Count</Name>
<ReferencedMemberName>Length</ReferencedMemberName>
</AliasProperty>
</Members>
</Type>
So my guess is that by providing the ".Count" property, PowerShell assumes this is an array.
Here is how i think it works:
Case 1: In Arrays the .Count Property actually links to the .Length property which shows the number of Items in the Array
Case 2: Non-exitent variables get automatically created by powershell and initialized with value $null
Case 3 / 4: On this one i am not exactly sure why it happens but since neither String nor Int or boolean Objects have a .Count property i could imagine that the Property is inherited by a parent-object.
The behaviour suggests that the variable is treated as array so with 1 Value assigned the output will be 1, without a value the result will be 0.
Edit:
For the sake of completeness here is the Link to the Documentation: Technet, thanks #David Brabant

Is there a way to echo a Powershell command line?

I've got several Powershell scripts under construction, and one thing I'd like to do in them is spit out a line at the top of the output echoing the command line used.
Use case is: output is being redirected to a file, and a year from now when someone examines that file, I want them to be able to copy/paste the command from the output file to regenerate the same output where the only differences are chronological. [ Okay, that was a little too generic ... first case: I'm examining ACLs and want to be able to repeat the same examination on the newer data at any point in the future by simply copy/pasting the same command. ]
My script begins with the parameter definitions:
[CmdletBinding()]
Param(
[string] $filter="Name -like '*'",
[string] $user=$null,
[switch] $test01=$false,
[switch] $test02=$false
)
What I'm doing now is a fall-back position, knowing what parameters can be accepted, I'm dumping out the names & values of those parameters:
if ($user.Length -eq 0) { $u = "NULL" } else { $u = "|$user|" }
if ($test01) { $u += ", -TEST01" }
if ($test02) { $u += ", -TEST02" }
"RUN BEGINS at $((get-date).ToString('F')) -- Filter is |$filter|, User is $u"
Ugly, hacky, not even a hint of portability in it, and definitely NOT a copy/paste of the command.
Regardless, this CAN be mangled into a command line; but not generically, and not with 100% surety.
I've tried using $args, but apparently the either defining named parameters or the CmdletBinding() breaks that mechanism, because it's always empty. Tried $PsBoundParameters, Get-History, and even $0 .. $9 bash-like variables. So far, nothing I can find gives the command line that launched the script that's running.
$PsBoundParameters is close, it's got all the right data as key,value pairs that could be built up into a command line. but it still isn't a command line, and would require mangling to get it into one.
Get-History came even closer as it includes a complete command line; problem is it gives the command run RIGHT BEFORE the command that launched the script, not the command that launched it.
Running out of options ... but am way open to suggestion.
Found it! And it was as simple as I'd hoped it would be. [ to use ... finding it was a pain ]
$MyInvocation.Line
While I agree that $MyInvocation.Line will get the literal command used which seems to be what you want on the surface, I'd still argue that the data in $PSBoundParameters is more useful long term simply because you can't guarantee users will call your function in a way that makes the command line actually useful.
Consider the common case where callers have declared variables to hold parameter values:
$myfilter = "Name -like '*Joe*'"
MyFunction -filter $myfilter
Consider the case where callers create a hashtable to splat with:
$myParams = #{
filter = "Name -like '*Joe*'"
test01 = $true
}
MyFunction #myParams
If you only record the command line, you'd lose the parameter data in both of these cases. And if you really want a literal command that people can copy/paste from the log, it shouldn't be that hard to generate a synthetic command based on the data in $PSBoundParameters. It doesn't have to be literally the same command as long as the same parameter data gets passed in, right?

Count characters in string then insert delimiter using PowerShell

I have a linux server that will be generating several files throughout the day that need to be inserted in to a database; using Putty I can sftp them off to a server running SQL 2008. Problem is is the structure of the file itself, it has a string of text that are to be placed in different columns, but bulk insert in sql tries to put it all in to one column instead of six. Powershell may not be the best method, but I have seen on several sites how it can find and replace or append to the end of the line, can it count and insert?
So the file looks like this: '18240087A +17135555555 3333333333', where 18, 24, 00, 87, A are different columns, then there is a blank space between the A and the +, that is character count 10-19 which is another column, then characters 20-30 are a column, characters 31-36 are a space which is new column and so on. So I want to insert a '|' or a ',' so that sql understands where the columns end. Is this possible for PowerShell to count randomly?
This may not be the way to respond to all who did answer, i apologize in advance. As this is my first PowerShell script, I appreciate the input from each of you. This is an Avaya SIP server that is generating CDR records, which I must pull from the server and insert in to SQL for later reports. The file exported looks like this:
18:47 10/15
18470214A +14434444444 3013777777 CME-SBC HHHH-CM 4 M00 0
At first I just thought to delete the first line and run a script against the output, which I modified from Kieranties post:
$test = Get-Content C:\Share\CDR\testCDR.txt
$pattern = "^(.{2})(.{2})(.{1})(.{2})(.{1})(.{1})\s*(.{15})(.{10})\s*(.{7})\s*(.{7})\s*(.{1})\s*(.{1})(.{1})(.{1})\s*(.*)$"
if($test -match $pattern){
$result = $matches.Values | select -first ($matches.Count-1)
[array]::Reverse($result, 0, $result.Length)
$result = $result -join "|"
$result | Out-File c:\Share\CDR\results1.txt
}
But then i realized I need that first line as it contains the date. I can try to work that out another way though.
I also now see that there are times when the file contains 2 or more lines of CDR info, such as:
18:24 10/15
18240087A +14434444444 3013777777 CME-SBC HRSA-CM 4 M00 0
18240096A +14434444445 3013777778 CME-SBC HRSA-CM 4 M00 0
Whereas the .ps1 file I made does not give the second string, so I tried adding in this:
foreach ($Data in $test)
{
$Data = $Data -split(',')
and it fails to run. How can I do multiple lines (and possibly that first line)? If you know of a tutorial that can help, that's greatly appreciated as well!
PowerShell is a great tool that I love and it can do many things. I see that you are using SQL Server 2008. Depending on the edition of SQL Server you have running on the server, it most likely has SQL Server Integration Services (SSIS), which is an Extract, Transform, and Load (ETL) tool designed to help migrate data in many scenarios, such as yours. The file you describe here is sounds like a fixed width file, which SSIS can easily handle and import and SQL Server has great ways to automate the loads if this is a recurring need (Which it sounds like), including the automation of the sftp task, and even running PowerShell scripts as part of the ETL (I've done that several times).
If your file truly is fixed width and you want to use PowerShell to transform it into a delimited file, the regex approach you have in your answer works well, or there are several approaches using the System.String methods, like .insert() which allows you to insert a delimiter character using a character index in your line (use Get-Content to read the file and create one String object per line, then loop through them using Foreach loop or Foreach-Object and the pipeline). A slightly more difficult approach would be to use the .Substring() method. You could build your new String line using Substring to extract each column and concatenating those values with a delimiter. That's probably a lot for someone new to PowerShell, but one of the best ways to learn and gain proficiency with it is to practice writing the same script multiple ways. You can learn new techniques that may solve other problems you might encounter in the future.
This is a way (really ugly IMO, I think it can better done):
$a = '18240087A +17135555555 3333333333'
$b = #( ($a[0..1] -join ''), ($a[2..3] -join ''), ($a[4..5] -join ''),
($a[6..7] -join ''), ($a[8] -join ''), ($A[10..19] -join ''),
($a[20..30] -join ''), ($a[31..36] -join ''))
$c = $b -join '|'
$c
18|24|00|87|A|+171355555|55 33333333|33
I don't know if is the rigth splitting you need, but changing the values in each [x..y] you can do what better fit your need. Remenber that character array are 0-based, then the first char is 0 and so on.
I don't quite follow the splitting rules. What kind of software writes the text file anyway? Maybe it can be instructed to change the structure?
That being said, inserting pipes is easy enough with .Insert()
$a= '18240087A +17135555555 3333333333'
$a.Substring(0, $a.IndexOf('+')).Insert(2, '|').insert(5,'|').insert(8, '|').insert(11, '|').insert(13, '|')
# Output: 18|24|00|87|A|
# Rest of the line:
$a.Substring($a.IndexOf('+')+1)
# Output: 17135555555 3333333333
From there you can proceed to splitting the rest of the row data.
I've improved my answer based on your response (note, it's probably best you update your actual question to include that information!)
The nice thing about Get-Content in Powershell is that it returns the content as an array split on the end of line characters. Couple that with allowing multiple assignment from an array and you end up with some neat code.
The following has a function to process each line based on your modified version of my original answer. It's then wrapped by a function which processes the file.
This reads the given file, setting the first line to $date and the rest of the content to $content. It then creates an output file adds the date to the output, then loops over the rest of the content performing the regex check and adding the parsed version of the content if the check is successful.
Function Parse-CDRFileLine {
Param(
[string]$line
)
$pattern = "^(.{2})(.{2})(.{1})(.{2})(.{1})(.{1})\s*(.{15})(.{10})\s*(.{7})\s*(.{7})\s*(.{1})\s*(.{1})(.{1})(.{1})\s*(.*)$"
if($line -match $pattern){
$result = $matches.Values | select -first ($matches.Count-1)
[array]::Reverse($result, 0, $result.Length)
$result = $result -join "|"
$result
}
}
Function Parse-CDRFile{
Param(
[string]$filepath
)
# Read content, setting first line to $date, the rest to $content
$date,$content = Get-Content $filepath
# Create the output file, overwrite if neccessary
$outputFile = New-Item "$filepath.out" -ItemType file -Force
# Add the date line
Set-Content $outputFile $date
# Process the rest of the content
$content |
? { -not([string]::IsNullOrEmpty($_)) } |
% { Add-Content $outputFile (Parse-CDRFileLine $_) }
}
Parse-CDRFile "C:\input.txt"
I used your sample input and the result I get is:
18:24 10/15
18|24|0|08|7|A|+14434444444 30|13777777 C|ME-SBC |HRSA-CM|4|M|0|0|0
18|24|0|09|6|A|+14434444445 30|13777778 C|ME-SBC |HRSA-CM|4|M|0|0|0
There are an incredible amount of resources out there but one I particularly suggest is Douglas Finkes Powershell for Developers It's short, concise and full of great info that will get you thinking in the right mindset with Powershell

Formatting a Powershell string containing hashtable values

The answer to this is likely to be trivial, but I have spent half an hour and still can't work it out.
Assume I have a the following hashtable:
$hash = #{face='Off';}
What I have tried to do, is output the value of "face" along side some other string elements.
This works:
Write-Host Face $hash['face']
=> Face Off
However, this doesn't:
Write-Host Face/$hash['face']
=> Face/System.Collections.Hashtable[face]
Somehow the lack of a space has affected the operator precedence - it is now evaluating $hash as a string, the concatenating [face] afterwards.
Trying to solve this precedence problem, I tried:
Write-Host Face/($hash['face'])
=> Face/ Off
I now have an extra space I don't want.
This works, but I don't want the extra line just to reassign:
$hashvalue = $hash['face']
write-host Face/$hashvalue
=> Face/Off
Any idea how to get this working as a one-liner?
Sure, use a subexpression:
Write-Host Face/$($hash['face'])
Generally, I'd just use a string, if I need precise control over whitespace with Write-Host:
Write-Host "Face/$($hash['face'])"
Yes, in this case you need a subexpression again, but more because you simply can't include an expression like $foo['bar'] in a string otherwise ;-)
By the way, $hash.face works just as well with much less visual clutter.
In such cases, and more so if there are more variables involved, I prefer using the string formatting. While in this case you can live with the $(..), be aware that string formatting will remove lot of doubt, cruft and improves readability:
write-host ("Face/{0}" -f $hash['face'])
In addition to using sub expressions as Joey showed you can:
Use string formatting:
Write-Host ('Face/{0}' -f $hash.face)
This will stick the value of face key in the place of {0}
Use string concatenation:
Write-Host ('Face/' + $hash.face)
Both of those require an expression to be evaluated which outputs a string which is used as Write-Host's Object parameter.
Another option is to insert your slash with the -Separator option of Write-Host:
$hash = #{Face="off"}
Write-Host ("Face",$hash.Face) -Separator '/'