I am trying to pull some file paths from an array and use them to test if a folder is located on serval machines.
I seem to get an error pulling the value from the 2-dimentional array and putting it in a variable.
Here is a striped down, very basic version of my script.
$PCS = "PC1","PC2","PC3"
$locations= #("System32","Windows\System32"),
("Public","Users\Public")
ForEach($PC in $PCS){
$i=0
Do
{
$fullpath = "\\" + $PC + "\C$\" + "$locations[$i][1]"
test-path $fullpath
$i++
}
While ($i -le ($locations.length-1) )
}
However when I use $fullpath to test if the folder is there, I get false, and upon further investigation, the actual value of $fullpath is:
\\PC1\C$\System.Object[] System.Object[] System.Object[] System.Object[]
How can I get the value from the array, so I can use it as a filepath location?
$fullpath = "\\" + $PC + "\C$\" + "$($locations[$i][1])"
or
$fullpath = "\\" + $PC + "\C$\" + $locations[$i][1]
As arco444 points out, your code as posted seems incomplete, but I think your issue will be fixed by above.
Explanation
When you use "$locations[$i][1]" it only interprets $locations as a variable within the string. Using . to access properties or [] to access elements is interpreted as literal characters.
So in this case, you can use the second option (don't surround it in quotes) if the result of the lookup is already a string or can be coerced into one.
In the general sense, the first option uses $(...) which is a sub-expression. The ... can be any code, including entire pipelines and function calls and such.
Related
I'm just learning PowerShell, and running into an issue.
I'm trying to use an array to define a collection of folder/file paths, and then use that collection in a foreach loop. When the loop is executed, it seems to be concatenating the collection into a single path instead of separately processing each item in the array. This results in a path not being found error.
Code:
$SourcePathRoot = "C:\Temp\"
#$SourcePaths = #($SourcePathRoot + "File1.dat", $SourcePathRoot + "File2.txt")
foreach ($Path in $SourcePaths) {
Write-Host $Path
Test-Path $Path }
Output ($Path and Test-Path $Path):
C:\Temp\File1.dat C:\Temp\File2.txt
False
I know it's probably something simple - what am I doing wrong?!? Thank you.
This has to do with operator precedence and type-dependent operator behavior in PowerShell.
When evaluating an expression, , takes precedence over +.
In your example, the , operator in the middle of your array expression is therefore evaluated before the + operators are.
Let's step through:
# Okay, this is the raw expression
#($SourcePathRoot + "File1.dat", $SourcePathRoot + "File2.txt")
# , takes precedence and is evaluated first, leaving us with
#("C:\Temp\" + #("File1.data", "C:\Temp\") + "File2.txt")
The behavior of + depends on the type of the left-hand side operand - which is a string - so PowerShell will attempt string concatenation of the middle array to the first string, resulting in:
#("C:\Temp\File1.data C:\Temp\" + "File2.txt")
Which is why the result is an array consisting of 1 string with the value C:\Temp\File1.dat C:\Temp\File2.txt
Solution is simple, just split the array expression into two separate statements:
$SourcePaths = #($SourcePathRoot + "File1.dat"; $SourcePathRoot + "File2.txt")
# or
$SourcePaths = #(
$SourcePathRoot + "File1.dat"
$SourcePathRoot + "File2.txt"
)
pracl is a sysinternal command that can be used to list the ACLs of a directory. I have a list of shares and I want to create a csv file such that for each ACL entry, I want the share path in one column and share permission in the next. I was trying to do that by using the following code
$inputfile = "share.txt"
$outputFile = "out.csv"
foreach( $path in Get-Content $inputfile)
{
$results=.\pracl.exe $path
{
foreach ($result in $results) {write-host $path,$line}
}
$objResult = [pscustomobject]#{
Path = $Path
Permission = $line
}
$outputArray += $objResult
$objresult
}
$outputArray | Export-Csv -Path $outputfile -NoTypeInformation
It failed with the following error :-
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
At C:\Users\re07393\1\sample.ps1:14 char:1
+ $outputArray += $objResult
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any suggestions ?
You're trying to create an array of [pscustomobject]s in your $outputArray variable iteratively, using +=, but you're not initializing $outputArray as an array - see the bottom section for an explanation of the resulting behavior.
Thus, the immediate solution to your problem is to do just that:
# Do this before your `foreach` loop, then `+=` will work for appending elements.
$outputArray = #()
However, using += to add to arrays is inefficient, because in reality a new array instance must be created every time, because arrays are immutable data structures. That is, every time += is used, PowerShell creates a new array instance behind the scenes to which the existing elements as well as the new element are copied.
A simpler and much more efficient approach is to let PowerShell create an array for you, by using the foreach loop as an expression and assigning it to a variable as a whole:
That is, whatever is output in every iteration of the loop is automatically collected by PowerShell:
A simplified example:
# Create an array of 10 custom objects
[array] $outputArray = foreach ($i in 1..10) {
# Create and implicitly output a custom object in each iteration.
[pscustomobject] #{
Number = $i
}
}
Note the use of type constraint [array] to the left of $outputArray, which ensures that the variable value is always an array, even if the loop happens to produce just one output object (in which case PowerShell would otherwise just store that object itself, and not wrap it in an array).
Note that you can similarly use for, if, do / while / switch statements as expressions.
In all cases, however, these statements can only serve as expressions by themselves; regrettably, using them as the first segment of a pipeline or embedding them in larger expressions does not work - see GitHub issue #6817.
As for what you tried:
$outputArray += $objResult
Since you didn't initialize $outputArray before the loop, the variable is implicitly created in the loop's first iteration:
If the LHS variable doesn't exist yet, += is effectively the same as =: that is, the RHS is stored as-is in the LHS variable, so that $outputArray now contains a [pscustomobject] instance.
In the second iteration, because $outputArray now has a value, += now tries to perform a type-appropriate + operation (such as numeric addition for numbers, and concatenation for strings), but no + (op_Addition()) operation is defined for type [pscustomobject], so the operation fails with the error message you saw.
I am writing a script to install/uninstall a collection of software. Some of this software must be installed before others, and when uninstalling they need to go in reverse order. I am attempting to design this script to read a text file containing the paths to all of the .msi files. This way I can read them with Get-Content in appropriate order, then if they need to be uninstalled I can read the same file and just reverse the array.
I'm new to powershell, but it's my understanding that get-object returns an object of type [array]. Microsoft's documentation on Get-Content doesn't specify a return type but every discussion of Get-Content on stack overflow describes the output as an array.
Here's the problem:
When I store the output of Get-Content to a variable $data = Get-Content -Path "C:\my\data.txt" then I print what's stored in $data write-host $data I get the expected output:
line 1
line 2
line 3
etc...
However
When I try to reverse $data = [array]::Reverse($data). It returns null
When I manually create an array $letters = #("a","b","c") and reverse it $letters = [array]::reverse($letters) then the out put of write-host $letters is, as expected:
c
b
a
Here's the question
Why is my call to [array]::reverse($data) returning null? instead of doing as I would expect in my latter example [array]::reverse($letters)?
NOTE: the only difference between those two examples is how I created the array. So either the return type of get-content is not an array, or their is something uniquely different about the returned array that I am missing.
The [array]::reverse() method does not output the array in reverse, it simply reverses it in place, so given:
$x = 'A','B','C'
[array]::reverse($x)
$x
That will output:
C
B
A
If you want to have a second variable that is a reverse of an existing array you can clone the array to the second variable, and then reverse the array contained in the second variable:
$x = 'A','B','C'
$y = $x.clone()
[array]::reverse($y)
At this point $x = 'A', 'B', 'C' while $y = 'C', 'B', 'A'.
I thought I would share that I was able to accomplish my goal using a different line of code.
$data = ($data[($data.Length-1)..0])
This reversed my Get-Content return object. But it doesn't answer my question why the reverse method returns null.
I've been beating myself up on this one all day and can't seem to figure it out... Here's my code real quick
C:\temp\biosconfigutility.exe --% /get:C:\temp\currentbios.txt
$bios = Get-Content C:\temp\currentbios.txt
$arr1 = [array]::IndexOf($bios, "TPM Device")
$arr2 = $arr1 + 1
$arr3 = $arr2 + 1
$bios[$arr3] -replace "\*Available", "Available"
$bios[$arr2] -replace "Hidden", "*Hidden"
So, I don't think I'm going about this in a way that makes sense or is necessarily efficient. But basically what's happening, is the first line generates a txt document that I need to programmatically change for roughly 10,000 PCs.
I need to find a way to make the changes seen at the bottom 2 lines and save that change to the overall array (the variable $bios).
What am I missing here?
Your problem:
$bios[$arr3] -replace "\*Available", "Available"
does not update $bios[$arr3] with the result of the -replace operation - instead, it simply outputs the result.
Generally, if you want the result of a -replace operation to update a variable, you must use an assignment with that variable as the LHS, in addition to using it as an operand:
$bios[$arr3] = $bios[$arr3] -replace '\*Available', 'Available'
$bios[$arr2] = $bios[$arr2] -replace 'Hidden', '*Hidden'
Note that PowerShell has a shorthand notation for some operators for applying the operator to a variable and updating it with the result of the operation (e.g., $v += 1 as shorthand for $v = $v + 1), but no such shortcut exists for -replace.
Well, I figured it out immediately after asking for help...
I changed the following:
$bios[$arr3] -Replace "\*Available","Available"
$bios[$arr2] -Replace "Hidden","*Hidden"
To:
$bios[$arr3] = " Available"
$bios[$arr2] = " *Hidden"
The first solution would replace the entire Array with just that one word, ruining the whole thing... But using the = sign, just that index called was changed, solving my problem
I'm trying to replace all links within an array from this format "\\domain\share" to this "\\domain\share" to have it HTML compatible (without the double single quotes, that's a formatting issue here on StackOverflow).
$arrayJobError = #()
$arrayJobError = "ERROR | Path doesn't exist: UNC, `"\\doman\Share`""
$arrayJobError += "This path `"\\doman\Share`" isn't right!"
I was trying like this $arrayJobError -match "\"*`"" but it's not really fool proof and I don't really know what the best way would be to replace only that piece within the array?
Any help is appreciated as I'm a noob in string manipulation.
I'm not sure I understand exactly what you are doing with $arrayJobError, the way you define it above $arrayJobError becomes a concatenated string rather than array of strings. Here is what I think you are after:
$arrayJobError = #("ERROR | Path doesn't exist: UNC, `"\\doman\Share`"","This path `"\\doman\Share`" isn't right!")
$regex = '"(.+?)"'
$arrayJobError -replace $regex, "`"`$1`""
Result:
ERROR | Path doesn't exist: UNC, ""\\doman\Share""
This path ""\\doman\Share"" isn't right!
If you are after extracting the paths and adding html tags use this, it will process all matches:
[regex]::matches($arrayJobError, $regex).Value | % {
"`"" + $_ + "`""
}
Result:
"\\doman\Share"
"\\doman\Share"