Powershell - Text file is not written to - powershell

Good evening,
I'm hardly experienced in programming, but every now and then I try to build the things I need myself.
What do I want to achieve with the script?
This script should read a text file with words.
There is one new word per line. When reading the script should look if the word has between 3 and 16 letters. If it has less than 3 or more than 16, then the word is skipped. But if it is between the 3 and 16, then the word will be saved in a new Text_File. Again, I would love a new word every line.
Here is what I created.
Please don't judge my script too hard.
$regex = '[A-Z][a-z]{3,16}'
foreach ($line in Get-Content -Path C:\\Users\\Administrator\\Desktop\\namecheck\\words.txt)
{
if($line -match $regex)
{
Out-File -FilePath C:\\Users\\Administrator\\Desktop\\namecheck\\sorted.txt -Force
}
}
As mentioned above, the words are not written to a file. However, the file is created and the script also takes a little while to finish. So from my point of view something seems to happen.

'[A-Z][a-z]{3,16}' is only accounting for words that are of length 3+. That would be your first issue, and your second one is your export. Out-File isn't being told what you want to export. So, either provide it the a value via pipeline input, or using -InputObject:
$path = 'C:\Users\Administrator\Desktop\namecheck\'
$stream = [System.IO.StreamReader]::new("$path\words.txt")
while ($line = $stream.ReadLine())
{
if ($line.Length -gt 3 -and $line.Length -lt 16)
{
Out-File -FilePath "$path\sorted.txt" -InputObject $line -Append -Force
}
}
$stream.Close()
$stream.Dispose()
Although, a foreach loop is the fastest of the loops, when using Get-Content it still has to wait for the completion of the content gathering before it can move through the list. Only mentioning this since you said that the script takes quite a bit and without knowing the size of your words.txt file, im left to assume that's the cause.
With that said, using [System.IO.StreamReader] should speed up that reading of the file as you'll get to read and iterate through the file at the same time; with a while loop that is.

Related

PowerShell: Is there a way to search an specific word in a file's name and open the file if it finds it?

I haven't been able to figure out how to make this task that sounds simple in PowerShell.
I am trying to make a powershell variable that represents a file only using a part of it's name, since the rest of the name changes periodically. This should represent a little better what is my intention.
#Each day the number changes except for the Name part.
Name1, Name2, Name3...
#Variable must be able to work regardless of the number it has since the Name part never changes.
$Variable: Volume\Folder\Name(X).exe
I'm sorry if i'm not explaining myself well enough.
I'll provide any aditional information that is needed.
Well, to me it seems to be two diiferent tasks at hand:
First your title suggests You are lokking for a way to check the filenames of files in a given directory i assume and run that file with the default filehandler (again i can only speculate)
# 1. task
$path="C:\myfolder\"
$mySearchKey="taxes"
$myItmes=Get-ChildItem -Path $myPath
foreach($item in $myItems){
if($item.name -like "*$mySearchkey*"){
$matchingPath=$path+$item.name
Invoke-Item $matchingPath
}
}
Secondly In your description and the code example the question seems to be evolving around the idea to create dynamic variables for filenames most likely for the files we where opening before, based on the day in relation to a start date:
#2. task
$afileName="Marry"
$startdate= Get-Date "2022-12-06"
$todays= get-date
$numberOfDays = New-TimeSpan -Start $startdate -End $todays
$numberOfDays++ # since a differnce of 0 days means your in the 1. day
$newPath="Volume\Folder\"+$afileName+$numberOfDays+".exe"
But I yet have to figure out your end-game. How are the two coming together?
Run script where you want to search.
$all_files = ls
$filename = Read-Host "Enter File Name"
foreach ($item in $filename)
{
if ($item -match $filename)
{
Get-Content $item
}
}

Powershell - Efficient way to keep content and append to the same file?

