This simple PowerShell script "fills in" the bad sectors of a USB flash drive. But I need to fill the drive with files containing 0x00 (all bits zero) and 0x255 (all bits 1). How can I use hex in PowerShell?
function filler {
Param( [byte]$hex )
$filearray = #()
$count = 1
$freespace = Get-PSDrive H
$maxfiles = [int]($freespace.Free / 1048576)
do {
$randomnum = Get-Random -Minimum 100000000 -Maximum 999999999
"$hex" * 1048576 | Out-File $randomnum
$filecontent = Get-Content $randomnum -Raw
if ($filecontent -notcontains ('$hex')) {
# do nothing because the content is incorrect
} else {
$filearray += $randomnum
}
$count++
} while ($count -le $maxfiles)
foreach ($filename in $filearray) {
Remove-Item $filename
}
}
filler -hex 0x00
filler -hex 0xFF
The string "0x00" is different from the numeric value represented by 0x00.
Make sure you specify -Encoding Byte when trying to read and write raw data to/from files:
,$hex * 1048576 | Set-Content $randomnum -Encoding Byte
And, similarly when reading from files:
$filecontent = Get-Content $randomnum -Encoding Byte
if($filecontent |Where-Object {$_ -notin #(0x00,0xFF)}){
# do nothing
}
Related
I am having a text file that has content in this manner.
One;Thomas;Newyork;2020-12-31 14:00:00;0
Two;David;London;2021-01-31 12:00:00;0
Three;James;Chicago;2021-01-20 15:00:00;0
Four;Edward;India;2020-12-25 15:00:00;0
In these entries according to date time, two are past entries and two are future entries. The last 0 in the string indicates the Flag. With the past entries that flag needs to be changed to 1.
Consider all the entries are separated with the array. I tried this block of code but its not working to solve the problem here.
for ($item=0 ; $item -lt $entries.count ; $item++)
{
if ($entries.DateTime[$item] -lt (Get-Date -Format "yyyy-MM-dd HH:mm:ss"))
{
$cont = Get-Content $entries -ErrorAction Stop
$string = $entries.number[$item] + ";" + $entries.name[$item] + ";" +
$entries.city[$item]+ ";" + $entries.DateTime[$item]
$lineNum = $cont | Select-String $string
$line = $lineNum.LineNumber + 1
$cont[$line] = $string + ";1"
Set-Content -path $entries
}
}
I am getting errors with this concept.
Output should come as:-
One;Thomas;Newyork;2020-12-31 14:00:00;1 ((Past Deployment with respect to current date)
Two;David;London;2021-01-31 12:00:00;0
Three;James;Chicago;2021-01-20 15:00:00;0
Four;Edward;India;2020-12-25 15:00:00;1 (Past Deployment with respect to current date)
This output needs to be overwritten on the file from where the content is extracted ie Entries.txt
param(
$exampleFileName = "d:\tmp\file.txt"
)
#"
One;Thomas;Newyork;2020-12-31 14:00:00;0
Two;David;London;2021-01-31 12:00:00;0
Three;James;Chicago;2021-01-20 15:00:00;0
Four;Edward;India;2020-12-25 15:00:00;0
"# | Out-File $exampleFileName
Remove-Variable out -ErrorAction SilentlyContinue
Get-Content $exampleFileName | ForEach-Object {
$out += ($_ -and [datetime]::Parse(($_ -split ";")[3]) -gt [datetime]::Now) ? $_.SubString(0,$_.Length-1) + "1`r`n" : $_ + "`r`n"
}
Out-File -InputObject $out -FilePath $exampleFileName
This code returns unique and shared lines between two files. Unfortunately, it runs forever if the files have 1 million lines. Is there a faster way to do this (e.g., -eq, -match, wildcard, Compare-Object) or containment operators are the optimal approach?
$afile = Get-Content (Read-Host "Enter 'A' file")
$bfile = Get-Content (Read-Host "Enter 'B' file")
$afile |
? { $bfile -notcontains $_ } |
Set-Content lines_ONLY_in_A.txt
$bfile |
? { $afile -notcontains $_ } |
Set-Content lines_ONLY_in_B.txt
$afile |
? { $bfile -contains $_ } |
Set-Content lines_in_BOTH_A_and_B.txt
As mentioned in my answer to a previous question of yours, -contains is a slow operation, particularly with large arrays.
For exact matches you could use Compare-Object and discriminate the output by side indicator:
Compare-Object $afile $bfile -IncludeEqual | ForEach-Object {
switch ($_.SideIndicator) {
'<=' { $_.InputObject | Add-Content 'lines_ONLY_in_A.txt' }
'=>' { $_.InputObject | Add-Content 'lines_ONLY_in_B.txt' }
'==' { $_.InputObject | Add-Content 'lines_in_BOTH_A_and_B.txt' }
}
}
If that's still too slow try reading each file into a hashtable:
$afile = Get-Content (Read-Host "Enter 'A' file")
$ahash = #{}
$afile | ForEach-Object {
$ahash[$_] = $true
}
and process the files like this:
$afile | Where-Object {
-not $bhash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_A.txt'
If that still doesn't help you need to identify the bottleneck (reading the files, comparing the data, doing multiple comparisons, ...) and proceed from there.
try this:
$All=#()
$All+= Get-Content "c:\temp\a.txt" | %{[pscustomobject]#{Row=$_;File="A"}}
$All+= Get-Content "c:\temp\b.txt" | %{[pscustomobject]#{Row=$_;File="B"}}
$All | group row | %{
$InA=$_.Group.File.Contains("A")
$InB=$_.Group.File.Contains("B")
if ($InA -and $InB)
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_in_A_And_B.txt -Append
}
elseif ($InA)
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_Only_A.txt -Append
}
else
{
$_.Group.Row | select -unique | Out-File c:\temp\lines_Only_B.txt -Append
}
}
Full code for the best option (#ansgar-wiechers). A unique, B unique, and A,B shared lines:
$afile = Get-Content (Read-Host "Enter 'A' file")
$ahash = #{}
$afile | ForEach-Object {
$ahash[$_] = $true
}
$bfile = Get-Content (Read-Host "Enter 'B' file")
$bhash = #{}
$bfile | ForEach-Object {
$bhash[$_] = $true
}
$afile | Where-Object {
-not $bhash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_A.txt'
$bfile | Where-Object {
-not $ahash.ContainsKey($_)
} | Set-Content 'lines_ONLY_in_B.txt'
$afile | Where-Object {
$bhash.ContainsKey($_)
} | Set-Content 'lines_in _BOTH_A_and_B.txt'
Considering my suggestion to do a binary search, I have created a reusable Search-SortedArray function for this:
Description
The Search-SortedArray (alias Search) (binary) searches a string in a sorted array. If the string is found, the index of the found string in the array is returned. Otherwise, if the string is not found, a $Null is returned.
Function Search-SortedArray ([String[]]$SortedArray, [String]$Find, [Switch]$CaseSensitive) {
$l = 0; $r = $SortedArray.Count - 1
While ($l -le $r) {
$m = [int](($l + $r) / 2)
Switch ([String]::Compare($find, $SortedArray[$m], !$CaseSensitive)) {
-1 {$r = $m - 1}
1 {$l = $m + 1}
Default {Return $m}
}
}
}; Set-Alias Search Search-SortedArray
$afile |
? {(Search $bfile $_) -eq $Null} |
Set-Content lines_ONLY_in_A.txt
$bfile |
? {(Search $afile $_) -eq $Null} |
Set-Content lines_ONLY_in_B.txt
$afile |
? {(Search $bfile $_) -ne $Null} |
Set-Content lines_in_BOTH_A_and_B.txt
Note 1: Due to the overhead, a binary search will only give advantage with (very) large arrays.
Note 2: The array has to be sorted otherwise the result will be unpredictable.
Nate 3: The search doesn't account for duplicates. In case of duplicate values, just one index will be returned (which isn't a concern for this specific question)
Added 2017-11-07 based on the comment from #Ansgar Wiechers:
Quick benchmark with 2 files with a couple thousand lines each (including duplicate lines): binary search: 2400ms; compare-object: 1850ms; hashtable lookup: 250ms
The idea is that the binary search will take its advantage on the long run: the larger the arrays the more it will proportional gain performance.
Taken $afile |? { $bfile -notcontains $_ } as an example, the performance measurements in the comment and that “a couple thousand lines” is 3000 lines:
For a standard search, you will need an average of 1500 iterations in the $bfile:*1
(3000 + 1) / 2 = 3001 / 2 = 1500
For a binary search, you will need an average of 6.27 iterations in the $bfile:
(log2 3000 + 1) / 2 = (11.55 + 1) / 2 = 6.27
In both situations you do this 3000 times (for each item in $afile)
This means that each single iteration takes:
For a standard search: 250ms / 1500 / 3000 = 56 nanoseconds
For a binary search: 2400ms / 6.27 / 3000 = 127482 nanoseconds
The breakeven point will at about:
56 * ((x + 1) / 2 * 3000) = 127482 * ((log2 x + 1) / 2 * 3000)
Which is (according my calculations) at about 40000 entries.
*1 presuming that a hashtable lookup doesn’t do a binary search itself as it is unaware that the array is sorted
Added 2017-11-07
Conclusion from the comments: Hash tables appear to have a similar associative array algorithms that can't be outperformed with low-level programming commands.
I try to make, the line from the first array is read from a file and is replaced with a line from the second array, so some times with different lines. I made a script, but I do not understand why it does not work.
$OldStrings = #(
"desktopwidth:i:1440",
"desktopheight:i:900",
"winposstr:s:0,1,140,60,1596,999"
)
$NewStrings = #(
"desktopwidth:i:1734",
"desktopheight:i:990",
"winposstr:s:0,1,50,7,1800,1036"
)
$LinesArray = Get-Content -Path 'C:\temp\My Copy\Default.rdp'
$LinesCount = $LinesArray.Count
for ($i=0; $i -lt $LinesCount; $i++) {
foreach ($OldString in $OldStrings) {
foreach ($NewString in $NewStrings) {
if ($LinesArray[$i] -like $OldString) {
$LinesArray[$i] = $LinesArray[$i] -replace $OldString, $NewString
Write-Host "`nline" $i "takes on value:" $LinesArray[$i] "`n" -ForegroundColor Gray
}
}
}
}
The file is probably why it is not read at all.
After executing the script, I see only
line 2 takes on value: desktopwidth:i:1734
line 3 takes on value: desktopwidth:i:1734
line 5 takes on value: desktopwidth:i:1734
You're looking through the string arrays twice. You want to do two loops, one for each line in the file AND another for each count in the lines you're replacing. I think this should work:
$OldStrings = #(
"desktopwidth:i:1440",
"desktopheight:i:900",
"winposstr:s:0,1,140,60,1596,999"
)
$NewStrings = #(
"desktopwidth:i:1734",
"desktopheight:i:990",
"winposstr:s:0,1,50,7,1800,1036"
)
$LinesArray = Get-Content -Path 'C:\temp\My Copy\Default.rdp'
# loop through each line
for ($i=0; $i -lt $LinesArray.Count; $i++)
{
for ($j=0;$j -lt $OldStrings.Count; $j++)
{
if ($LinesArray[$i] -match $OldStrings[$j])
{
$LinesArray[$i] = $LinesArray[$i] -replace $OldStrings[$j],$NewStrings[$j]
Write-Host "`nline" $i "takes on value:" $LinesArray[$i] "`n" -ForegroundColor Gray
}
}
}
$LinesArray | Set-Content -Path 'C:\temp\My Copy\Default.rdp'
You don't need to bother checking the lines to look for matches. Since you have the replacements ready just do the replacements outright anyway. Should be faster this way as well.
$stringReplacements = #{
"desktopwidth:i:1440" = "desktopwidth:i:1734"
"desktopheight:i:900" = "desktopheight:i:990"
"winposstr:s:0,1,140,60,1596,999" = "winposstr:s:0,1,50,7,1800,1036"
}
$path = 'C:\temp\My Copy\Default.rdp'
# Read the file in as a single string.
$fileContent = Get-Content $path | Out-String
# Iterate over each key value pair
$stringReplacements.Keys | ForEach-Object{
# Attempt the replacement for each key/pair search/replace pair
$fileContent =$fileContent.Replace($_,$stringReplacements[$_])
}
# Write changes back to file.
# $fileContent | Set-Content $path
$stringReplacements is a key value hash of search and replace strings. I don't see you writing the changes back to file so I left a line on the end for you to uncomment.
You could add in checks to do the replacements still if you value the write-host lines but I figured that was for debugging and you already know how to do that.
i have a txt file with this :
1230;
012;
45;
125
and i want to convert this in an int
but is doesn't work... he juste return the last number
here is my code :
$numbertxt = get-content -Path C:\mysticpath\number.txt -Raw
$numbertxt.GetType()
write-host $numbertxt
foreach ($flags in $numbertxt)
{
$integer = [int]$flags
}
echo $integer
somebody can help me ?
Sorry for my english
$numbertxt = (get-content -Path C:\mysticpath\number.txt -Raw) -split ';'
$numbertxt.GetType()
write-host $numbertxt
foreach ($flags in $numbertxt)
{
$integer = [int]$flags
echo $integer
}
First a integer can only be made of numbers so you will need to split the contents by ';'. This will make a array of strings that are numbers.
Also put the echo on the inside of the for loop will allow for it to display each number as its processed
try this method (control if it's convertible to integer before print)
$res=0;
#verbose version
(Get-Content "c:\temp\test.txt") -split ';' | where {[int]::TryParse($_, [ref] $res)} | foreach {$res}
#short version
(gc "c:\temp\test.txt") -split ';' | ?{[int]::TryParse($_, [ref] $res)} | %{$res}
What I want :
open a file, find a match with a regex,increment the match,replace the match in text,save the file.
Is it possible to do this with Powershell or the FINDSTR command ?
"*original file*"
gc matchtest.txt
$match_pat = "^(Match\stext\s)(\d+)"
$newfile = #()
gc matchtest.txt |% {
if ($_ -match $match_pat) {
$incr = 1 + $matches[2]
$newfile += $_ -replace $match_pat,($matches[1] + $incr)
}
else {$newfile += $_}
}
$newfile | out-file matchtest.txt -force
"`n*new file*"
gc matchtest.txt
*original file*
Not match 121
Match text 127
Not match 123
*new file*
Not match 121
Match text 128
Not match 123