REG EXPORT single value to .reg file - powershell

I want to export a single registry value to a file using PowerShell (eventually scripting to backup a bunch of registry values).
I can view the file with QUERY:
REG QUERY "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse
>>
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
LimitBlankPasswordUse REG_DWORD 0x1
However, I can't export the single value.
REQ EXPORT has no /v flag, so:
REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa" \v "LimitBlankPasswordUse" LBPU.reg
ERROR: Invalid syntax.
Doesn't work.
If I try to export the value I get:
REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\LimitBlankPasswordUse" LBPU.reg
ERROR: The system was unable to find the specified registry key or value.
And if I simply do the folder:
REG EXPORT "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\" Lsa.reg
The operation completed successfully.
I get a .reg file which includes the correct value:key pair "LimitBlankPasswordUse"=dword:00000001 - but also all the others. Which is no use. I want one pair, not hundreds.
How do I export a single value to a .reg file?

There is no direct way of achieving what you want, and given that you want a .reg file as output, using PowerShell's cmdlets (such as Get-Item, Get-ItemProperty, and Get-ItemPropertyValue) is not an option - unless you're prepared to emulate the .reg file format in all aspects, which is nontrivial both in terms of effort and complexity.[1]
The best approach in your case is to first export the whole key, with reg.exe export, to a temporary file, and then extract and export only the header lines and the data-value pair of interest.
Given that a single data-value pair can span multiple lines, this isn't straightforward, but the following should work robustly (you could wrap it in a function for convenience and reuse):
# Determine the key path and the name of the value to export...
$keyPath = 'HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa'
$valueName = 'LimitBlankPasswordUse'
# ... and the output file path.
$outFile = 'LBPU.reg'
# Create a temporary file and export the whole key to it.
$tempFile = New-TemporaryFile
$null = reg.exe export $keyPath $tempFile /y
# Extract the header lines and the key's *immediate* value children.
# Note: reg.exe export invariably exports *recursively*, so that any
# descendant keys are exported as well, which we want to exclude.
$null = (Get-Content -Raw $tempFile) -match '(?s)^(.+?\])\r\n(.+?)\r\n(?:\r\n|\z)'
Remove-Item $tempFile
# Extract the header lines (format identifier and key path)...
$headerLinesBlock = $Matches[1]
# ... and the key's immediate children as value-data pairs
$valueLinesBlock = $Matches[2]
# Extract the specific value-data pair of interest.
# Note: Such a pair can span *multiple* lines, if "\" is used as the
# line-continuation character at the end of a line, as is common
# with hex. data.
# Continuation lines always start with a *space*, so a data-value pair
# ends either with a new line whose first char. is a non-whitespace char.
# or with the end of the input string.
if ($valueLinesBlock -notmatch "(?sm)^`"$valueName`"=.+?(?=(\r\n\S|\z))") {
throw "Value name not found: $valueName"
}
$valueDataPair = $Matches[0]
# Save the header lines and the data-value pair line(s) to the result file.
# Note that .reg files are "Unicode" (UTF-16LE) files.
$headerLinesBlock, $valueDataPair | Set-Content -Encoding Unicode $outFile
$outFile receives the following content:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa]
"LimitBlankPasswordUse"=dword:00000001
[1] To name a few challenges: REG_DWORD and REG_QWORD values must be represented as hex values, REG_EXPAND_SZ values, when queried with Get-ItemProperty are invariably expanded, so you wouldn't preserve the actual value as stored in the registry; furthermore, REG_EXPAND_SZ and REG_MULTI_SZ values must be represented as types hex(2) and hex(7) in the form of hex byte arrays, respectively.

You haven't used PowerShell at all here. As zett42 said, this is a Get-ItemProperty problem. If you must have a .reg file as the output then you could easily create one providing you have simple case such as this where the output is a single dword. The approach will struggle if you have numerous items and more complex datatypes.
$lsa = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" -Name LimitBlankPasswordUse
$path = $lsa.PSPath.Split('::')
Set-Content foo.reg "Windows Registry Editor Version 5.00`r`n`r`n[$($path[2])]`r`n`"LimitBlankPasswordUse`"=dword:$($lsa.LimitBlankPasswordUse)"
You can hard code or script as much as you need depending upon your overall objective. Probably export to CSV and then apply then at the target with Set-ItemProperty.

Related

Powershell import of a CSV file with line break in the header

For a project i would need to export data from an .xlsm file data to a CSV file and then process it with powershell.
I do the export manually. In the original file, there are line breaks in the headers, which are also transferred to the CSV file.
My questions:
Can I export a specific sheet from an .xlsm with Powershell?
How can I replace the line breaks with spaces?
Attached is an example of what the original .xlsm file looks like.
Update: Unfortunately, I do not know how to upload a file.
Here is the content of an example CSV file:
Host name;Computer name old;IP-addr.;"IP-addr.
free?";"Subnetmask
CIDR Suffix";Static DNS entry;DNS alias;"vCPU Number
[Units]";"RAM
[GB]";"Boot disk
[GB]";;;;;;;;
Broadcast;;172.225.145.0;Net;26;;;;;;;;;;;;;
Gateway;;172.225.145.1;Net;26;;;;;;;;;;;;;
Server125;;172.225.145.2;yes;26;;;;;;;;;;;;;
Server126;;172.225.145.3;yes;26;;;;;;;;;;;;;
Server127;;172.225.145.4;yes;26;;;;;;;;;;;;;
Server128;;172.225.145.5;no;26;;;;;;;;;;;;;
Screenshot from Notepad++ of the CSV file
Can I export a specific sheet from an .xlsm with Powershell?
Use the ImportExcel module, which you can install with Install-Module ImportExcel.
See this answer for an example.
How can I replace the line breaks with spaces?
A simple - though not efficient - approach is to:
Import the CSV with Import-Csv or Import-Excel (from the ImportExcel module) first - which works fine with header fields that have embedded line breaks, as long as such fields are enclosed in "..."
Then process the resulting [pscustomobject] instances and replace them with a newly constructed variant whose property names have the line breaks replaced with spaces.
Import-Csv yourFile.csv | # parse the CSV file into [pscustomobject]s
ForEach-Object { # process each object
$oht = [ordered] #{} # create an aux. ordered hashtable
foreach ($p in $_.psobject.Properties) { # loop over the object's properties
# add a name-value entry with the modified column (property) name.
$oht[($p.Name -replace '\s+', ' ')] = $p.Value
}
[pscustomobject] $oht # convert the hashtable to [pscustomobject] and output
}
Pipe the above to Export-Csv or Export-Excel (from the ImportExcel module) in order to re-export the data back to a file, pipe it to another command, or capture the transformed objects in a variable ($objects = Import-Csv ...)
Note:
-replace is used to replace any non-empty run (+) of whitespace characters. (\s) with a single space (' '). This ensures that the sequence <space><LF> is replaced with a single space.

Changing column headings from powershell Query [duplicate]

Editor's note:
The gist of this question is:
* How do I add custom properties to objects output by Get-ChildItem that contain derived path information, namely the parent folder path (as Folder and name (as Foldername)?
* How do I export the resulting objects to a CSV file?
Currently have a script running that I took from StackOverflow and modified for my own use. The purpose of the script is to look at a directory, grab file names, then export them to a directory as a .csv file.
I was able to modify the script to pull through just the file name as originally it only pulled through the path, user etc. Was able to do this by adding ,Name. For some reason I can't get the script to pull through the parent folder it's in, also wanting the parents parent folder.
I believe this part of the code is what I am having most trouble with. Upon the select I was able to add ,Name but adding ,folder or ,foldername after the select doesn't pull through properly.
ForEach-Object {$_ | Add-Member -Name "Owner" -MemberType NoteProperty -Value (Get-Acl $_.FullName).Owner -PassThru} |
Sort-Object fullname |
Select FullName,CreationTime,LastWriteTime,Owner,Name,Folder,Foldername
The [System.IO.FileInfo] instances returned by Get-ChildItem for files do not have Folder or FolderName properties.
Get-ChildItem -File $HOME\Desktop | Get-Member, for instance, will show you the available properties, and will show you that the desired information can be derived from the PSPath and PSParentPath properties.
Select-Object allows hashtable-based property definitions, so-called calculated properties, which allow you to rename and/or transform properties and/or add custom properties derived from other property values by way of a script block.
Note: You can also use calculated properties with the Format-Table and Format-List cmdlets for creating output for display only (as well as with other cmdlets - see the linked help topic).
A simplified example of what you're looking for (includes output to a CSV file):
Get-ChildItem $HOME\Desktop | Select-Object Name,
#{ Name = 'Folder'; Expression = { Convert-Path $_.PSParentPath } },
#{ Name = 'Foldername'; Expression = { ($_.PSPath -split '\\')[-2] } } |
Export-Csv Out.csv -Encoding Utf8 -NoTypeInformation
Note that, alternatively, you could add Folder and FolderName properties to the input objects via Add-Member, as you did with the Owner property in your question.
Explanation:
Note that you can get more detailed information about any of the commands mentioned by running Get-Help <command-name> -Full; add -online to view the help topic in a browser; to learn more about the -split operator, run Get-Help about_split; to learn about PowerShell's help system in general, run Get-Help Get-Help -online.
Each #{ ... } construct passed to Select-Object is a hash table that defines a calculated property to attach to each output object:
The hash table must have two entries:
Name or Label, which defines the property's name; for brevity, you may use a (case-insensitive) prefix of the key name, such as just n or l.
Expression, which defines the property's value; again, a (case-insensitive) prefix of the key name works too, such as just e.
The expression can be a mere property name (a string), in case you simply want to rename an input property, but is more typically a script block ({ ... }), which is a piece of code that gets executed for each input object, and whose output becomes the value of the property being defined; inside that script block, automatic variable $_ (or $PSItem) refers to the input object at hand.
Definition of the Folder property: Convert-Path $_.PSParentPath converts the fully qualified PowerShell path that the PSParentPath property contains - which includes a prefix identifying the drive provider - to a regular filesystem path; e.g., Microsoft.PowerShell.Core\FileSystem::C:\Users\jdoe\Desktop -> C:\Users\jdoe\Desktop.
Definition of the Foldername property: ($_.PSPath -split '\\')[-2] splits the full path into components by path separator \, and then accesses the next-to-last component (-2), which is the parent folder name; e.g., C:\Users\jdoe\Desktop\file.txt -> Desktop
'\\' must be used to represent \, because -split's 1st RHS operand is a regular expression, where \ has special meaning and must therefore be doubled to be taken as a literal.
If you wanted to support / as the path separator as well for cross-platform support, you'd use ($_.PSPath -split '[\\/]')[-2].
Export-Csv exports the objects output by Select-Object to CSV Out.csv, where the input objects' property names become the header row, and the property values the data rows.
-Encoding Utf8, required in Windows PowerShell only (PowerShell (Core) 7+ now fortunately consistently uses BOM-less UTF-8), ensures that non-ASCII characters are properly encoded; by default, Export-Csv uses ASCII encoding and simply replaces non-ASCII characters, such as foreign letters, with verbatim ? characters, resulting in loss of information; note that -Encoding Utf8 in Windows PowerShell invariably creates UTF-8 files with a BOM.
-NoTypeInformation, again required in Windows PowerShell only, suppresses a line that Export-Csv by defaults adds as the first line of the output file, which contains the full type name (class name) of the input objects (e.g., #TYPE System.Management.Automation.PSCustomObject; this is meant to facilitate later reconversion into objects).

Read a file and then based on file content names, move the file from one folder to another using Powershell script

I need to read a file (e.g. file.txt) which has file names as its content. File names are separated by unique character (e.g. '#'). So my file.txt looks something like:
ABC.txt#
CDE.csv#
XYZ.txt#
I need to read its content line by line based on its extension. I have 1 source folder and 1 destination folder. Below is my scenario that I need to achieve:
If extension = txt then
check if that file name exists in destination_folder1 or destination_folder2
if that file exists then
copy that file from source_folder1 to destination_folder1
else delete that file from destination_folder1
Else display msg as "Invalid file"
I am new to powershell scripting. can someone pls help? Thanks in advance.
It will make my job easier if we assume the following pseudocode. Then you can take the elements I demonstrate and change them to fit your needs.
If the string from "file.txt" contains the file extension "txt" then continue.
If the file does not exist in the destination folder then copy the file from the source folder to the destination folder.
Use Get-Content to read a text file.
Get-Content .\file.txt
Get-Content processes files line by line. This has a few consequences:
Each line in our input text file will trigger our code.
Each time our code triggers, it will have input that looks like this: ABC.txt#
We can focus on solving the problem for one line.
If we need to evaluate strings, I suggest using regular expressions.
Remember, we are operating on a single line from the text file:
ABC.txt#
We need to detect the file extension.
A good place to start would be the end of the string.
In regular expressions, the end of a string is represented by $
So let's start there.
Here is our regular expression so far:
$
The next thing that would be useful is if we accounted for that # symbol. We can do that by adding it before $
#$
If there was a different character, we would add that instead: ;$ Keep in mind that there are reserved characters in regular expressions. So we might need to escape certain characters with a backslash: \$$
Now we have to account for the file extension.
We have three letters, we don't know what they are.
Regular expressions have a special escape sequence (called a character class) that can match any letter: \w
Let's add three of those.
\w\w\w#$
Now, while crafting regular expressions, it is a good idea to limit the text we're looking for.
As humans, we know we're looking for .txt# But, so far, the computer only knows about txt# with no dot. So it would accept .txt#, .xlsx#, and anythingGoes# as matches. We limited the right side of our string. Now let's limit the left side.
We're only interested in three characters. And the left side is bounded by a . So let's add that to our regular expression. I'll also mention that a period is a reserved character in regular expressions. So, we will have to escape it.
\.\w\w\w#$
So if we're looking at text like this
ABC.txt#
then our regular expression will output text like this
.txt#
Now, .txt# is a pretty good result. But we can make our job a little easier by limiting the result to just the file extension.
There are several ways of doing this. But I suggest using regular expression groups.
We create a group by surrounding our target with parentheses.
\.(\w\w\w)#$
This now produces output like:
txt
From here, we can just make intuitive comparisons like if txt = txt.
Another piece of the puzzle is testing whether a file already exists. For this we can use the Test-Path and Join-Path cmdlets.
$destination = ".\destination 01"
$exampleFile = "ABC.txt"
$destinationFilePath = Join-Path -Path $destination -ChildPath $exampleFile
Test-Path -Path $destinationFilePath
With these concepts, it is possible to write a working example.
# Folder locations.
$source = ".\source"
$destination = ".\destination 01"
# Load input file.
Get-Content .\file.txt |
Where-Object {
# Enter our regular expression.
# I've added an extra group to capture the file name.
# The $matches automatic variable is created when the -match comparison operator is used.
if ($_ -match '([\w ]+\.(\w\w\w))#$')
{
# Which file extensions are we interested in processing?
# Here $matches[2] represents the file extension: ex "txt".
# We use a switch statement to handle each type of file extension.
# Accept new file types by creating new switch cases.
switch ($matches[2])
{
"txt" {$true; Break}
#"csv" {$true; Break}
#"pdf" {$true; Break}
default {$false}
}
}
else { $false }
} |
ForEach-Object {
# Here $matches[1] is the file name captured from the input file.
$sourceFilePath = Join-Path -Path $source -ChildPath $matches[1]
$destinationFilePath = Join-Path -Path $destination -ChildPath $matches[1]
$fileExists = Test-Path -Path $destinationFilePath
# Copy the source file to the destination if the destination doesn't exist.
if (!$fileExists)
{ Copy-Item -Path $sourceFilePath -Destination $destinationFilePath }
}
Note on Copy-Item
Copy-Item has known issues.
Issue #10458 | PowerShell | GitHub
Issue #2581 | PowerShell | GitHub
You can substitute robocopy which is more reliable.
Robocopy - Wikipedia
The robocopy syntax is:
robocopy <source> <destination> [<file>[ ...]] [<options>]
where <source> and <destination> can be folders only.
So, if you want to copy a file, you have to write it like this:
robocopy .\source ".\destination 01" ABC.txt
We can invoke robocopy using Start-Process and the variables we already have.
# Copy the source file to the destination if the destination doesn't exist.
if (!$fileExists)
{
Start-Process -FilePath "robocopy.exe" -ArgumentList "`"$source`" `"$destination`" `"$($matches[1])`" /unilog+:.\robolog.txt" -WorkingDirectory (Get-Location) -NoNewWindow
}
Using Get-ChildItem
You use file.txt as input. If you wanted to gather a list of files on disc, you can use Get-ChildItem.
Multiple Conditions
You wrote "destination_folder1 or destination_folder2". If you need multiple conditions you can construct this with three things.
Use the if statement. Inside the test condition, you can add multiple conditions with logical -or And you can group statements together to make them easier to read.
Functions
If you need to move a piece of code around, you can use a function. Just remember to create parameters for the inputs to the function. Then call a PowerShell function without parentheses or commas:
# Calling a PowerShell function.
myFunction parameterOne parameterTwo parameterThree
Writing Output
You can use Write-Output to send text to the console.
Write-Output "Invalid File"
Further Reading
Here are some references which you might find useful.
about_Comparison_Operators - PowerShell | Microsoft Docs
about_Pipelines - PowerShell | Microsoft Docs
about_Switch - PowerShell | Microsoft Docs
Regular-Expressions.info - Regex Tutorial, Examples and Reference - Regexp Patterns
Where-Object (Microsoft.PowerShell.Core) - PowerShell | Microsoft Docs

How can we add new rows at the top of the CSV file using powershell script?

I am new to powershell scripting and I am looking for a way to add 2 new rows at the top of the already present csv file.
Things that I have tried is replacing the header and rows with the new rows.
I am looking for a way to add 2 new rows above the header in CSV.
You mention that you want to add the new lines above the header, which means that no CSV-specific processing is needed - it sounds like you're asking how to prepend lines to an existing text file (which happens to contain CSV - note that the resulting file will no longer be a valid CSV file).
E.g., assuming a target file named some.csv:
Note: Best to make a backup of the target file before trying these commands.
If the input file is small enough to fit into memory as a whole:
Reading the entire target file into memory as a single string with Get-Content -Raw allows for a convenient and concise solution:
Set-Content -LiteralPath some.csv -NoNewLine -Value (
#'
New line 1 above header
New line 2 above header
'# + (Get-Content -Raw some.csv)
)
Note that Set-Content applies a default character encoding (the active ANSI code page in Windows PowerShell, UTF-8 without BOM in PowerShell Core), irrespective of the current encoding of some.csv, so you may have to use the -Encoding parameter to specify the encoding explicitly.
Also note that the single-quoted here-string (#'<newline>...<newline>'#) uses the same newline style (CRLF (Windows-style) vs. LF (Unix-style)) as the enclosing script, which may not match the style used in some.csv - though PowerShell itself has no problem processing files with mixed newlines styles.
If the file is too large to fit into memory, use a streaming (line-by-line) approach:
$ErrorActionPreference = 'Stop'
# Create a temporary file and fill it with the 2 new lines.
$tempFile = [IO.Path]::GetTempFileName()
'New line 1 above header', 'New line 2 above header' | Set-Content $tempFile
# Then append the CSV file's lines one by one.
Get-Content some.csv | Add-Content $tempFile
# If that succeeded, replace the original file.
Move-Item -Force $tempFile some.csv
Note: Use of the Get-Content, Set-Content and Add-Content cmdlets is convenient, but slow; the next section shows a faster alternative.
If performance matters, use .NET types such as [IO.File] instead:
$ErrorActionPreference = 'Stop'
# Create a temporary file...
$tempFile = [IO.Path]::GetTempFileName()
# ... and fill it with the 2 new lines.
$streamWriter = [IO.File]::CreateText($tempFile)
foreach ($lineToPrepend in 'New line 1 above header', 'New line 2 above header') {
$streamWriter.WriteLine($lineToPrepend)
}
# Then append the CSV file's lines one by one.
foreach ($csvLine in [IO.File]::ReadLines((Convert-Path some.csv))) {
$streamWriter.WriteLine($csvLine)
}
$streamWriter.Dispose()
# If that succeeded, replace the original file.
Move-Item -Force $tempFile some.csv

Create objects with custom properties containing derived filesystem path information and export them to a CSV - calculated properties

Editor's note:
The gist of this question is:
* How do I add custom properties to objects output by Get-ChildItem that contain derived path information, namely the parent folder path (as Folder and name (as Foldername)?
* How do I export the resulting objects to a CSV file?
Currently have a script running that I took from StackOverflow and modified for my own use. The purpose of the script is to look at a directory, grab file names, then export them to a directory as a .csv file.
I was able to modify the script to pull through just the file name as originally it only pulled through the path, user etc. Was able to do this by adding ,Name. For some reason I can't get the script to pull through the parent folder it's in, also wanting the parents parent folder.
I believe this part of the code is what I am having most trouble with. Upon the select I was able to add ,Name but adding ,folder or ,foldername after the select doesn't pull through properly.
ForEach-Object {$_ | Add-Member -Name "Owner" -MemberType NoteProperty -Value (Get-Acl $_.FullName).Owner -PassThru} |
Sort-Object fullname |
Select FullName,CreationTime,LastWriteTime,Owner,Name,Folder,Foldername
The [System.IO.FileInfo] instances returned by Get-ChildItem for files do not have Folder or FolderName properties.
Get-ChildItem -File $HOME\Desktop | Get-Member, for instance, will show you the available properties, and will show you that the desired information can be derived from the PSPath and PSParentPath properties.
Select-Object allows hashtable-based property definitions, so-called calculated properties, which allow you to rename and/or transform properties and/or add custom properties derived from other property values by way of a script block.
Note: You can also use calculated properties with the Format-Table and Format-List cmdlets for creating output for display only (as well as with other cmdlets - see the linked help topic).
A simplified example of what you're looking for (includes output to a CSV file):
Get-ChildItem $HOME\Desktop | Select-Object Name,
#{ Name = 'Folder'; Expression = { Convert-Path $_.PSParentPath } },
#{ Name = 'Foldername'; Expression = { ($_.PSPath -split '\\')[-2] } } |
Export-Csv Out.csv -Encoding Utf8 -NoTypeInformation
Note that, alternatively, you could add Folder and FolderName properties to the input objects via Add-Member, as you did with the Owner property in your question.
Explanation:
Note that you can get more detailed information about any of the commands mentioned by running Get-Help <command-name> -Full; add -online to view the help topic in a browser; to learn more about the -split operator, run Get-Help about_split; to learn about PowerShell's help system in general, run Get-Help Get-Help -online.
Each #{ ... } construct passed to Select-Object is a hash table that defines a calculated property to attach to each output object:
The hash table must have two entries:
Name or Label, which defines the property's name; for brevity, you may use a (case-insensitive) prefix of the key name, such as just n or l.
Expression, which defines the property's value; again, a (case-insensitive) prefix of the key name works too, such as just e.
The expression can be a mere property name (a string), in case you simply want to rename an input property, but is more typically a script block ({ ... }), which is a piece of code that gets executed for each input object, and whose output becomes the value of the property being defined; inside that script block, automatic variable $_ (or $PSItem) refers to the input object at hand.
Definition of the Folder property: Convert-Path $_.PSParentPath converts the fully qualified PowerShell path that the PSParentPath property contains - which includes a prefix identifying the drive provider - to a regular filesystem path; e.g., Microsoft.PowerShell.Core\FileSystem::C:\Users\jdoe\Desktop -> C:\Users\jdoe\Desktop.
Definition of the Foldername property: ($_.PSPath -split '\\')[-2] splits the full path into components by path separator \, and then accesses the next-to-last component (-2), which is the parent folder name; e.g., C:\Users\jdoe\Desktop\file.txt -> Desktop
'\\' must be used to represent \, because -split's 1st RHS operand is a regular expression, where \ has special meaning and must therefore be doubled to be taken as a literal.
If you wanted to support / as the path separator as well for cross-platform support, you'd use ($_.PSPath -split '[\\/]')[-2].
Export-Csv exports the objects output by Select-Object to CSV Out.csv, where the input objects' property names become the header row, and the property values the data rows.
-Encoding Utf8, required in Windows PowerShell only (PowerShell (Core) 7+ now fortunately consistently uses BOM-less UTF-8), ensures that non-ASCII characters are properly encoded; by default, Export-Csv uses ASCII encoding and simply replaces non-ASCII characters, such as foreign letters, with verbatim ? characters, resulting in loss of information; note that -Encoding Utf8 in Windows PowerShell invariably creates UTF-8 files with a BOM.
-NoTypeInformation, again required in Windows PowerShell only, suppresses a line that Export-Csv by defaults adds as the first line of the output file, which contains the full type name (class name) of the input objects (e.g., #TYPE System.Management.Automation.PSCustomObject; this is meant to facilitate later reconversion into objects).