Seaching windows environment for a specific value - powershell

I want to search the windows environment variables for a specific string
Get-ChildItem -Path Env: | TEE-OBJECT -variable newvar1 | grep windir $newvar1
It works for first time
STDIN
windir C:\WINDOWS
and then fails the subsequent times
grep: Could not open 'System.Collections.DictionaryEntry'
How do i remove old variables from the dictionary (if that is the problem)?

Part 1 - Root Cause
Your core problem is grep windir $newvar1 - the command line parameters for grep are (from https://man7.org/linux/man-pages/man1/grep.1.html):
SYNOPSIS
grep [OPTION...] PATTERNS [FILE...]
DESCRIPTION
grep searches for PATTERNS in each FILE.
You're asking grep to search in a file ($newvar1), not the input stream. Each entry in $newvar1, gets serialised as the literal string System.Collections.DictionaryEntry so grep is basically looking for a file called System.Collections.DictionaryEntry, which doesn't exist.
Part 2 - Solution
The best bet is to go full idiomatic PowerShell and use #JPBlanc's answer, but if you're really wedded to grep just remove the trailing $newvar to use the input stream (stdin) instead of a file for input:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir
or, if you don't actually need the values stored in $newvar1 for downstream processing, just simply:
Get-ChildItem -Path Env: `
| grep windir
Part 3 - Why no error the first time?
Your original command works the first time because $newvar1 isn't defined yet, so it's equivalent to:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir $null
... so grep is defaulting to searching the input stream (stdin) rather than a file.
You can confirm this if you enable strict mode - you'll get this error from PowerShell instead:
Set-StrictMode -Version "Latest";
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir $newvar1
# InvalidOperation:
# Line |
# 3 | | grep term $newvar1
# | ~~~~~~~~
# | The variable '$newvar1' cannot be retrieved because it has not been set.
The second time you run the commnad, $newvar1 is already initialised with the results from the first time, so it's equivalent to:
Get-ChildItem -Path Env: `
| TEE-OBJECT -variable newvar1 `
| grep windir "System.Collections.DictionaryEntry"
which, as we've seen, tells grep to look for a file called System.Collections.DictionaryEntry, and results in an error.
Part 4 - More details
Note that $newvar1 isn't defined in the first call to grep because Tee-Object only creates the variable in its End block once it's processed all of its pipeline input (see the source code for Tee-Object.cs on GitHub), which doesn't happen until the entire pipeline has been processed, including the downstream calls to grep.
Not that it's very useful, but you can force the first command to fail by doing this:
(Get-ChildItem -Path Env: | TEE-OBJECT -variable newvar1) `
| grep windir $newvar1
# /usr/bin/grep: System.Collections.DictionaryEntry: No such file or directory
Wrapping the first two expressions in a Grouping Operator forces the pipeline inside to be fully evaluated first, which means Tee-Object's End block creates the $newvar1 variable before grep is invoked, and we get the file-searching behaviour instead of the input stream behaviour.

You an try :
Get-ChildItem -Path Env: | Where-object {$_.value -like 'C:\Users*'}
Get-ChildItem returns a list of objects with properties Name and value. Then Where-object, allow you to filter on these properties, $_ representthe object, you can choose the operator (in this case -like) in the list of Powershell operators).

Related

How to get the first word of output from a PowerShell command

I am trying to get first word from the output of this powershell command
Get-ChildItem -Path Cert:\Certificate::LocalMachine\My | findstr -i ecimas
Which is returning output like:
ffdrggjjhj ecims.example.com
How can I return the string "ffdrggjjhj" only?
You should just be able to split the output like so:
(Get-ChildItem -Path Cert:\Certificate::LocalMachine\My | findstr -i ecimas).split()[0]
Usually powershell looks more like this. Since there's objects, parsing isn't needed.
get-childitem Cert:\LocalMachine\TrustedPublisher | where subject -match wireless |
select -expand thumbprint
ABCDEFABCDEFABCDEFABCDEFABCDEFABCDEFABCD

Writing console output to a file - file is unexpectedly empty

