uUable to use the output of the command "SFC /scannow" in powershell - string

What I have done in powershell:
$SFC = SFC /scannow
write-output $SFC | out-file d:\cours\powershell\SFC.txt
$string = 'd i d n o t f i n d a n y i n t e g r i t y v i o l a t i o n s'
$test = $false
$SFC | foreach {If ($_.name -match $string) {$test = $true}}
if ($test -eq $false) {write-host 'Ok'}
What is in the file 'SFC.txt' is unreadable with notepad++.
More, $test is always equal to $false (and that's not true).
What to do to save the output correctly in my file in order to be able to read it and what to do for testing this output?

Out-File defaults to writing in Unicode. You can you change that with -Encoding ASCII.
You can also use Set-Content which defaults to ASCII.
You might also be able to tell Notepad++ to interpret the file as Unicode, but I'm not familiar with how to do that.

Be sure tbat before posting this issue, I have tried all the options of the parameter encoding.
-encoding ASCII is not the trick.
I can only read correctly the output on the console with the write-host command. That's all.
In addition, I am unable to find any string in the ouput of this stupid command "SFC /scannow".
That's very strange. when I change the command "SFC" by an other (for example: DIR), The script works like a charm.
You can verify yourself but you must run powershell ise as an administrator.
What is the diference between SFC command and all others?

I've been playing with this as well and found that -match works. It uses regex, so you can use a period to substitute any single character.
Try:
$string -match '.f.i.n.d...a.n.y...i.n.t.e.g.r.i.t.y...v.i.o.l.a.t.i.o.n.s.'

Sfc outputs unicode no bom. Here's a workaround that deletes nulls and blank lines:
(sfc) -replace "`0" | where {$_}

I've been having this problem too... As stated before, SFC outputs UNICODE which is two bytes per character (which is why the output is all spaced out, the first character of every pair is empty, binary 00000000 or in notepad++ it shows as NULL)
To get out of this you have to make powershell expect Unicode (and when you are finished reset it to how it was before)
In my case I was calling SFC /SCANNOW from the command prompt and saving the output to a temp file. I prefer to use tee-object as it shows the output onto the console as well
POWERSHELL "$prev = [console]::OutputEncoding"; "[console]::OutputEncoding = [Text.Encoding]::Unicode"; "SFC /scannow | Tee-Object -filepath %tempfile%"; "[console]::OutputEncoding = $prev"
Tidying this up into a powershell script (and changing the output file to match the question):
$prev = [console]::OutputEncoding
[console]::OutputEncoding = [Text.Encoding]::Unicode
SFC /scannow | Tee-Object -filepath d:\cours\powershell\SFC.txt
[console]::OutputEncoding = $prev
There is a potential flaw in this if SFC crashes - leaving Powershell in Unicode (although this is untested)

Related

Version strings to "System.Version" too long (or short) in PowerShell

How can I force conversion to type System.Version in PowerShell, or more likely, better understand why I cannot arbitrarily assign number strings type System.Version?
We ingest some software updates in folders whose titles include version numbers. In trying to get reports on what the latest versions ingested are, I have been doing the following quick and dirty:
ForEach ($Folder in $(Get-ChildItem -Path $SoftwareDirectory -Directory))
{
$CurrentVersion = $Folder -Replace "[^0-9.]"
If ($CurrentVersion -ne $null)
{
If ([System.Version]$CurrentVersion -gt [System.Version]$MaxVersion)
{
$MaxVersion = $CurrentVersion
$MaxFolder = $Folder
}
}
}
This would be fed directory titles like the following,
foo-tools-1.12.file
bar-static-3.4.0.file
Most of the time, this is acceptable. However, when encountering some oddballs with longer numbers, like the following,
applet-4u331r364.file
In which case, System.Version refuses the resulting string as being too long.
Cannot convert value "4331364" to type "System.Version". Error: "Version string portion was too short or too long."
You need to ensure that your version strings have at least two components in order for a cast to [version] to succeed:
(
#(
'oo-tools-1.12.file'
'bar-static-3.4.0.file'
'applet-4u331r364.file'
) -replace '[^0-9.]'
).TrimEnd('.') -replace '^[^.]+$', '$&.0' | ForEach-Object { [version] $_ }
The above transforms 'applet-4u331r364.file' into '4331364.0', which works when cast to [version].
Note that you can avoid the need for .TrimEnd('.') if you exclude the filename extension to begin with: $Folder.BaseName -replace '[^0-9.]'
-replace '^[^.]+$', '$&.0' matches only strings that contain no . chars., in full, i.e. only those that don't already have at least two components; replacement expression $&.0 appends literal .0 to the matched string ($&).
Output (via Format-Table -AutoSize):
Major Minor Build Revision
----- ----- ----- --------
1 12 -1 -1
3 4 0 -1
4331364 0 -1 -1

Text Replacement - Strings & spaces

