select-string not taking variable in powershell - powershell

I have stored a MAC address as a string in a variable:
$macaddress= "1234567"
I'm trying to store the output from a command in another variable:
$testing = { arp -a; | select-string $macaddress }
Write-Host $testing
If the command is executed in PowerShell, I do not see the value of $macaddress. It displays as a '$macaddress' in the screen instead of its value "1234567".
Please help me set the value of $macaddress correctly.

The problem is not how you define variable $macaddress, but in how you try to capture command output.
Remove the enclosing { ... } and the ;:
$testing = arp -a | select-string $macaddress
As for what you tried:
{ ... } creates a script block, which is a piece of source code (loosely speaking) for later execution (e.g., with operators . or &).
If you pass a script block to Write-Host - whose use you should generally avoid, by the way - it is converted to a string, and the string representation is the literal contents of the script block between { and } - that's why you saw $macaddress appear (unexpanded) in your output.
; terminates a command, and it is only necessary if you place multiple commands on a single line.
A pipeline is still considered a single command, even though it is composed of multiple sub-commands; do not attempt to use ; in a pipeline - you'll break it (and, in fact, even your script-block-creating command would break).

Try it this way:
$macAddress = "00-01-02-03-04"
arp -a | Select-String $macAddress
If you want to extract the IP address related to the MAC address, you can do this:
$macAddress = "00-01-02-03-04"
arp -a | Select-String ('\W*((?:[0-9]{1,3}\.){3}(?:[0-9]{1,3}))\W+(' +
$macAddress + ')') | ForEach-Object { $_.Matches[0].Groups[1].Value }

Related

Using subexpression with & in powershell

I am completely new to powershell.
I have a requirement to have a set of commands as a subexpression $() because I want the output of the command to be sent to Out-Host and without $() the if loops create an issue.
Now there might also be a possibility that the command has filenames with spaces and to handle that we append & before the file name in command.
Bacially, $(& file_name) | Out-Host fails saying & here is invalid.
How to go about using $() with &
Works ok for me. You'll need to supply a counterexample. In this case you couldn't pipe from "if" without $() or &{}.
$( If (1 -eq 1) { & echo hi } ) | out-host > file
hi
# no output until it's finished
$( If (1 -eq 1) { & 'c:\program files\internet explorer\iediagcmd' } ) |
out-host > file

Powershell Select-Object: Executing native commands in calculated property

