PowerShell: how to append text files in Order? - powershell

$inputpath = 1.txt, 2.txt ....
Command:
Get-ChildItem -Path $inputpath\* -Include *.txt | Sort-Object -Property Name | Get-Content | ForEach-Object { Add-Content -Path d:\MergedFile.txt -Value "$_" }
The Result is a mixed file with duplicated content and in a random order. How can I append multiple files to a new file while considering the order of the source files?

When you sort on a file's Name property, you're sorting string values, and the result will be sorted alphabetically - which means 10 goes before 9 for example.
If you want to sort by the numerical value represented by the string name, you'll have to explicitly tell Sort-Object to treat the input values as such:
Get-ChildItem ... | Sort-Object -Property {$_.Name -replace '\D' -as [int]} |...
In the property expression {$_.Name -replace '\D' -as [int]} we take the Name property value (like before), remove any non-digit characters, and finally convert the resulting string to an integer.

Related

How to add a string to the output from powershell

I use this to get a list of folders containing .h files.
**
$type = "*.h"
$HDIRS = dir .\$type -Recurse |
Select-Object Directory -Unique |
Format-Table -HideTableHeaders
** It gives me a list of folders.
Now I want "-I " before every foldername. Common string manipulation doesn't seem to work
You still have rich objects after the select so to manipulate the strings you have to reference the one property you've selected "Directory"
$type = "*.h"
$HDIRS = Dir .\$type -Recurse |
Select-Object Directory -Unique |
ForEach-Object{
$_.Directory = "-I" + $_.Directory
$_
} |
Format-Table -HideTableHeaders
This will result in $HDIRS looking like a list of folder paths like -IC:\temp\something\something...
However, the format output objects are generally not suitable for further consumption. It looks like you're interested in the strings you could simply make this a flat array of strings like:
$type = "*.h"
$HDIRS = Dir .\$type" -Recurse |
Select-Object -ExpandProperty Directory -Unique |
ForEach-Object{ "-I" + $_ }
The mantra goes filter left, format right.
Our data:
$type = '.\*.h'
Get-ChildItem -Path $type -Recurse
The manipulation (filter):
Select-Object { "-I $($_.Directory)" } -Unique
And the format:
Format-Table -HideTableHeaders
In many cases, PowerShell cmdlets allow you to pass scriptblocks (closures) to evaluate to values and that's what I'm doing above with the Select-Object call.

Using Powershell, how to return a list of files based on the existence of duplicate files with a different naming convention?

There are multiple .webp files in a project folder. Some .webps are the original picture and some function as thumbnail (their size is different). The used naming convention is: original files are just called NAME.webp and tumbnails are NAME-thumb.webp.
I am trying to return all .webp files based on if the corresponding thumb-webp exists. So if picture SAMPLE.webp has a SAMPLE-thumb.webp, don't add this file to the list. But if SAMPLE.webp doesn't have a corresponding SAMPLE-thumb.webp, then do at it to the list.
This is what i've tried so far:
$example = Get-ChildItem -File $dir\*.webp |
Group-Object { $_.BaseName } |
Where-Object { $_.Name -NotContains "-thumb" } |
ForEach-Object Group
You can get this without the grouping with a Where-Object and testing paths.
Get-ChildItem -File $dir\*.webp |
Where-Object {$_.Name -notmatch "-thumb" -and -not(Test-Path ($_.FullName -replace ".webp","-thumb.webp"))}
This should get you a list of all the files that do not have a corresponding thumbnail file.
You can do the following:
(Get-ChildItem $dir\*.webp -File |
Group-Object {$_.BaseName -replace '-thumb$'} |
Where Count -eq 1).Group
You must have a commonality with grouping. Replacing the ending -thumb in the BaseName property creates that. If there is no filename and filename-thumb the resulting GroupInfo will have a count value of 1.
Using the syntax ().Group returns all file objects. If you want to process code against each file, you may use Foreach-Object instead:
Get-ChildItem $dir\*.webp -File |
Group-Object {$_.BaseName -replace '-thumb$'} |
Where Count -eq 1 | Foreach-Object {
$_.Group
}

