Length of string after delimiter in the data inside a file - windows

I have a ton of data files that have delimiters inside and I would like to know the max length of the column after each delimiter. Since I can't to use a third-party program, I would like to get this done through PowerShell as that is built-in for Windows. And, at the same time I can't manually do. So, wondering if this could be achieved with PowerShell at all or any simple trick to do so?
Here is my sample data in a file FOO.TXT
Col1|Col2|Col3
12345|This is a String|This is another String
45688|String|This is another String of unknown length
30098|Second Column String|Third Column String
Expected output:
Col1 Max Length - 5
Col2 Max Length - 20
Col3 Max Length - 40

You can do it like this(but file with data must have csv extension):
$j=Import-Csv -Delimiter "|" -Path D:\testdir\new.csv #import our csv as array string
$colums=$j|gm -MemberType NoteProperty|Select-Object -ExpandProperty Name #get columns name
Foreach($colum in $colums){
$l=($j."$colum"|Measure-Object -Maximum -Property Length).Maximum #for each column get max length
Write-Host $colum" Max Length- "$l
}

Here is the answer I was looking for irrespective of the file extension. Thanks to both #Vad and #Theo.
$j=Import-Csv -Delimiter "|" -Path D:\testdir\FOO.TXT #import our csv as array string
$columns = $j[0].PSObject.Properties.Name #get columns name
Foreach($column in $columns){
$l=($j."$column"|Measure-Object -Maximum -Property Length).Maximum #for each column get max length
Write-Host $column" Max Length- "$l
}

Related

Insert string into multiple filenames

I have multiple files named in this format:
Fat1920OVXPlacebo_S20_R1_001.fastq
Kidney1235SHAM_S65_R1_001.fastq
Kidney1911OVXPlacebo_S94_R2_001.fastq
Liver1289OVXEstrogen_S24_R2_001.fastq
I need to insert the string "L1000_" into their names so that they read
Fat1920OVXPlacebo_S20_L1000_R1_001.fastq
Kidney1235SHAM_S65_L1000_R1_001.fastq
Kidney1911OVXPlacebo_S94_L1000_R2_001.fastq
Liver1289OVXEstrogen_S24_L1000_R2_001.fastq
I apologize but I have absolutely no experience in coding in powershell. The closest thing I could find to do this was a script that renames the entire file:
Set objFso = CreateObject(“Scripting.FileSystemObject”)
Set Folder = objFSO.GetFolder(“ENTER\PATH\HERE”)
For Each File In Folder.Files
sNewFile = File.Name
sNewFile = Replace(sNewFile,”ORIGINAL”,”REPLACEMENT”)
if (sNewFile<>File.Name) then
File.Move(File.ParentFolder+”\”+sNewFile)
end if
Next
however, I just need to insert a string at a specific place in the file's title. I have 257 files and do not want to go 1 by 1. Does anyone have an idea on how to run this in windows?
Use Get-ChildItem to enumerate the files of interest, pipe them to Rename-Item, and use a delay-bind script block ({ ... }) to dynamically determine the new name, via a regex-based -replace operation.
(Get-ChildItem $yourFolder -Filter *.fastq) |
Rename-Item -NewName { $_.Name -replace '(?<=_S\d+_)', 'L1000_' } -WhatIf
Note:
• The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
• Even though not strictly necessary in this case, enclosing the Get-ChildItem command in (...), the grouping operator ensures that already renamed files don't accidentally re-enter the enumeration of files to be renamed - see this answer.
(?<=_S\d+_) uses a positive look-behind assertion ((?<=...)) to match verbatim string _S, followed by one or more (+) digits (\d), followed by verbatim _.
Since the look-behind assertion merely matches a position in the string rather than a substring, the replacement operand, verbatim L1000_ in this case, is inserted at that position in (a copy of) the input string.
For a more detailed explanation of the delay-bind script-block technique, see this answer.
here's one way to do that with PoSh. note that the demo does not handle either the rename or directory related stuff. it ONLY handles generating the new file names.
what it does ...
fakes reading in a list of fileinfo objects
when ready to do this for real, replace the entire #region/#endregion block with a call to Get-ChildItem and save it to $FileList.
sets the text to be inserted
iterates thru the file list
splits the file .Name property on the underscores
saves that to a $Var
adds the 1st two splits, the insertion text, and the last two splits to a new array
joins that array with an underscore as the delimiter
sends the new file name to the $Result collection
displays the list of new names
the code ...
#region - fake reading in a list of files
# in real life, use Get-ChildItem
$FileList = #(
[system.io.fileinfo]'Fat1920OVXPlacebo_S20_R1_001.fastq'
[system.io.fileinfo]'Kidney1235SHAM_S65_R1_001.fastq'
[system.io.fileinfo]'Kidney1911OVXPlacebo_S94_R2_001.fastq'
[system.io.fileinfo]'Liver1289OVXEstrogen_S24_R2_001.fastq'
)
#endregion - fake reading in a list of files
$InsertionText = 'L1000'
$Result = foreach ($FL_Item in $FileList)
{
$FLI_Parts = $FL_Item.Name.Split('_')
($FLI_Parts[0,1] + $InsertionText + $FLI_Parts[2,3]) -join '_'
}
$Result
output ...
Fat1920OVXPlacebo_S20_L1000_R1_001.fastq
Kidney1235SHAM_S65_L1000_R1_001.fastq
Kidney1911OVXPlacebo_S94_L1000_R2_001.fastq
Liver1289OVXEstrogen_S24_L1000_R2_001.fastq
Using PowerShell, you could use a regular expression to rename the files. Example:
Get-ChildItem "C:\foldername\here\*.fastq" | ForEach-Object {
$oldName = $_.Name
$newName = [Regex]::Replace($oldName,'(S\d+)_(R\d+)','$1_L1000_$2')
Rename-Item $_ $newName -WhatIf
}
[Regex] is a PowerShell type accelerator for the .NET Regex class, and Replace is the method for the Regex class that performs text substitutions. The first parameter to the Replace method is the input string (the old filename), the second parameter is the regular expression pattern (run help about_Regular_Rxpressions for more information), and the third parameter is the replacement string pattern, where $1 is the first capture pattern in ( ), and $2 is the second capture pattern in ( )). Finally, the Rename-Item cmdlet renames the files. Remove the -WhatIf parameter if the output looks correct to actually perform the renames.

