Reading and increasing a number in a textfile with ++ or equivalent - powershell

I am trying to read a number from a file with Get-Content and adding it to a variable.
Then i add this number to a string in a file, increase the number by 1, then save that to the file again.
I have tried something like:
$i = Get-Content C:\number.txt
$i++
Set-Content C:\number.txt
The content of number.txt is: 1000
But i get this error:
The '++' operator works only on numbers. The operand is a 'System.String'.
At line:2 char:5
+ $i++ <<<<
+ CategoryInfo : InvalidOperation: (1000:String) [], RuntimeException
+ FullyQualifiedErrorId : OperatorRequiresNumber
Does anyone have any idea of a better way of doing this operation?

I guess you need to convert it to an integer before increment it.
$str = Get-Content C:\number.txt
$i = [System.Decimal]::Parse($str)
$i++
Set-Content C:\number.txt $i

short way:
[decimal]$i = Get-Content C:\number.txt # can be just [int] if content is always integer
$i++
Set-Content C:\number.txt $i

Let's do it in one line:
[decimal] ( $i = Get-Content C:\number.txt ) | % {Set-Content C:\number.txt -value ($_ + 1); return ($_ + 1)}
Returns the value incremented. $i has the value before the increment.

Related

Input string was not in a correct format in powershell

I am facing the error "Input string was not in a correct format." while using the variable in for loop.
$totalrows=$filepath |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}
echo $totalrows
Output is 8 and it is correct.
Used this variable in for loop:
For ($i = 0; $i -lt $totalrows; $i++) {
Write-host $i
}
but i get the error :
8" to type "System.Int32". Error: "Input string was not in a correct format."
So, I looked into SO for same questions so i found to typecast into integer:
$totalrows=$filepath |% {$n = $_; $c = 0; Get-Content -Path $_ -ReadCount 1000 |% { $c += $_.Count }; "$n; $c"}
$totalrowscast=[int]$totalrows
echo $totalrowscast
For ($i = 0; $i -lt $totalrowscast; $i++) {
Write-host $i
}
But still I am facing the same error.
You're outputting a single string containing both the path AND the row count, eg. "path; 8", which can't be converted to a numerical type, hence the error.
You don't need to manually count each chunk read by Get-Content - the cmdlet already adds a line number to each string it outputs, so you can simply discard everything but the last line and its line number will be the line count (excluding any trailing newline):
$totalrows = Get-Content $filepath |Select -Last 1 -ExpandProperty ReadCount
The ReadCount property is already an [int], so everything will start working expected, including $i -lt $totalrows

How to write to a csv file with no newline using powershell?