find string from a group of files

I have a group of txt files contain similar strings like this:
Windows 7 Professional Service Pack 1
Product Part No.: *****
Installed from 'Compliance Checked Product' media.
Product ID: 0000-0000-0000 match to CD Key data
CD Key: xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
Computer Name: COMP001
Registered Owner: ABC
Registered Organization:
Microsoft Office Professional Edition 2003
Product ID: 00000-00000-00000-00000
CD Key: xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
How may I pick all office keys one time and save into another file?
My code:
$content = Get-ChildItem -Path 'S:\New folder' -Recurse |
where Name -like "b*" |
select name
Get-Content $content
I get a list of files name but it wouldn't run for Get-Content.
The code you posted doesn't work, because $content contains a list of custom objects with one property (name) containing just the file name without path. Since you're apparently not listing the files in the current working directory, but some other folder (S:\New Folder), you need the full path to those files (property FullName) if you want to be able to read them. Also, the property isn't expanded automatically. You must either expand it when enumerating the files:
$content = Get-ChildItem -Path 'S:\New folder' -Recurse |
Where-Object { $_.Name -like "b*" } |
Select-Object -Expand FullName
Get-Content $content
or when passing the value to Get-Content:
$content = Get-ChildItem -Path 'S:\New folder' -Recurse |
Where-Object { $_.Name -like "b*" } |
Select-Object FullName
Get-Content $content.FullName
With that out of the way, none of the code you have does even attempt to extract the data you're looking for. Assuming that the license information blocks in your files is always separated by 2 or more consecutive line breaks you could split the content of the files at consecutive line breaks and extract the information with a regular expression like this:
Get-ChildItem -Path 'S:\New folder' -Recurse | Where-Object {
-not $_.PSIsContainer -and
$_.Name -like "b*"
} | ForEach-Object {
# split content of each file into individual license information fragments
(Get-Content $_.FullName | Out-String) -split '(\r?\n){2,}' | Where-Object {
# filter for fragments that contain the string "Microsoft Office" and
# match the line beginning with "CD Key: " in those fragments
$_ -like '*Microsoft Office*' -and
$_ -match '(?m)(?<=^CD Key: ).*'
} | ForEach-Object {
# remove leading/trailing whitespace from the extracted key
$matches[0].Trim()
}
} | Set-Content 'C:\output.txt'
(\r?\n){2,} is a regular expression that matches 2 or more consecutive line breaks (both Windows and Unix style).
(?m)(?<=^CD Key: ).* is a regular expression that matches a line beginning with the string CD Key: and returns the rest of the line after that string. (?<=...) is a so-called positive lookbehind assertion that is used for matching a pattern without including it in the returned value. (?m) is a regular expression option that allows ^ to match the beginning of a line inside a multiline string instead of just the beginning of the string.
try Something like this:
Get-ChildItem "c:\temp" -file -filter "*.txt" |
%{select-string -Path $_.FullName -Pattern "CD Key:" } | select line | export-csv "c:\temp\found.csv" -notype
If you want computer information you can do it (-context take N rows before and M rows after example -context 3, 2 take 3 before and 2 after) :
Get-ChildItem "c:\temp" -file -filter "*.txt" |
%{select-string -Path $_.FullName -Pattern "CD Key:" -context 6,0 } | where {$_.Context.PreContext[0] -like 'Computer Name:*'} |
select Line, #{Name="Computer";E={($_.Context.PreContext[0] -split ':')[1] }} | export-csv "c:\temp\found.csv" -notype
Or classically:
Get-ChildItem "c:\temp" -file -filter "*.txt" | foreach{
$CurrenFile=$_.FullName
#split current file rows to 2 column with ':' like delimiter
$KeysValues=get-content $CurrenFile | ConvertFrom-String -Delimiter ":" -PropertyNames Key, Value
#if file contains CD Key, its good file
if ($KeysValues -ne $null -and $KeysValues[2].Key -eq 'CD Key')
{
#build object with asked values
$Object=[pscustomobject]#{
File=$CurrenFile
ComputerName=$KeysValues[3].Value
OfficeKey=$KeysValues[7].Value
}
#send objet to standard output
$Object
}
} | export-csv "c:\temp\found.csv" -notype

