Simplifying PowerShell pipeline splitting data from command output - powershell

I'm trying to do some processing on the output from a psftp "ls" command. Basically, I want to put all of the file names that match certain conditions into an array. I have this:
PS C:\path\to\pwd> $a = & "C:\Program Files (x86)\PuTTY\psftp.exe" -l myusername -batch -pw mypassword -b sftpbatch.txt myserver | where {$_.split(" ", [StringSplitOptions]'RemoveEmptyEntries')[0] -eq "-rw-r--r--"} | select-object {$_.split(" ", [StringSplitOptions]'RemoveEmptyEntries')[8]}
(If you want more details about that command, I can provide them. The output is very similar to the output of the "ls" command in PowerShell.)
It seems to me that I can do better by selecting the split first, then filtering it with where. When I try this:
$a = & <# ... #> | select-object {$_.split(" ", [StringSplitOptions]'RemoveEmptyEntries')} | where { $_[0] -eq "-rw-r--r--" }
I get
Unable to index into an object of type System.Management.Automation.PSObject.
How can I simplify this?

Something like this should work.
$a = & <#...#>| % {$b = $_ -split ' '|?{$_}; if($b[0] -eq '-rw-r--r--'){$b[8]}}
If you are placing this in a script, I would replace the alias % with Foreach-Object, and the alias ? with Where-Object
Edit:
Here is a more pipeline-oriented approach:
'-rw-r--r-- 1 2 3 4 5 6 7 test.txt'|
select #{Name='Words'; Expression={$_ -split ' '|where {$_}}}|
where {$_.Words[0] -eq "-rw-r--r--"}|
foreach {$_.Words[8]}

Related

Fastlane & Powershell two factor authentication

Running fastlane in Powershell and try to get environment variable set by fastlane.
I have the following code, that I am trying to use to get the variable, but the FASTLANE_SESSION is empty after the loop - that is why I try to set an env myself in the loop:
fastlane spaceauth -u user#domain.com 2>&1 | ForEach-Object {
Write-Host $env:FASTLANE_SESSION
return $_
}
Any ideas for solving this?
If the last line of the fastlane output is empty, e.g., line feed(s), that's what you get. Don't have fastlane, so tested with java instead. First test for [string] like your example:
java -version 2>&1 | foreach { if ($_ -is [string]) { return $_; } } | select -Last 1;
no output.
Second test for non-whitespace characters:
java -version 2>&1 | where { $_ -match '\S+' } | select -Last 1;
outputs the currently installed version.
For your specific example something like this should work:
$OUTPUT = fastlane spaceauth -u email#adomain.com 2>&1 |
where { $_ -match '\S+' } |
select -Last 1;

Renaming output of a command in PowerShell

I am using Mp4box in order to split a file into one minute parts, normally Mp4box use file_nnn.mp4 where the n are like 001, 002, 003, ....
I would like to rename it to partn.mp4 where n is also an increasing odd number.
I use this code but it is not working.
Mp4box -split 60 file.mp4 | foreach-
object -begin {$c=1} -process rename-
item $_ -newname "part$c.mp4";
$c=$c+2 }
So lets talk about what you have wrong.
Mp4box -split 60 file.mp4 |
foreach-object -begin {$c=1} -process
rename-item $_ -newname "part$c.mp4";
$c=$c+2
}
This is not a valid powershell statement.
The beginning is fine but after the first pipe | you then use a incomplete foreach-object with the parameters -process and -begin but they are separated by the foreach block where you create a variable $c that cant be seen by the rest of the script because its scope is confined to the foreach. You then have a rename-item that is outside the pipe | and then try to use a piped variable $_ which will be null because it is outside the pipe |. Finally you add 2 to $c which is null becuase its outside the scope of the $c in the foreach. You also add closing bracket } when there is no opening bracket.
Here is a working script which fully depends on the output of Mp4box. If Mp4box is not a powershell command and is instead a executable then this will not work.
$C = 1
Mp4box -split 60 file.mp4 |
%{
rename-item $_ -newname "part$c.mp4"
$C += 2
}
Lets go over whats above. I call $C = 1 outside the foreach so its usable in the foreach scope.
I pipe | the output of Mp4box to a % which is shorthand for foreach-object .
Inside the % (foreach-object) brackets { } it renames the item $_ from the pipe |.
Then it adds 2 to c using shorthand for += which is the same as add to ($C = $C + 2)
Now again this purely relies on if the output of Mp4box.

What is an equivalent of *Nix 'cut' command in Powershell?