I'm new to scripting and I am trying to write the information returned about a VM to a text file. My script looks like this:
Connect-VIServer -Server 192.168.255.255 -Protocol https -User xxxx -Password XXXXXX
Get-VM -Name xxxxxx
Get-VM xxxxx | Get-HardDisk | Select Parent, Name, Filename, DiskType, Persistence | FT -AutoSize
Out-File -FilePath C:Filepath
I am able to connect to the VM, retrieve the HDD info and see it in the console. The file is created where I want it and is correctly named. No data is ever put into the file. I have tried Tee-Object with the same results. I've also tried the -append switch. I did see a post about the data being returned as an array and Powershell is not able to move the data from an array to a string. Do I need to create a variable to hold the returned data and write to file from there?
Thanks
Guenther Schmitz' answer is effective, but it's worth explaining why:
Your Out-File -FilePath C:Filepath is a stand-alone command that receives no input.
An Out-File call with no input simply creates an empty file (0 bytes).
In order for cmdlets such as Out-File to receive input from (an)other command(s) (represented as ... below), you must use the pipeline, which means that you must place a | after the input command(s) and follow it with your Out-File call:Note that I'm using the shorter -Path parameter alias for the less commonly used -FilePath[1]
... | Out-File -Path C:Filepath
In the simplest case, as above, the entire command (pipeline) is placed on the same line; if you want to spread it across multiple lines for readability, you have have two choices:
Put a line break immediately after |, which tells PowerShell that the command continues on the next line:
... |
Out-File -Path C:Filepath
End a line with an explicit line continuation, which means placing ` at the very end of a line:
... `
| Out-File -Path C:Filepath
Alternatively, since you're using Out-File with its default behavior, you could use >, an output redirection, instead:
... > C:Filepath
A couple of asides:
Using Out-File with something other than strings, and using Format-* cmdlets in general, means that the output is only suitable for display (human consumption), not for further programmatic processing.
If you want to send output to both the console and a file, use the Tee-Object cmdlet, as TobyU suggests:
... | Tee-Object -Path C:Filepath
[1] Strictly speaking, -LiteralPath is the best choice in this case, because -Path interprets its arguments as wildcard expressions. However, omitting -Path, i.e. specifying the file path as a positional argument, as is common, implicitly binds to -Path.
try this:
Get-VM xxxxx |
Get-HardDisk |
Select Parent, Name, Filename, DiskType, Persistence |
Out-File -FilePath C:\Filepath

gsutil - cp not processing each item in the pipeline