Need PS Get-Childitem to not truncate name results

This is what I'm running:
Get-Childitem $("C:\Powershell Tests\Group 1") -Recurse -Force | where { -not$_.PSIsContainer } | group name -NoElement | sort name > "C:\Powershell Tests\Group 1.txt"
I'm later using this text file and comparing with the names in another to see what he differences are between the two.
In the text file I'm getting the name truncated with "..."
What can I add so that it doesn't truncate so that I can compare?
PowerShell outputs objects, not text.
If you want to output the file's names, then select the names and output them:
Get-ChildItem "C:\PowerShell Tests\Group 1" -Recurse -Force |
Where-Object { -not $_.PSIsContainer } |
Select-Object -ExpandProperty Name |
Sort-Object -Unique |
Out-File "C:\Powershell Tests\Group 1.txt"
Notes:
you don't need the subexpression operator, $( ), for the parameter to Get-ChildItem.
I removed your call to Group-Object. (It looked to me like you want a sorted list of unique file names.)

List of items in a folder using sub string of filename

I have a directory which contains .xls files named as follows:
Recon_[Account No]_[YYYYMMDD].xls
e.g:
Recon_9020111006076954416_20131216.xls
The Account number can be any number of characters between 16 and 20.
I need to get a list of items in this folder - but only the account numbers.
What would be FANTASTIC is if I could get a list of account numbers and then next to them the date the file was created (which can be the datestamp in the file name or the last modified date of the file), in YYYY/MM/DD format, sorted by date.
Like this:
9020111006076954416 2013/12/16
10201129080000235952 2013/12/17
I then need this list of accounts in a text file, or even better, an excel file. Any ideas?
Give this a try:
Get-ChildItem -Filter *.xls | Where-Object {$_.BaseName -match '^Recon_\d{16,20}_\d+$'} | ForEach-Object{
$id,$date = $_.BaseName.Split('_')[1..2]
New-Object PSObject -Property #{
AccountNumber = $id
DateCreated = $date.Insert(4,'/').Insert(7,'/')
}
} | Export-Csv itemList.csv -NoTypeInformation
Fairly, easy, actually:
First obtain the raw data
Get-ChildItem *.xls |
Then extract the properties you need from it:
ForEach-Object {
if ($_.Basename -match '^Recon_(?<account>\d+)_(?<date>\d+)$') {
$_ | Add-Member NoteProperty AccountNumber $Matches.account
}
} |
Select those properties you are interested in (we are still dealing with the original FileInfo object, we just added the account number to it):
Select-Object AccountNumber,LastWriteTime
You could make the header nicer as well:
Select-Object #{l='Account Number'; e={$_.AccountNumber}}, #{l='Date'; e={$_.LastWriteTime}}
At this point you have something you can display on screen nicely. You can then continue formatting the data by piping it into another ForEach-Object:
ForEach-Object {
'{0}`t{1:yyyy-MM-dd}' -f $_.AccountNumber, $_.LastWriteTime
}
or convert it to CSV (which Excel can then open) by piping into ConvertTo-Csv:
ConvertTo-Csv foo.csv
To recap:
$data = Get-ChildItem *.xls |
ForEach-Object {
if ($_.Basename -match '^Recon_(?<account>\d+)_(?<date>\d+)$') {
$_ | Add-Member NoteProperty AccountNumber $Matches.account
}
} |
Select-Object AccountNumber,LastWriteTime
$data | ForEach-Object {
"{0}`t{1:yyyy-MM-dd}" -f $_.AccountNumber, $_.LastWriteTime
} | Out-File foo.txt
$data | ConvertTo-Csv foo.csv
Get all files of this directory. in a loop get file name and split it by '_'. the second item of array is account number and the third one is date.