Getting Local user objects and comparing them to a known good string?

Hi im currently working on a script to monitor back to an RMM tool, seem to be having issues converting my objects to match a "known string" inside my script.
ideally i'd like to poll the local computers local admin group then inline compare that with a string i've predefined, i was hoping to get the value, then just write a multi-lined string to match, then do some if statements to compare the 2.
$test3 = Get-LocalGroupMember -SID "S-1-5-32-544" | select -ExpandProperty Name | out-string
$test =#"
PC\Administrator
PC\test
"#
this is a little snippet, so the first one pulls the local ad group then saves it to a varible, and $test is my defined variable.
Both appear identical when outputted to console.
thanks so much in advance.
Instead of a predefined multiline string, Use either a string array or a hashtable to compare against.
The way you try to do it can fail the comparison simply because the items returned can be in a different order as in your predefined string.
Option 1: use an array
$testUsers = 'PC\Administrator', 'PC\test'
# this gets the users that are mentioned in the $testUsers array.
# if you want the opposite (users in the group, but NOT in the $testUsers array),
# change '-contains' into '-notcontains'
(Get-LocalGroupMember -SID "S-1-5-32-544").Name | Where-Object { $testUsers -contains $_ }
Option 2: use a Hashtable (a bit more work to set up, but extremely fast)
$testusers = #{
'PC\Administrator' = $true # the Values will not be used, so anything can go in here
'PC\test' = $true
}
# this gets the users that are mentioned in the $testUsers Hashtable.
# if you want the opposite (users in the group, but NOT in the $testUsers Hashtable),
# change '$testUsers.ContainsKey($_)' into '!$testUsers.ContainsKey($_)'
(Get-LocalGroupMember -SID "S-1-5-32-544").Name | Where-Object { $testUsers.ContainsKey($_) }
It's a bug in Windows where orphaned SIDs are left in the group. Try this instead:
$adminGroup = [ADSI]::new("WinNT://$env:COMPUTERNAME/$((Get-LocalGroup -SID S-1-5-32-544).Name)")
$adminGroupMembers = $adminGroup.Invoke('Members') |% {([ADSI]$_).Path.Replace('WinNT://', '')}
$adminGroupMembers | Out-String
You'll need to manipulate the output as required.

Powershell Strange behaviour with Import-CSV