My company has deployed Splunk to gather logs, and report system changes.
Splunk uses two files - inputs.conf and server.conf to track the existing host name and report it to Splunk control.
If a host name changes (common in our environment) the system reports a "Broken Host Sanity check" Requiring that we log into each system and replace the old host name, with the new one.
This is difficult with 1000 systems in the field.
I want to automate this process, and I'm having issues with the script I've written (see below).
Both the inputs and server use this format for the .conf files (.conf is just a txt file with a .conf extension).
Host = systemname
The script below will currently read the text file, and then instead of replacing 'systemname' it will append the changes to the end of the existing value. ie. instead of hostname1 ⇔ hostname2 it does Hostname2Hostname1.
$InputsOLD = "host = *"
$InputsNEW = "host = $Env:COMPUTERNAME"
Get-Content "C:\Program Files\SplunkUniversalForwarder\etc\system\local\inputs.conf" |
Foreach-Object {$_ -replace "$InputsOLD","$InputsNEW"} |
Set-Content "C:\Program Files\SplunkUniversalForwarder\etc\system\local\inputs_1.conf"
$ServerOLD = "serverName = *"
$ServerNew = "serverName = $Env:COMPUTERNAME"
Get-Content "C:\Program Files\SplunkUniversalForwarder\etc\system\local\server.conf" |
Foreach-Object {$_ -replace "$ServerOLD","$ServerNew"} |
Set-Content "C:\Program Files\SplunkUniversalForwarder\etc\system\local\server_1.conf"
The -replace operator matches based on a regular expression. So your match expression:
"host = *"
will end its match after it matches its first "host = " not including the host name, and that's what gets replaced, leaving everything intact afterwards. To include the host name in the match expression, use this regular expression:
"host = .*"
I'd use a regular expression which uses a zero length assertion
to match anything on the line following the keyword (even a literal asterisk)
This script changes inplace, using the same file name on save.
## Q:\Test\2019\01\18\SO_54259368.ps1
Push-Location "C:\Program Files\SplunkUniversalForwarder\etc\system\local"
$Clie = "inputs.conf"
$Serv = "server.conf"
(Get-Content $Clie) -replace "(?<=^host = ).*$",$Env:COMPUTERNAME | Set-Content $Clie
(Get-Content $Serv) -replace "(?<=^serverName = ).*$",$Env:COMPUTERNAME | Set-Content $Serv
Pop-Location

how to add string to the start of each chunk of fixed-line text file in PowerShell?

I have a text file which is comprised of only one line. I have had much trouble with splitting the file into a specific number of characters, then adding a string in front of each chunk of characters.
With a multi-line file, I can add characters to each line very easily using
Get-Content -Path $path | foreach-object {$string + $_} | out-file $output but it is much more complicated with a file with only one line.
For example, if I had a file with these random characters,
(******************************************) and i wanted to add a string to the start of every 10 chars, then it would look like this, (examplestring**********examplestring**********examplestring**********) and so on. I have researched everywhere but I have just managed to add the chars to the end of each chunk of characters.
Does anyone have a way of doing this? Preferably using streamreader and writer as get-content may not work for very large files. Thanks.
Hmm, there are some dynamic parameters applicable to file-system get-content and set-content commands that are close to what you are asking for. For example, if test.txt contains a number of * characters, you might interleave every four * with two + characters with something like this:
get-content .\test.txt -Delimiter "****" | % { "++$_" } | Set-Content test2.txt -NoNewline
I don't know how close that is to a match for what you want, but it's probably useful to know that some of these provider-specific parameters, like '-Delimiter' aren't obvious. See https://technet.microsoft.com/en-us/library/hh847764.aspx under the heading 'splitting large files'.
Alternatively, here's a quick function that reads length-delimited strings from a file.
Set-StrictMode -Version latest
function read-characters( $path, [int]$charCount ) {
begin {
$buffer = [char[]]::new($charCount)
$path = Join-Path $pwd $path
[System.IO.StreamReader]$stream = [System.IO.File]::OpenText($path)
try {
while (!$stream.EndOfStream) {
$len = $stream.ReadBlock($buffer,0,$charCount);
if ($len) {Write-Output ([string]::new($buffer,0,$len))}
}
} catch {
Write-Error -Exception $error[0]
} finally {
[void]$stream.Close()
}
}
}
read-characters .\test.txt -charCount 10 |
% {"+$_"} |
write-host -NoNewline
It could use some parameter checking, but should get you started...
With a manageable file size, you might want to try something like this:
$directory = "C:\\"
$inputFile = "test.txt"
$reader = new-object System.IO.StreamReader("{0}{1}" -f ($directory, $inputFile))
# prefix string of each line
$startString = "examplestring"
# how many chars to put on each line
$range = 10
$outputLine = ""
$line = $reader.ReadLine()
$i = 0
while ($i -lt $line.length) {
$outputLine += $($startString + $line.Substring($i, [math]::min($range, ($line.length - $i))))
$i += $range
}
$reader.Close()
write-output $outputLine
Basically it's using substring to cut out each chunk, prefixing the chumk with given string, and appending to the result variable.
Sample input:
==========================
Sample output:
examplestring==========examplestring==========examplestring======