I'm trying to create a report file which lists all files in my Git repository with these columns:
Name
Size
Last Modified
Git Commit
The first 3 are no problem:
gci -File -Recurse | Select-Object -Property #{
label = 'Size(KB)'
expr = { [string]::Format("{0:0.00}", $_.Length/1KB) }
}, LastWriteTime, FullName
However, retrieving the git commit requires running Git, a native command.
I've tried, among others:
gci -File -Recurse | Select-Object -Property #{
label = 'Git commit'
expr = { Invoke-Expression "git log -1 --format:format=`"%s`" -- $($_.FullName)" }
},
#{
label = 'Size(KB)'
expr = { [string]::Format("{0:0.00}", $_.Length/1KB) }
}, LastWriteTime, FullName
But it just gets stuck.
Does anyone know how to do this??
P.S.
All the flags and options to Git doesn't matter for the manner of sake, I just copied what I already did.
As #mklement0 suggested in the comments, the issue was just that your formatting for the --format command was off just enough to cause a problem.
You had:
--format:format="%s" # won't work
--format=format:"%s" # works
So to fix it, we just swap in the right format, giving us this command with the output below.
gci -File | Select-Object -Property #{
label = 'Git commit'
expr = { Invoke-Expression "git log -1 --format=format:`"%s`" $($_.Name)" }
},
#{
label = 'Size(KB)'
expr = { [string]::Format("{0:0.00}", $_.Length/1KB) }
}, LastWriteTime, FullName
Git commit Size(KB) LastWriteTime FullName
---------- -------- ------------- --------
applied gitingore 6.07 5/11/2020 11:22:06 AM C:\git\ClientFaux\.gitignore
cleanin up layout 1.40 10/9/2020 3:20:33 PM C:\git\ClientFaux\ClientFaux.sln
Create LICENSE (#25) 34.98 7/13/2020 9:55:00 AM C:\git\ClientFaux\LICENSE
Update README.md (#27) 3.37 7/13/2020 9:55:00 AM C:\git\ClientFaux\README.md
31.13 7/13/2020 9:55:27 AM C:\git\ClientFaux\UpgradeLog.htm
FoxDeploy's helpful answer contains the crucial pointer, but let me offer a (corrected) PowerShell-idiomatic reformulation of your code.
Get-ChildItem -File -Recurse | Select-Object -Property #{
label = 'Git commit'
expr = { git log -1 --format=format:%s -- $_.FullName }
},
#{
label = 'Size(KB)'
expr = { '{0:0.00}' -f ($_.Length/1KB) }
}, LastWriteTime, FullName
Note:
Invoke-Expression should generally be avoided; definitely don't use it to invoke an external program or PowerShell script, so the above uses direct invocation of git.
As mentioned, --format:format="%s" is syntactically incorrect and should be --format=format:"%s" (I've omitted the " above, which PowerShell would strip behind the scenes anyway).
-f, the format operator, is used as the PowerShell-idiomatic alternative to directly calling the underlying .NET API, System.String.Format.
Calling native executables in calculated properties:
Generally, note that there's nothing fundamentally special about calling native executables from the expression script block ({ ... }) of a calculated property.
Specifically, the following considerations apply:
In PowerShell, stdout output from native executables is invariably converted to text ([string] instances). If the output comprises multiple lines, the property values becomes a (regular [object[]]) array containing strings.
If the native executable produces no stdout output, the value of the property is "nothing", i.e. the so-called "Automation Null" value ([System.Management.Automation.Internal.AutomationNull]::Value) that in most context behaves like $null.
Any stderr output from the native executable is quietly discarded. (Analogously, errors from PowerShell commands or expressions in expression script blocks are quietly ignored.)
Therefore, in order to troubleshoot an expression script block, execute it stand-alone, via a ForEach-Object command (whose built-in alias is %), so as to surface any errors; e.g.:
# Uses broken `git` syntax; note the resulting error message.
PS> (Get-ChildItem -File)[0] | % { git log -1 --format:format=%s -- $_.FullName }
fatal: unrecognized argument: --format:format=%s
As for what you tried:
Because your git command was syntactically incorrect, git only produced stderr output, which PowerShell then ignored, as explained above.
Thus, your Git commit properties ended up containing "nothing", which simply renders blank in output formatting.

Powershell: ConvertFrom-Json doesn't get the variable assigned due to forward and backward compatibility

I am trying to load a variables into powershell from variables.json having the following
{
"psfp": "C:/San\/SV65\/ps",
"vmfp": "'C:/San\/SV65\/hyper-packer\/hyper-packer\/output-centos8-9\/Virtual Machines'",
"psmp": "C:/San\/SV65\/ps",
"vmname": ""
}
Trying to import with
$jvariables=Get-Content -Raw -Path '.\variables-ps.json' | ConvertFrom-Json
Output on powershell
PS C:\San\SV65\ps> Write-host $jvariables.psfp
C:/San/SV65/ps
PS C:\San\SV65\ps> Write-host $jvariables.vmfp
'C:/San/SV65/hyper-packer/hyper-packer/output-centos8-9/Virtual Machines'
PS C:\San\SV65\ps> Write-host $jvariables.psmp
C:/San/SV65/ps
These forward slash not incompatible on powershell for a windows path! ConvertFrom-Json doesn't accept backslash on the variables tried with '' as well
Hence could not load my modules on these path
any other way to achieve the above case?
Want to use json as it easy for the end user to update rather a txt files
Please share
you can loop through the object properties and change the path separator like this:
$jvariables.psobject.properties | where {$_.membertype -eq "NoteProperty"} |
foreach {$data.($_.name) = $_.value.replace("/","\")}
or a simpler approach would be to escape backslash in config file, like this :
{
"psfp": "C:\\San\\SV65\\ps",
"vmfp": "'C:\\San\\SV65\\hyper-packer\\hyper-packer\\output-centos8-9\\Virtual Machines'",
"psmp": "C:\\San\\SV65\\ps",
"vmname": ""
}

Powershell Switch Parameter to add to end of expression

Here's what I'm trying to do:
param([Switch]$myparameter)
If($myparamter -eq $true) {$export = Export-CSV c:\temp\temp.csv}
Get-MyFunction | $export
If $myparameter is passed, export the data to said location. Else, just display the normal output (in other words, ignore the $export). What doesn't work here is setting $export to the "Export-csv...". Wrapping it in quotes does not work.
I'm trying to avoid an if, then statement saying "if it's passed, export this. If it's not passed, output data"
I have a larger module that everything works in so there is a reason behind why I am looking to do it this way. Please let me know if any additional information is needed.
Thank you everyone in advance.
tl;dr:
param([Switch] $myparameter)
# Define the core command as a *script block* (enclosed in { ... }),
# to be invoked later, either with operator . (no child variable scope)
# or & (with child variable scope)
$scriptBlock = { Get-MyFunction }
# Invoke the script block with . (or &), and pipe it to the Export-Csv cmdlet,
# if requested.
If ($myparameter) { # short for: ($myparameter -eq $True), because $myparameter is a switch
. $scriptBlock | Export-Csv c:\temp\temp.csv
} else {
. $scriptBlock
}
TessellatingHeckler's answer is concise, works, and uses a number of advanced features cleverly - however, while it avoids an if statement, as requested, doing so may not yield the best or most readable solution in this case.
What you're looking for is to store a command in a variable for later execution, but your own attempt to do so:
If ($myparameter -eq $true) { $export = Export-CSV c:\temp\temp.csv }
results in immediate execution, which is not only unintended, but fails, because the Export-Csv cmdlet is missing input in the above statement.
You can store a snippet of source code for later execution in a variable via a script block, simply by enclosing the snippet in { ... }, which in your case would mean:
If ($myparameter -eq $true) { $export = { Export-Csv c:\temp\temp.csv } }
Note that what you pass to if is itself a script block, but it is by definition one that is executed as soon as the if condition is found to be true.
A variable containing a script block can then be invoked on demand, using one of two operators:
., the "dot-sourcing" operator, which executes the script block in the current scope.
&, the call operator, which executes the script block in a child scope with respect to potential variable definitions.
However, given that you only need the pipeline with an additional command if switch $myparameter is specified, it's better to change the logic:
Store the shared core command, Get-MyFunction, in a script block, in variable $scriptBlock.
Invoke that script block in an if statement, either standalone (by default), or by piping it to Export-Csv (if -MyParameter was specified).
I'm trying to avoid an if, then statement
Uh, if you insist...
param([Switch]$myparameter)
$cmdlet, $params = (('Write-output', #{}),
('Export-Csv', #{'LiteralPath'='c:\temp\temp.csv'}))[$myparameter]
Get-MyFunction | & $cmdlet #params

In powershell, i want Ioop twice through a text file but in second loop i want to continue from end of first loop

I have a text file to process. Text file has some configuration data and some networking commands. I want to run all those network commands and redirect output in some log file.
At starting of text file,there are some configuration information like File-name and file location. This can be used for naming log file and location of log file. These line starts with some special characters like '<#:'. just to know that rest of the line is config data about file not the command to execute.
Now, before i want start executing networking commands (starts with some special characters like '<:'), first i want to read all configuration information about file i.e. file name, location, overwrite flag etc. Then i can run all commands and dump output into log file.
I used get-content iterator to loop over entire text file.
Question: Is there any way to start looping over file from a specific line again?
So that i can process config information first (loop till i first encounter command to execute, remember this line number), create log file and then keep running commands and redirect output to log file (loop from last remembered line number).
Config File looks like:
<#Result_File_Name:dump1.txt
<#Result_File_Location:C:\powershell
<:ping www.google.com
<:ipconfig
<:traceroute www.google.com
<:netsh interface ip show config
My powerhsell script looks like:
$content = Get-Content C:\powershell\config.txt
foreach ($line in $content)
{
if($line.StartsWith("<#Result_File_Name:")) #every time i am doing this, even for command line
{
$result_file_arr = $line.split(":")
$result_file_name = $result_file_arr[1]
Write-Host $result_file_name
}
#if($line.StartsWith("<#Result_File_Location:"))#every time i am doing this, even for command line
#{
# $result_file_arr = $line.split(":")
# $result_file_name = $result_file_arr[1]
#}
if( $conf_read_over =1)
{
break;
}
if ($line.StartsWith("<:")) #In this if block, i need to run all commands
{
$items = $line.split("<:")
#$items[0]
#invoke-expression $items[2] > $result_file_name
invoke-expression $items[2] > $result_file_name
}
}
If all the config information starts with <# just process those out first separately. Once that is done you can assume the rest are commands?
# Collect config lines and process
$config = $content | Where-Object{$_.StartsWith('<#')} | ForEach-Object{
$_.Trim("<#") -replace "\\","\\" -replace "^(.*?):(.*)" , '$1 = $2'
} | ConvertFrom-StringData
# Process all the lines that are command lines.
$content | Where-Object{!$_.StartsWith('<#') -and ![string]::IsNullOrEmpty($_)} | ForEach-Object{
Invoke-Expression $_.trimstart("<:")
}
I went a little over board with the config section. What I did was convert it into a hashtable. Now you will have your config options, as they were in file, accessible as an object.
$config
Name Value
---- -----
Result_File_Name dump1.txt
Result_File_Location C:\powershell
Small reconfiguration of your code, with some parts missing, would look like the following. You will most likely need to tweak this to your own needs.
# Collect config lines and process
$config = ($content | Where-Object{$_.StartsWith('<#')} | ForEach-Object{
$_.Trim("<#") -replace "\\","\\" -replace "^(.*?):(.*)" , '$1 = $2'
} | Out-String) | ConvertFrom-StringData
# Process all the lines that are command lines.
$content | Where-Object{!$_.StartsWith('<#') -and ![string]::IsNullOrEmpty($_)} | ForEach-Object{
Invoke-Expression $_.trimstart("<:") | Add-Content -Path $config.Result_File_Name
}
As per your comment you are still curious about your restart loop logic which was part of your original question. I will add this as a separate answer to that. I would still prefer my other approach.
# Use a flag to determine if we have already restarted. Assume False
$restarted = $false
$restartIndexPoint = 4
$restartIndex = 2
for($contentIndex = 0; $contentIndex -lt $content.Length; $contentIndex++){
Write-Host ("Line#{0} : {1}" -f $contentIndex, $content[$contentIndex])
# Check to see if we are on the $restartIndexPoint for the first time
if(!$restarted -and $contentIndex -eq $restartIndexPoint){
# Set the flag so this does not get repeated.
$restarted = $true
# Reset the index to repeat some steps over again.
$contentIndex = $restartIndex
}
}
Remember that array indexing is 0 based when you are setting your numbers. Line 20 is element 19 in the string array for example.
Inside the loop we run a check. If it passes we change the current index to something earlier. The write-host will just print the lines so you can see the "restart" portion. We need a flag to be set so that we are not running a infinite loop.