Add Text to file when performing Get-ChildItem [duplicate] - powershell

This question already has answers here:
Does PowerShell require the opening curly brace of a script block to be on the same line?
(2 answers)
Closed 5 years ago.
I've been looking around trying to solve this issue but nothing has worked so far.
I am wanting to pick 1/multiple files, add a hardcoded value (e.g "DIAB`r`n") to the start of the file content and then write to another volume.
Do I have to do multiple statements or is it something I can do in one.
What I have so far is try using 2 statements:
# Find the file/s .HL7, move to a new destination and change the filename to add "DIAB-"
# to the front - this is working
Get-ChildItem -Path $DiabetesPath |
Where-Object { (!$_.PSIsContainer -and $_.Name -like "*.HL7") } |
Move-Item -Destination $DestinationPath -PassThru |
Rename-Item -NewName {"DIAB-" + $_.Name}
# this is what is not working - after files moved to destination trying to add some text
# to the start of the file content
Get-ChildItem $DestinationPath -Recurse -Filter DIAB*.HL7 | ForEach-Object
{
$content = Get-Content $_.FullName
Set-Content $_.FullName -Value "DIAB`r`n", $content
}
When I try it, it says:
cmdlet ForEach-Object at command pipeline position 2
Supply values for the following parameters
process[0]:
When I enter something it keeps going:
process[1]:
process[2]:
...
Not sure what is going on.