I have following content in a configuration file (sample.cfg),
Time_Zone_Variance(Mins):300
Alert_Interval(Mins):2
Server:10.0.0.9
Port:1840
I'm trying to store an each values after the : by using split in PowerShell. but i'm not able to produce require output.
Can someone tell me how to use PowerShell split for the above problem ?
You can read the contents of the file using Get-Content, then pipe each line through ForEach-Object, then use the split command on each line, taking the second item in the array as follows:
$filename = "sample.cfg"
Get-Content $filename | ForEach-Object {
$_.split(":")[1]
}
Output
300
2
10.0.0.9
1840
Update
I prefer the approach by #AnsgarWiechers, but if you really need specifically named values you could create a hashtable and replace the name with the value:
$configValues = #{
hour = "Time_Zone_Variance(Mins)"
min = "Alert_Interval(Mins)"
server = "Server"
port = "Port"
}
Get-Content $filename | ForEach-Object {
# Courtesy of Ansgar Wiechers
$key, $value = $_ -split ':', 2
foreach($configValuesKey in $($configValues.keys)) {
if ($configValues[$configValuesKey] -eq $key)
{
$configValues[$configValuesKey] = $value
}
}
}
write-host "`nAll Values:"
$configValues
write-host "`nIndividual value:"
$configValues.port
Output
All Values:
Name Value
---- -----
port 1840
min 2
server 10.0.0.9
hour 300
Individual value:
1840
How's this?
function cut {
param(
[Parameter(ValueFromPipeline=$True)] [string]$inputobject,
[string]$delimiter='\s+',
[string[]]$field
)
process {
if ($field -eq $null) { $inputobject -split $delimiter } else {
($inputobject -split $delimiter)[$field] }
}
}
PS C:\> 'hi:there' | cut -f 0 -d :
hi
PS C:\> 'hi:there' | cut -f 1 -d :
there
PS C:\> 'hi:there' | cut -f 0,1 -d :
hi
there
PS C:\> 'hi:::there' | cut -f 0 -d :+
hi
PS C:\> 'hi there' | cut
hi
there
For a more succint syntax, this will also do the trick:
((Get-Content "your-file.txt") -Split ":")[1]
So the trick to use the -Split method is to have a String object returned by Get-Content (alias cat can also be used, actually), and from the resulting String[] object you can use the brackets to extract the nth item.
Note: Using -Split without parenthesis around Get-Content won't work since -Split is not a parameter name for that command... 🤷‍♂️
I suppose you don't want to just split the lines, but actually create key/value pairs. That could be achieved like this:
$config = #{}
Get-Content 'C:\path\to\sample.cfg' | % {
$key, $value = $_ -split ':', 2
$config[$key] = $value
}
You could also use the ConvertFrom-StringData cmdlet:
Get-Content 'C:\path\to\sample.cfg' | % {
ConvertFrom-StringData ($_ -replace ':','=')
}
The -replace operation is necessary, because ConvertFrom-StringData expects key and value to be separated by =. If you could change the delimiter in the config file from : to =, you could use ConvertFrom-StringData $_ without replacement.

Foreach loop in Powershell on the console

Very simple question here, I want to see how can we process a bunch of commands using foreach on the command line (not through a PS1 script).
For instance, I display the directory listing on the console, now I want to execute 2 commands per object.
Get-ChildItem | ForEach-Object { Write-Host $_ $_}
This is ok, it shows the filename twice, but lets say I wanted to run 2 Write-Host commands for the same object, would that be possible on the console?
PS: I'm trying to achieve writing an output to 2 files using the out-file cmdlet, so I can read something and have 2 separate out-file calls per object
Thanks
you can script in the console windows just as you would in a powershell file. Use the "`" (backtick) key to separate lines. e.g.:
PS > Write-Host `
>>> hello, world!
So you could do
PS > Get-ChildItem | ForEach-Object { `
>>> Write-Host $_ `
>>> doFoo() `
>>> doBar() `
>>> ` }
Basically you want to execute 2 commands in ForEach-Object statement, right?
Just use ; to separate commands in this way ForEach-Object { command1; command2 }
In your code it should be something like this
Get-ChildItem | ForEach-Object { Write-Host $_; Write-Host $_ }

Powershell V2 find and replace

I am trying to change dates programmatically in a file. The line I need to fix looks like this:
set ##dateto = '03/15/12'
I need to write a powershell V2 script that replaces what's inside the single quotes, and I have no idea how to do this.
The closest I've come looks like this:
gc $file | ? {$_ -match "set ##dateto ="} | % {$temp=$_.split("'");$temp[17]
=$CorrectedDate;$temp -join ","} | -outfile newfile.txt
Problems with this: It gives an error about the index 17 being out of range. Also, the outfile only contains one line (The unmodified line). I'd appreciate any help with this. Thanks!
You can do something like this ( though you may want to handle the corner cases) :
$CorrectedDate = '10/09/09'
gc $file | %{
if($_ -match "^set ##dateto = '(\d\d/\d\d/\d\d)'") {
$_ -replace $matches[1], $CorrectedDate;
}
else {
$_
}
} | out-file test2.txt
mv test2.txt $file -force