I have following powershell code:
clear;
$importedIDs = (Import-Csv "testing.csv" -Delimiter ';').id;
Write-Host $importedIDs.Length;
for ($i=0; $i -lt $importedIDs.Length; $i++) {
Write-Host $($importedIDs[$i]);
}
The goal is to read only the id column in the csv file which looks like this:
"created";"id"
"2018-04-04 21:03:01";"123456"
"2018-04-04 21:03:01";"123457"
When there are two or more rows the output is as expected:
2
123456
123457
However when there is only 1 row in the csv file (row with id 123456) the output is:
6
1
2
3
4
5
6
Desired output would be:
1
123456
Can anyone explain why this is happening and how can I fix this?
Any help is appreciated
If there are multiple rows in the csv you get an array of strings. One array-element per row. Therefore the index applies to the rows. If there is only one row you don't get a array containing one string, as you would probably expect. You get a single string instead. When using an index on a string powershell treats the string as an array of characters and therefore returns only one char.
You can slightly rewrite your script to get around the problem described by J. Bergmann.
Instead of using a for loop to loop through each element in the array, where an "element" may refer to a string in a string array or a character in a string, you can use a foreach loop and loop through elements in an array like this. foreach won't treat a string as an character array
clear;
$importedIDs = (Import-Csv "testing.csv" -Delimiter ';').id;
Write-Host $importedIDs.Length;
foreach ($importedID in $importedIDs) {
Write-Host $($importedID);
}

Sorting movie titles into CSV

I've got a media server at home, and I've created a script that pulls all the file names and sorts them before putting them into a CSV. My only problem is that it sorts alphanumeric, but from a movie titles perspective, I'd like to ignore "A", "An", and "The". Is there a way to ignore those strings and have the sort work correctly without actually altering the file name in the CSV?
Yes, you can sort multiple objects into order by any property, and if none of the properties are quite what you want then you can provide a scriptblock to Sort-Object with some code "do xyz to each object" and it will sort them based on the output of the scriptblock - and that will only be used for sorting, it won't change anything.
So calculate the name without the leading words A, An, The using any code you want to. Here, I'm cooking with regex because it's quick, tasty and does case-insensitive matching by default:
Get-ChildItem | Sort-Object -Property { $_.Name -replace '^(A|An|The).' }
But you can do something just as effective with the plain ingredients around your kitchen:
Function Mangle-FilmName
{
param($file)
$name = $file.Name.ToLower()
if ($name.startswith('an'))
{
$name.Substring(4)
}
elseif ($name.startswith('the')
{
$name.Substring(5)
}
...
else
{
$name
}
}
Get-ChildItem | Sort-Object -Property Mangle-FilmName
Or with switch statements or loops over arrays of words, and/or/etc.
You could use something like the following.
Due to missing sample data I don't know the structure of your file names (word separators etc.) but you can customize the following code to your needs. What the code essentially does is splitting the base file name by separators '_', ' ' and '.', filters out your ignored words ('The', 'A', 'An' etc.) and joins back the parts to a single string.
Please note that at the end of this, the file names are compared without their initial word separators (i.e. 'The_Blue_House.mpg' and 'The.Blue.House.mpg' would be considered the same) which IMHO is a good thing but your needs may be different.
Hope that helps
$wordSeparator = '_| |\.'
$ignoredWords = #(
'The'
'A'
'An'
# add more
)
filter sortableFileName {
($_ -split $wordSeparator | ? { $_ -notin $ignoredWords }) -join ''
}
Get-ChildItem | Sort-Object { $_.BaseName | sortableFileName } # | Export-CSV

Powershell sorting multiple columns and directions

I've managed to get Powershell to sort multiple columns and in different directions;
$prop1 = #{Expression='Priority'; Descending=$true }
$prop2 = #{Expression='Date'; Ascending=$true }
(Import-Csv $BackUpDataPath"WaitList.csv") |
Sort-Object $prop1, $prop2 |
Export-Csv $BackUpDataPath"WaitList.csv" -NoType
My problem is that the Priority value is stored as string data and sorts as text so the output to the file is;
Date Priority
6/28/2016 16:46 2
6/28/2016 16:59 2
6/29/2016 15:27 11
6/28/2016 16:42 1
6/28/2016 16:49 1
I've tried [int]$prop1 and similar ways to change it to an integer but it doesn't seem to work. The Line 6/29/2016 15:27 11 should be first but it isn't. Bare in mind as long as the #'s are less than 10 it sorts fine.
Try it with
$prop1 = #{Expression={[int]$_.Priority}; Descending=$true }
Looking how to convert from string->int i found [int] is not reliably, so I went for another solution. Please try something like this:
"2;2;11;1" -split ";" | %{[int]$Numeric=[convert]::ToInt32($_.toString(), 10) ; add-member -NotePropertyName "Numeric" -NotePropertyValue $Numeric -InputObject $_ ; echo $_} | Sort-Object Numeric
I create an String array, and then split it, for every object I generate the Int32 version of the value and then add a new member to the input object, I print this new object and finally just sort the array by the new property.
I tried to include the script inside the hashtable for the "property" parameter for sort-object but that didn't work :(.
Hope this can solve your problem.
Regards.

Resources