I am trying to create a small Powershell script which will copy a list of files matching a specific condition to a specified GCP Storage Bucket. I have gotten this far:
Get-ChildItem $Path | Where-Object { $_.psiscontainer -and $_.LastWriteTime -gt $Age } | Select-Object -ExpandProperty FullName | ft -hidetableheaders | gsutil -m cp -L log.log -r -n -I gs://bucket
But this only uploads the contents of the first folder in the list. I've tried using a foreach-object on the gsutil command, but I get an error due to not finding a URL to upload. When writing the output of the foreach to the console, the output appears to be completely empty.
I have confirmed that the entire line minus the gsutil command returns the correct folders from the path, so I know that the data is going into the pipeline. But I'm not sure why gsutil is only considering the first item in the pipeline.
Any assistance would be greatly appreciated, and thank you in advance!
My "c:\temp" folder has two child folders. When I run Get-ChildItem "c:\temp" | Where-Object {$_.psiscontainer} | Select-Object -ExpandProperty FullName | ft -hidetableheaders Powershell does output the names of the two child folders in c:\temp to the console:
C:\temp\child folder A
C:\temp\child folder B
However, if you capture the output of the command and examine the data type of each path output, you'll see that they are not strings, and I think gsutil requires a string as input when using -I (I think Ansgar Wiechers's comment is correct)
Run this:
$x = Get-ChildItem "c:\temp"| Where-Object {$_.psiscontainer} | Select-Object -ExpandProperty FullName | ft -hidetableheaders
write-host $x.Count
$x[0] | get-member
In my case, I see a count of 2 as expected (two child folders)
However, the datatype of the first item is not a string, it is a FormatEntryData:
$x[0] | get-member
shows the following on the console:
TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
If you remove the | ft -hidetableheaders portion of your command, the data type of each item is a string
Run this:
$x = Get-ChildItem "c:\temp"| Where-Object {$_.psiscontainer} | Select-Object -ExpandProperty FullName
write-host $x.Count
$x[0] | get-member
You'll see a data type of TypeName: System.String for $x[0]
Does this work with gsutil?
As Ansgar Wiechers already said, do not use Format-* cmdlets unless you have a specific need to display formatted output to a user. If it still copies just the first directory the parameter -I may not work as it should. Try ... | ForEach-Object { gsutil -m cp -n -r $_ gs://... } instead.

why sls aka select-string does not work

I am newbie on powershell. Today I tried something very simple
Alias | sls -Pattern 'echo'
which produced echo, but what I want is Alias echo -> Write-Out.
In bash, you can just do
alias | grep 'echo'
My question is why sls does not work. BTW, if I replace sls with findstr, it worked.
If you want to get an alias with a particular name you can do:
alias -name echo
The echo -> Write-Out is the DisplayName:
(alias -name echo).DisplayName
The Get-Alias command returns a sequence of objects, each of which represents a single command alias. When displayed in powershell, these objects are formatted as a table containing the CommandType, Name and ModuleName properties.
When you pipe into findstr, it is the string representations of these columns which are being filtered, so any match displays the whole table row:
Alias echo -> Write-Output
When you pipe into Select-String each object is being bound to the -InputObject parameter of the Select-String cmdlet. Since Select-String operates on text, it just calls ToString on the received object to get its string representation.
ToString only returns the Name property. You can see this by executing the following:
alias | %{$_.tostring()}
Therefore any matches from Select-String only match on the alias name.
select-string only behaves like grep when used with a text file. With a powershell object, the behavior changes (as Lee explained in his answer).
This can be demonstrated with:
alias > out.txt; cat out.txt | sls -pattern 'echo'
Which returns Alias echo -> Write-Output because now slsis operating on a text file.
The other solutions to do what you want are:
alias | where DisplayName -like '*echo*'
alias | out-string -stream | sls -pattern 'echo'
This converts the powershell object to a string so that sls works like grep.

equivalent of (dir/b > files.txt) in PowerShell

dir/b > files.txt
I guess it has to be done in PowerShell to preserve unicode signs.
Get-ChildItem | Select-Object -ExpandProperty Name > files.txt
or shorter:
ls | % Name > files.txt
However, you can easily do the same in cmd:
cmd /u /c "dir /b > files.txt"
The /u switch tells cmd to write things redirected into files as Unicode.
Get-ChildItem actually already has a flag for the equivalent of dir /b:
Get-ChildItem -name (or dir -name)
Simply put:
dir -Name > files.txt
In PSH dir (which aliases Get-ChildItem) gives you objects (as noted in another answer), so you need to select what properties you want. Either with Select-Object (alias select) to create custom objects with a subset of the original object's properties (or additional properties can be added).
However in this can doing it at the format stage is probably simplest
dir | ft Name -HideTableHeaders | Out-File files.txt
(ft is format-table.)
If you want a different character encoding in files.txt (out-file will use UTF-16 by default) use the -encoding flag, you can also append:
dir | ft Name -HideTableHeaders | Out-File -append -encoding UTF8 files.txt
Since powershell deals with objects, you need to specify how you want to process each object in the pipe.
This command will get print only the name of each object:
dir | ForEach-Object { $_.name }
Just found this great post, but needed it for sub directories as well:
DIR /B /S >somefile.txt
use:
Get-ChildItem -Recurse | Select-Object -ExpandProperty Fullname | Out-File Somefile.txt
or the short version:
ls | % fullname > somefile.txt
I am using:
(dir -r).FullName > somefile.txt
and with filter for *.log:
(dir -r *.log).FullName > somefile.txt
Note:
dir is equal to `gci` but fits the naming used in cmd
-r recursive (all subfolders too)
.FullName is the path only