I want to keep the first comment section lines of a file and overwrite everything else. Currently this section is 27 lines long.
Each line begins with a # (think of it as a giant comment section).
What I want to do is keep the initial comment section, delete everything following the comment section, then append a new string to this file just below this comment section.
I found a way to hardcode it, but I think this is pretty ineffecient. I don't think it's best to hardcode in 27 as a literal.
The way I've handled it is:
$fileProc = Get-Content $someFile
$keep = $fileProc[0..27]
$keep | Set-Content $someFile
Add-Content $someFile "`n`n# Insert new string here"
Add-Content $someFile "`n EMPTY_PROCESS.EXE"
Is there a more efficient way to handle this?
You can use a switch statement to efficiently extract the section of comment lines at the start.
Set-Content out.txt -Value $(
#(
switch -Wildcard -File $someFile {
'#*' { $_ }
default { break } # End of comments section reached.
}
) + "`n`n# Insert new string here", "`n EMPTY_PROCESS.EXE"
)
Note:
To be safe, the above writes to a new file, out.txt, but you can write directly back to $someFile, if desired.
Wildcard expression #* assumes that each line in the comment section starts with #, with no preceding whitespace; if you need to account for preceding whitespace, use the -Regex switch in lieu of -Wildcard, and use regex '^\s*#' in lieu of '#*'
Not sure about limiting it to first set of 27 or so lines but this should work.
First line below is to only keep the lines of file that start with '#'.
(Get-Content $somefile) | Where { $_ -match "^#" } | Set-Content $somefile
Add-Content $somefile "`n`nblah blah"
Add-Content $somefile "`nglug glug blug glug"
You can then use Add-Content for additional lines. Hope this helps :]
Efficient way [...] pretty inefficient [...] a more efficient way
Don't open the file many times, paying the cost of ACL security and AntiVirus checks and disk access delays.
Avoid PowerShell cmdlets and scriptblocks.
Avoid loops in PowerShell, push work to lower layers.
Avoid heavyweight searches like regex and wildcard.
Avoid making arrays of string for the lines.
Open file once, do a single linear scan and truncate when the pattern is found then write new data. Assuming no other comment lines in the data the pattern is "the last "\n#" is the start of the last comment, then the newline after that is the cutoff". e.g.:
$f = [System.IO.FileStream]::new('d:\test.txt', 'open')
$content = [System.IO.StreamReader]::new($f).ReadToEnd()
$lastComment = $content.LastIndexOf("`n#")
$nextLine = $content.IndexOf("`n", 1+$lastComment)
$f.SetLength($nextLine) # truncate
$w = [System.IO.StreamWriter]::new($f)
$w.WriteLine("new next Line")
$w.Close()
If there could be other comment lines, redesign the file so there is a sentinal value to find - easier than finding the absence of a thing.
Compared to mklement0's answer this doesn't cost any PowerShell cmdlet startup time, uses no subshells, no wildcard pattern matching, no arrays of string, and doesn't open the file twice. On a file with 10,000 comment lines:
your original code takes ~0.4 seconds
mklement0's code takes ~0.04 seconds
this code takes ~0.02 seconds.
A more efficient way - QED.

Aligning the corrupted data records in a text file using powershell