Why file path now getting concatenate in variable

I am writing powershell script to find few string in a path. It returns file path correctly in variable. Now if I try to add it to some other variable it adds empty (adds nothing), but if I print variable separately it gets print correctly.
My code,
$final = ""
foreach($e in $stringIDColum)
{
$e
$var = (Get-ChildItem “C:\path” -recurse -exclude $excluded | Select-String -pattern $e | group path | select name)
$final += "," + $e + "," + $var
}
Here value of $e is getting added, $var is not getting added. But if I print $var separately it prints path correctly.
Secondly I have tried various ways, to print line number, but it does not print line number in front of path.
I think the issue is that $var is potentially an array. So you need to convert it a string. Check the type once it's created.

How do I delay expansion of variables in PowerShell strings?

Whatever you want to call it, I'm trying to figure out a way to take the contents of an existing string and evaluate them as a double-quoted string. For example, if I create the following strings:
$string = 'The $animal says "meow"'
$animal = 'cat'
Then, Write-Host $string would produce The $animal says "meow". How can I have $string re-evaluated, to output (or assign to a new variable) The cat says "meow"?
How annoying...the limitations on comments makes it very difficult (if it's even possible) to include code with backticks. Here's an unmangled version of the last two comments I made in response to zdan below:
----------
Actually, after thinking about it, I realized that it's not reasonable to expect The $animal says "meow" to be interpolated without escaping the double quotes, because if it were a double-quoted string to begin with, the evaluation would break if the double quotes weren't escaped. So I suppose the answer would be that it's a two step process:
$newstring = $string -replace '"', '`"'
iex "`"$string`""
One final comment for posterity: I experimented with ways of getting that all on one line, and almost anything that you'd think works breaks once you feed it to iex, but this one works:
iex ('"' + ($string -replace '"', '`"') + '"')
Probably the simplest way is
$ExecutionContext.InvokeCommand.ExpandString($var)
You could use Invoke-Expression to have your string reparsed - something like this:
$string = 'The $animal says `"meow`"'
$animal = 'cat'
Invoke-Expression "Write-Host `"$string`""
Note how you have to escape the double quotes (using a backtick) inside your string to avoid confusing the parser. This includes any double quotes in the original string.
Also note that the first command should be a command, if you need to use the resulting string, just pipe the output using write-output and assign that to a variable you can use later:
$result = Invoke-Expression "write-output `"$string`""
As noted in your comments, if you can't modify the creation of the string to escape the double quotes, you will have to do this yourself. You can also wrap this in a function to make it look a little clearer:
function Invoke-String($str) {
$escapedString = $str -replace '"', '`"'
Invoke-Expression "Write-Output `"$escapedString`""
}
So now it would look like this:
# ~> $string = 'The $animal says "meow"'
# ~> $animal = 'cat'
# ~> Invoke-String $string
The cat says "meow"
You can use the -f operator. This is the same as calling [String]::Format as far as I can determine.
PS C:\> $string = 'The {0} says "meow"'
PS C:\> $animal = 'cat'
PS C:\> Write-Host ($string -f $animal)
The cat says "meow"
This avoids the pitfalls associated with quote stripping (faced by ExpandString and Invoke-Expression) and arbitrary code execution (faced by Invoke-Expression).
I've tested that it is supported in version 2 and up; I am not completely certain it's present in PowerShell 1.
Edit: It turns out that string interpolation behavior is different depending on the version of PowerShell. I wrote a better version of the xs (Expand-String) cmdlet with unit tests to deal with that behavior over here on GitHub.
This solution is inspired by this answer about shortening calls to object methods while retaining context. You can put the following function in a utility module somewhere, and it still works when you call it from another module:
function xs
{
[CmdletBinding()]
param
(
# The string containing variables that will be expanded.
[parameter(ValueFromPipeline=$true,
Position=0,
Mandatory=$true)]
[string]
$String
)
process
{
$escapedString = $String -replace '"','`"'
$code = "`$ExecutionContext.InvokeCommand.ExpandString(`"$escapedString`")"
[scriptblock]::create($code)
}
}
Then when you need to do delayed variable expansion, you use it like this:
$MyString = 'The $animal says $sound.'
...
$animal = 'fox'
...
$sound = 'simper'
&($MyString | xs)
&(xs $MyString)
PS> The fox says simper.
PS> The fox says simper.
$animal and $sound aren't expanded until the last two lines. This allows you to set up a $MyString up front and delay expansion until the variables have the values you want.
Invoke-Expression "`"$string`""

Resources