I want each row to be populated with the data retrieved from each file. Currently, the 2nd and 3rd column entries are being written to a newline.CSV file output I have tried using "export-csv" and the "-nonewline" command. Perhaps there is a regex command that would solve this?
#Column headings
$headings = "Source file, Review file existence, Review Result, Compilation Check Result, Static Analysis Result, Review up-to-date, Reviewed code version, Latest code version"
# Create array with $headings as first input
$output = #($headings)
$SourceParentDir = "C:\Document"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
$output += $BaseName
$output += ", Review Exists" # writes to a newline
$output += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
}
# write output to a csv file
$output | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
You can do things that way, but there's definitely a more-Powershelley way:
Get-ChildItem -Path $SourceParentDir -Recurse -Filter '*.?pp' |
ForEach-Object {
$File = $_
# Put your other code here
# This will output an object to the stream
[PSCustomObject]#{
'Source file' = $File.Name
'Review file existence' = 'Review Exists'
'Review Result' = $Result
'Compilation Check Result' = $Compilation
'Static Analysis Result' = $StaticAnalysis
'Review up-to-date' = $UpToDateFlag
'Reviewed code version' = $ReviewFileVersionNumber
'Latest code version' = $SourceFileVersionNumber
}
} | Export-Csv Example-csv.csv -NoTypeInformation
The big drawback here is that you don't get a lot of formatting choices about the CSV. Every field is quoted, for example.
Alternately, if you really want really detailed control of the $output string, you should use a StringBuilder instead of a String. StringBuilder is one of the most potent and widely used classes in C#. This is because strings in C# and Powershell are immutable, so when you += a String you create a new string, copy everything over with the new bit, then throw the old string away. It can be very memory intensive with large operations. StringBuilder lets you get around all that. It's a class that's designed to let you append stuff to strings and format them however you want.
You instance it like so:
$output = [System.Text.StringBuilder]::new()
And then you typically call one of two methods to add text. Append($string) appends the string, AppendLine($string) appends the line and then adds a newline. You can also call AppendLine() with no argument to just add a newline. To get your final string, you call the ToString() method. The append methods do return a status when you call them which you can prevent from outputting pretty easily with a [void], or by saving it to another variable if you need it.
$output = [System.Text.StringBuilder]::new()
[void]$output.AppendLine($headings)
$SourceParentDir = "C:\StarTeam\00011114-JSENS_TRS\ATR\04_SW_Implementation\Operational"
$Files = get-childitem -Path $SourceParentDir -Recurse -Filter '*.?pp' | % { $_.FullName }
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
# Populate each row for each file
[void]$output.Append($BaseName)
[void]$output.Append(", Review Exists")
[void]$output.Append(", $Result,$Compilation,$StaticAnalysis,$UpToDateFlag,$ReviewFileVersionNumber,$SourceFileVersionNumber,")
[void]$output.AppendLine()
}
$output.ToString() | Out-File -FilePath Documents\Example-csv.csv -encoding utf8
$output is an array, so each of those += inside the loop is a new entry in the array, and therefore a new line in the file.
You can fix this by using a temporary string variable in the middle of the loop, and appending it to $output once at the end of each iteration:
foreach ($File in $Files)
{
$row = [System.IO.Path]::GetFileName($File)
$row += ", Review Exists"
$row += ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","
$output += $row
}
or by putting everything in one big string interpolation:
foreach ($File in $Files)
{
$BaseName = [System.IO.Path]::GetFileName($File)
$output += "$BaseName, Review Exists, $Result, $Compilation, $StaticAnalysis, $UpToDateFlag, $ReviewFileVersionNumber, $SourceFileVersionNumber"
}
But I agree with the other answer that building up an array of custom objects for the Export-Csv commandlet is more idiomatic for PowerShell.
The issue is how you're populating $Output. Since it is defined as an array, each time you're adding new information it's creating a new entry rather than just adding the additional information to a string.
If you make your $output into one line with all required fields it should correct it without you changing the array.
$output += $BaseName + ", Review Exists" + ", " + $Result + "," + $Compilation + "," + $StaticAnalysis + "," + $UpToDateFlag + "," + $ReviewFileVersionNumber + "," + $SourceFileVersionNumber + ","

Adding a row from a List to a row in a 2nd List Powershell

In Powershell: I have 2 lists-FontIDList and FontDefinitionList. I want to take the first row of FontID and add the first row of FontDefinition. Repeat thru all the elements, then write to a text file. I've tried:
$newContent = $FontIDList2 | foreach {$_ + $FontDefList2}
Add-Content "C:\Desktop\MasterFontList-TimeStamps\TestLatestDate.txt" -value $newContent
As well as $newContent = $FontIDList2 + FontDefList2-and I'm pretty sure i need to use some kind of foreach, but am not sure of the syntax.
Using Linq's Zip method:
$selector = [func[object, object, object]] {
param($x1, $x2)
$x1 + $x2
}
[Linq.Enumerable]::Zip($FontIDList2, $FontDefList2, $selector) | Add-Content out.txt
You could iterate by index instead using a for loop and then concatenate your strings:
$newContent = for ($i = 0 ; $i -lt $FontIDList.Count ; $i++) {
$FontIDList[$i] + $FontDefList2[$i]
}
Add-Content "C:\Users\dtxxxxx\Desktop\MasterFontList-TimeStamps\TestLatestDate.txt" -value $newContent
Note that Add-Content adds to a file, not sure if that's what you actually wanted.