Wow! didn't realise the position of { would affect anything.
Moved the { next to the ForEach-Object and it works.
Get-ChildItem $DestinationPath -Recurse -Filter DIAB*.HL7 | ForEach-Object {
$content = Get-Content $_.FullName
Set-Content $_.FullName -Value "DIAB`r`n", $content
}

Related

Move all even files to different directory | Powershell and regex

I'm trying to move all even files from the current location to the directory "foo", but I have a problem matching them with regex.
Filenames are in this format:
11.txt, 121.txt, 342.txt
The command I'm currently using is:
Get-ChildItem | Where-Object {$_.Name -match '^[0-9]*[02468]$'} | Move-Item -Destination .\foo
The previous command I was using works OK but only for two-digit files 1.txt-99.txt
Get-ChildItem | Where-Object {$_.Name -match '^[0-9]+[02468]'} | Move-Item -Destination .\foo
I tried at https://regex101.com/ site with .NET flavor and checked this regex ^[0-9]*[02468]$ and it works OK - matches all even numbers, but for some reason, I have a problem with above mention PS command...
An alternate method I have used previously for odds/evens is a division method. The solution Santiago has provided in the comments is also a valid regex method for this problem.
# Gets files and starts loop on files
Get-ChildItem "C:\Temp\AllFiles" -File | ForEach-Object {
# If the BaseName of file is divisible by 2, else
If($_.BaseName % 2 -eq 0) {
Move-Item -Destination "C:\Temp\Evens" -Path $_.FullName
} Else {
Move-Item -Destination "C:\Temp\Odds" -Path $_.FullName
}
}

How to Rename Using Powershell

How to rename bunch of files in windows using powershell.
Example of filenames:
Image001 L#ter
Image002 L#ter
I have tried these two commands ,
get-childitem * | ForEach { Move-Item -LiteralPath $_.name $_.name.Replace("L#ter","")}
get-childitem * | ForEach { rename-item $_ $_.Name.Replace("L#ter","") }
I expect the output to be as Image001,Image002
Your question says to rename, but your code samples are using the Move-Item command. I am going to assume you were unsure hot to rename them correctly as you weren't actually moving them.
Get-ChildItem "C:\Temp\" -File | ForEach-Object {
Rename-Item -Path $_.FullName -NewName "$($_.Name -replace '\s*(l#ster)')"
}
The \s* will match any whitespace before your main capturing group
The (l#ster) is the main capture group, it looks for that exact phrase and will match it.
This is a frequent category of question. I like to use rename-item with a scriptblock. Take off the -whatif if it looks right. I'm assuming you don't want the space at the end of the names.
ls '* l#ter' | rename-item -newname { $_.name -replace ' l#ter' } -whatif
first, some small details about your commands:
get-childitem * | ForEach { Move-Item -LiteralPath $_.name $_.name.Replace("L#ter","")}
LiteralPath is meant to be used used exactly as it is typed. So I would use it with
$.FullName instead of $.name if I must use strange paths (like network shares).
second:
In the get-help for both move-item and rename-item I can see that the -path parameter :
Required? true
Position? 0
Default value None
Accept pipeline input? True (ByPropertyName, ByValue)
which means that we can pipe a collection of objects into it and the rename cmdlet will automatically pass through the collection :
Get-ChildItem -Path 'c:\tests\' -File -Recurse | Rename-Item -NewName ($_.name -replace ' l#ter') -Force -WhatIf
I have made this reply redundant for Drew's and Js2010's replys, but, as I am a beginner in powershell, I find easier to understand the answers with full commands.

Move all files with a certain extension from multiple subdirectories into same subdirectory structure using PowerShell [duplicate]

This question already has an answer here:
How to replace the content of every file in the directory?
(1 answer)
Closed 5 years ago.
I am trying to read multiple CSV files inside multiple sub directories and doing some row deletion based on filter using PowerShell scripting.
Get-ChildItem -Path J:\new -Recurse -Filter daq*.csv | ForEach-Object {
Get-Content $_.FullName | Where {
$_ -notmatch "serverTime"
} | Out-File $_.FullName
}
But I want the output to be in same directory structure as the source file.
The directory structure is as below:
root/
sub_dir1/
1.csv
2.csv
sub_dir2/
1.csv
2.csv
Is there a way I can do it?
Probably not optimal, or even idiomatic, but the following worked in a quick test:
Get-ChildItem -Path J:\new -Recurse -Filter daq*.csv |
ForEach-Object {
$origFile = $_
$filteredContent = Get-Content $origFile.FullName | Where{$_ -notmatch "serverTime"}
$filteredContent | Out-File $origFile.FullName
}
All we're doing, is loading the content of each file and filtering it to $filteredContent, then writing it back out to the original file.
Another option (courtesy of Replacing Contents of a Text File Using Powershell) is to wrap the Get-Content command in parens, which forces the full content to be loaded and then passed down the pipeline. This will give shorter code, but arguably it's less understandable e.g.
Get-ChildItem -Path J:\new -Recurse -Filter daq*.csv |
ForEach-Object {
$origFile = $_
( Get-Content $origFile.FullName ) |
Where{$_ -notmatch "serverTime"} |
Out-File $origFile.FullName
}

Trying to iterate through folders and take recursive actions on each file

I'm trying to build a script that I can use to delete old files based on Last Accessed date. As part of the script I want to interrogate each sub folder, find files not accessed in the last X days, create a log in the same folder of the files found and record file details in the log then delete the files.
What I think I need is a nested loop, loop 1 will get each subfolder (Get-ChildItem -Directory -Recurse) then for each folder found a second loop checks all files for last accessed date and if outside the limit will append the file details to a logfile in the folder (for user reference) and also to a master logfile (for IT Admin)
loop 1 is working as expected and getting the subfolders, but I cannot get the inner loop to recurse through the objects in the folder, I'm trying to use Get-ChildItem inside the first loop, is this the correct approach?
Code sample below, I have added pseudo to demo the logic, its really the loops I need help with:
# Set variables
$FolderPath = "E:TEST_G"
$ArchiveLimit = 7
$ArchiveDate = (Get-Date).AddDays(-$ArchiveLimit)
$MasterLogFile = "C:\Temp\ArchiveLog $(Get-Date -f yyyy-MM-dd).csv"
# Loop 1 - Iterate through each subfolder of $FolderPath
Get-ChildItem -Path $FolderPath -Directory -Recurse | ForEach-Object {
# Loop 2 - Check each file in the Subfolder and if Last Access is past
# $ArchiveDate take Action
Get-ChildItem -Path $_.DirectoryName | where {
$_.LastAccessTime -le $ArchiveDate
} | ForEach-Object {
# Check if FolderLogFile Exists, if not create it
# Append file details to folder Log
# Append File & Folder Details to Master Log
}
}
I think you're overcomplicating a bit:
#Set Variables
$FolderPath = "E:\TEST_G"
$ArchiveLimit = 7
$ArchiveDate = (Get-Date).AddDays(-$ArchiveLimit)
$MasterLogFile = "C:\Temp\ArchiveLog $(get-date -f yyyy-MM-dd).csv"
If (!(Test-Path $MasterLogFile)) {New-Item $MasterLogFile -Force}
Get-ChildItem -Path $FolderPath -File -Recurse |
Where-Object { $_.LastAccessTime -lt $ArchiveDate -and
$_.Extension -ne '.log' } |
ForEach-Object {
$FolderLogFile = Join-Path $_.DirectoryName 'name.log'
Add-Content -Value "details" -Path $FolderLogFile,$MasterLogFile
Try {
Remove-Item $_ -Force -EA Stop
} Catch {
Add-Content -Value "Unable to delete item! [$($_.Exception.GetType().FullName)] $($_.Exception.Message)"`
-Path $FolderLogFile,$MasterLogFile
}
}
Edit:
Multiple recursive loops are unnecessary since you're already taking a recursive action in the pipeline. It's powerful enough to do the processing without having to take extra action. Add-Content from the other answer is an excellent solution over Out-File as well, so I replaced mine.
One note, though, Add-Content's -Force flag does not create the folder structure like New-Item's will. That is the reason for the line under the $MasterLogFile declaration.
Your nested loop doesn't need recursion (the outer loop already takes care of that). Just process the files in each folder (make sure you exclude the folder log):
Get-ChildItem -Path $FolderPath -Directory -Recurse | ForEach-Object {
$FolderLogFile = Join-Path $_.DirectoryName 'FolderLog.log'
Get-ChildItem -Path $_.DirectoryName -File | Where-Object {
$_.LastAccessTime -le $ArchiveDate -and
$_.FullName -ne $FolderLogFile
} | ForEach-Object {
'file details' | Add-Content $FolderLogFile
'file and folder details' | Add-Content $MasterLogFile
Remove-Item $_.FullName -Force
}
}
You don't need to test for the existence of the folder log file, because Add-Content will automatically create it if it's missing.

How do I remove Blank Space from File Names

I am trying to remove blank spaces from many file names using PowerShell 3.0. Here is the code that I am working with:
$Files = Get-ChildItem -Path "C:\PowershellTests\With_Space"
Copy-Item $Files.FullName -Destination C:\PowershellTests\Without_Space
Set-Location -Path C:\PowershellTests\Without_Space
Get-ChildItem *.txt | Rename-Item -NewName { $_.Name -replace ' ','' }
For example: the With_Space directory has these files:
Cable Report 3413109.pdf
Control List 3.txt
Test Result Phase 2.doc
The Without_Space directory will need the above file name to be:
CableReport3413109.pdf
ControlList3.txt
TestResultPhase 2.doc
Currently, the script shows no error but it only copies the source files to the destination folder, but doesn't remove the spaces in file names.
Your code should work just fine, but since Get-ChildItem *.txt lists only .txt files the last statement should remove the spaces from just the text files, giving you a result like this:
Cable Report 3413109.pdf
ControlList3.txt
Test Result Phase 2.doc
This should remove spaces from the names of all files in the folder:
Get-ChildItem -File | Rename-Item -NewName { $_.Name -replace ' ','' }
Prior to PowerShell v3 use this to restrict processing to just files:
Get-ChildItem | Where-Object { -not $_.PSIsContainer } |
Rename-Item -NewName { $_.Name -replace ' ','' }
something like this could work
$source = 'C:\temp\new'
$dest = 'C:\temp\new1'
Get-ChildItem $source | % {copy $_.FullName $(join-path $dest ($_.name -replace ' '))}
I think your script should almost work, except $_ isn't going to be defined as anything. By using the for-each cmdlet (%), you assign it and then can use it.
Get-ChildItem *.txt | %{Rename-Item -NewName ( $_.Name -replace ' ','' )}
EDIT:
That interpretation was totally wrong. Some people seem to have found it useful, but as soon as you have something being piped, it appears that $_ references the object currently in the pipe. My bad.