My data file(.txt) has records of 31 fields/columns each and the fields are pipe delimited. Somehow, few records are corrupted(the record is split into multiple lines).
Can anyone guide in writing a script that reads this input data file and shapes it into a file containing exactly 31 fields in each record?
PS: I am new to powershell.
Sample data:
Good data - Whole record shows up in a single line.
Bad data - Record is broken into multiple lines.
Below is the structure of the record.
11/16/2007||0007327| 3904|1000|M1||CCM|12/31/2009|000|East 89th Street|01CM1| 11073|DONALD INC|001|Project 077|14481623.8100|0.0000|1.00000|1|EA|September 2007 Invoice|Project 027||000000000000|1330|11/16/2007|X||11/29/2007|2144.57
Here is what i have tried and script hangs
#Setup paths
$Input = "Path\Input.txt"
$Output = "Path\Output.txt"
#Create empty variables to set types
$Record=""
$Collection = #()
#Loop through text file
gc Path\Input.txt | %{
$Record = "$Record$_"
If($Record -Match "(\d{1,2}/\d{1,2}/\d{4}(?:\|.*?){31})(\d{1,2}/\d{1,2}/\d{4}\|.*?\|.*)"){
$Collection+=$Matches[1]
$Record=$Matches[2]
}
}
#Add last record to the collection
$Collection+=$Record $Collection | Out-File $Output
I see some issues that need to be clarified or addressed. First I noticed the line $Record=$Matches[2] does not appear to serve a purpose. Second your regex string appears to have some flaws which you were looking for. When i test your regex against your test data here: http://regex101.com/r/yA9tZ1/1
At least on that site the forward slashes needed to be escaped. Once I escaped the tester threw the error at me
Your expression took too long to evaluate.
I know the root of that issue comes from this portion of your regex which is trying to match your passive group with a non greedy quantifier 31 times. (?:\|.*?){31}
So taking a guess as to your true intention I have the following regex string
(\d{1,2}\/\d{1,2}\/\d{4}.{31}).*?(\d{1,2}\/\d{1,2}\/\d{4}\|.*?\|.*)
You can see the results here: http://regex101.com/r/qY1jZ7/2
While i doubt it is exactly what you wanted I hope this leads you in the right direction.
I just tried this, and while that solution worked for an extremely similar issue where the user only had 11 fields per record, apparently it's just no good for your 31 field records. I'd like to suggest an alternative using -Split alongside a couple of regex matches. This should work faster for you I think.
#Create regex objects to match against
[RegEx]$Regex = "(.*?)(\d{2}/\d{2}/\d{4})$"
[RegEx]$Regex2 = "(\d{2}/\d{2}/\d{4}.*)"
#Setup paths
$Input = "Path\Input.txt"
$Output = "Path\Output.txt"
#Create empty variables to set types
$Record=""
$Collection = #()
#Loop through text file
gc $Input | %{
If($_ -match "^\d{1,2}/\d{1,2}/\d{4}" -and $record.split("|").count -eq 31){$collection+=$record;$record=$_}
else{
$record="$record$_"
if($record.split("|").count -gt 31){
$collection+=$regex.matches(($record.split("|")[0..30]) -join "|").groups[1].value
$record=$regex2.matches(($record.split("|")[30..($record.split("|").count)]) -join "|").groups[1].value
}
}
}
#Add last record to the collection
$collection+=$record
#Output everything to a file
$collection|out-file $Output

Looping though and executing CSV file

I have a CSV file that gets created with long lists of images paths. E.x. ( del \servername\images\1.2.840.11....really long number.dic )
I want to run though this list and execute each of these lines. I tried to do some research and saw people using powershell for this. I wrote a small script but I am not sure how to get powerscript to execute each line of the CSV.
I am sure I just do not know powershell very well but I am open to try any method of going though a CSV and executing the lines.
$delList=IMPORT-CSV C:\application\imagesToDelete.csv
FOREACH ($Image in $delList) {
-- execute each line somehow
}
Thanks in advance, this is a one off issue I need to deal with but I have several CSV's containing tens of thousands of images to be deleted
Ok, let's make this simple and not use Import-CSV. We'll just use Get-Content and skip the header line. Then we use Remove-Item, get a substring of each line starting at the 4th character and going to the end of the line, and remove the result.
$Commands = GC $file | Select -Skip 1
$Counter=1
ForEach($Line in $Commands){
Write-Progress -Activity "Performing commands ($Counter/$($Commands.Count))" -PercentComplete ($Counter/$commands.count*100) -Status "Please wait..."
Remove-Item $Line.substring(4,$line.length-4)
$Counter++
}

Powershell - Getting a directory to output a file at a time

I'm super new at all of this so please excuse my lack of technical elegance and all around idiocy.
dir c:\Users\me\desktop\Test\*.txt | %{ $sourceFile = $_; get-content $_} | Out-File "$sourceFile.results"
How can I modify this command line so that instead of one file with the contents of all the text files I have a one to one ratio so that each output files represents the contents of each text file?
I realize that this object is ridiculous in terms of application but I'm conceptually trying to piece this together bit by bit so I can really understand.
P.S. What's with the %? Haha another ridiculous question, doesn't seem worth a separate post, what does it do?
dir | % { Out-File -FilePath "new_$($_.Name)" -InputObject (gc $_.FullName) }
only one pipeline needed. this command appends "new_" to the filename because I was using the same directory to write to. You can remove this if it's not needed.