How to total Kbytes of several files

I'm looping through a directory full of files and need to get the grand total of all the files in a directory. However I'm getting a conversion problem due to the way I fetch/calculate the kilobytes.
Here is my code:
$destinationDir = "\\server\folder"
$filename = "FILE_1234567.xls"
$filesize = 0
$totalfilesize = 0
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | Select-Object #{Name="Kbytes";Expression={"{0:N0}" -f ($_.Length / 1Kb)}}
$totalfilesize += [int]($filesize)
Write-Host $filesize
Write-Host $totalfilesize
However when I run this I get the following error message:
Cannot convert the "#{Kbytes=93}" value of type "Selected.System.IO.FileInfo" to type "System.Int32".
At C:\Sample.ps1:7 char:18
+ $totalfilesize += <<<< $filesize
+ CategoryInfo : NotSpecified: (:) [], RuntimeException
+ FullyQualifiedErrorId : RuntimeException
I'm pretty new to Powershell but it seems like I cannot cast $filesize to an integer for totaling. Can anyone help with where I've gone wrong?
Direct answer:
Ok, first I'll just answer your question about why your code is getting that error. Basically, Select-Object is returning an object which has a KBytes property - that object is what you're trying to add to $totalfilesize. There are two ways around this:
One option is to use a foreach to emit the value that you want.
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | ForEach-Object { $_.Length / 1kb }
Try this, and you'll see the result is an int, because it's only returning $_.Length, which is an int.
Alternatvely, you could add the .KBytes property instead of adding the result of Select-Object:
$totalfilesize += [int]($filesize.KBytes)
Caveat
Are you planning to iterate over multiple files? If so, then you may run into issues because in that case you'll get an array back. You can avoid that by moving the addition into a loop based on the get-childitem results:
$destinationDir = "$pshome"
$filename = "*"
$totalfilesize = 0
$filesize = Get-ChildItem ($destinationDir + "\" + $filename) | ForEach-Object { $totalfilesize += $_.Length / 1kb }
Write-Host $totalfilesize
Finally, there's the Measure-Object cmdlet which does stuff like this for free:
[int]((Get-ChildItem $PSHOME | Measure-Object -Sum Length).Sum / 1kb)
Hope that helps

Complex file rename

I have some 100 files in a folder. I want to rename those files. The format of files is like
AB1234.gif
B3245.gif
AB2541.gif
AB11422.jpg
and so on..
Output files shld be
AB-0111-1.gif
B-0111-2.gif
AB-0111-3.gif
AB-0111-4.jpg
Logic will be
for(int n=0;n<files.count;n++){
if(file is starting with AB)
fileName = AB + "-0111" + n;
if(file is starting with B)
fileName = B + "-0111" + n;
}
Is this thing possible with powershell script?
With the filename format that you describe, you can use the powershell -replace operator to replace the middle digits with the expression that you want. The only tricky part is that you have to maintain a counter as you loop through the items. It would look like this:
dir | foreach -begin{$cnt = 1} -process { mv $_ ($_.Name -replace '\d+',"-0111-$cnt"); $cnt += 1}
You can use [System.IO.Path]::GetExtension to get the extension of the file like this:
$ext = [System.IO.Path]::GetExtension('AB1234.gif')
and you can get the first alpha characters of the file name using the $matches variable like this:
if ('AB1234.gif' -match '^\D*') { $first = $matches[0]; }
and you can build your new file name like this:
$first + '-0111-' + '1' + $ext
You can replace the '1' above with a variable that you can increment by checking the exists property like this:
$i = 1;
while (test-path ($first + '-0111-' + $i + $ext)) {$i++}
when this loop completes, you will have the file name you need with $first + '-0111-' + $i + $ext and you can use this to rename the file with Rename-File:
Rename-File 'AB1234.gif' ($first + '-0111-' + $i + $ext)
now, wrap all that up in a loop and you should have it:
dir | ForEach-Object { ... }
for testing, add the -whatif parameter to the end of the Rename-File command and PS will tell you what it would do instead of actually performing the action.
thanks,
mark