Powershell add text to start, end of output file - powershell

I have a folder containing multiple text files that I am combining into one file. On that output file I need to add a string to the top and the bottom of the file. I have tried using insert and I keep getting an error. The file is contained in the $Output variable
My code so far:
if(!(Test-Path -Path $PathDump)) {
# create the folder if it does not yet exist
New-Item -ItemType Directory $PathDump
}
# move all *.txt items from 'C:\RemoveFirst\txt' to 'C:\RemoveFirst\DumpARoo'
# EXCEPT the output file itself
$Path = (Get-ChildItem -Path $Path -Filter '*.txt' -File).FullName | Where-Object { $_ -ne $Output}
Move-Item -Path $Path -Destination $PathDump # move (not copy) files into new directory to concat
Get-ChildItem -Path $PathDump -Filter '*.txt' -File | ForEach-Object {
'' # Output an empty line at SOF
'---------------------------------------------------------------'
$_ | Get-Content | Select-Object -Skip 1 | Select-Object -SkipLast 1
'---------------------------------------------------------------'
'' # Output an empty line at EOF
} | Add-Content -Path $OutPut

The story continues ;)
Try this:
$Path = 'C:\RemoveFirst\*.txt'
$PathDump = 'C:\RemoveFirst\DumpARoo'
$Output = 'C:\RemoveFirst\TestingFile.txt'
if(!(Test-Path -Path $PathDump)) {
# create the folder if it does not yet exist
New-Item -ItemType Directory $PathDump
}
# move all *.txt items from 'C:\RemoveFirst\txt' to 'C:\RemoveFirst\DumpARoo'
# EXCEPT the output file itself
$Path = (Get-ChildItem -Path $Path -Filter '*.txt' -File).FullName | Where-Object { $_ -ne $Output}
Move-Item -Path $Path -Destination $PathDump # move (not copy) files into new directory to concat
# Output 'SOF'
"SOF" | Add-Content -Path $OutPut
Get-ChildItem -Path $PathDump -Filter '*.txt' -File | ForEach-Object {
# output the content of the current file
$_ | Get-Content | Select-Object -Skip 1 | Select-Object -SkipLast 1 | Add-Content -Path $OutPut
}
# Output 'EOF'
"EOF" | Add-Content -Path $OutPut

Related

powershell script not interpreting file path correctly

I have an issue where a path to a file is generated by an application. So the path looks like this….
Unfortunately, this output is generated from an application…so I cannot manipulate the output.
Now…when I run my powershell script to copy the file …I get the following errors
It seems that the problem is that my powershell script is not getting the path correctly….
$folders = Get-Content -Path '\\TFA-APP-01\CUI\WS\FOUNDFILES\results.txt'
$dest = '\\TFA-APP-01\CUI\WS\FOUNDFILES\found\'
$excludes = "WS","FOUNDFILES"
foreach ($folder in $folders) {
# Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Desktop'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Documents'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Downloads'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Outlook'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'INetCache'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path $folder | Where-Object{$_.Name -notin $excludes} | Where-Object{$_.FullName -like 'Attachments'} | Copy-Item -Destination $dest -Recurse -ErrorAction SilentlyContinue
}
How can I either parse the results.txt file and extract just the file paths to another text file?
Or how can I modify my CopyResults.ps1 script to interpret the file path correctly?
Your issue is that Get-Content converts every line of a file into an element of an array, and you don't want to look at every line. You want to look at a specific part of a specific line that repeats in a specific pattern.
Because of all of these constants, we can make a pretty simple script to get the information you want.
$paths = Get-Content C:\temp\paths.txt
for ($i = 1; $i -lt $paths.Length; $i += 3) {
$path = "$($paths[$i].Split('.txt')[0]).txt"
$path # \\SERVER\PATH\FILE.TXT
}
Your file has a pattern of [empty line, path line, company line]. If we think about the .txt file as an array with many subarrays of that pattern, we can see that we want to get the 1st index ($i = 1) for every set of 3 ($i += 3) in the file.
Since I split by .txt, I have to string interpolate the .txt extension back on.
Edit:
Here's the script modified for your issues
$paths = Get-Content C:\temp\paths.txt
for ($i = 1; $i -lt $paths.Length; $i += 3) {
$pathSplit = $paths[$i].Split('.')
$extension = $pathSplit[1].split(' ')[0]
$path = "${$pathSplit[0]).$extension"
$path # \\SERVER\PATH\FILE.TXT
}
$pathSplit is split at the extension into 2 parts. One is the majority of the path and the other is the rest of the line.
$extension looks at the 1st index and splits by the space in order to isolate the file extension.
Finally, $path combines $pathSplit[0] and $extension to give you the full file path.

Powershell Find all empty folders and subfolders in a given Folder name

I´m trying to get a
a) list of all empty folders and subfolders if the folder is named "Archiv"
b) I´d like to delete all those empty folders. My current approch doesn´t check the subfolders.
It would be also great if the results would be exportet in a .csv =)
$TopDir = 'C:\Users\User\Test'
$DirToFind = 'Archiv'>$EmptyDirList = #(
Get-ChildItem -LiteralPath $TopDir -Directory -Recurse |
Where-Object {
#[System.IO.Directory]::GetFileSystemEntries($_.FullName).Count -eq 0
$_.GetFileSystemInfos().Count -eq 0 -and
$_.Name -match $DirToFind
}
).FullName
$EmptyDirList
Any ideas how to adjust the code? Thanks in advance
You need to reverse the order in which Get-ChildItem lists the items so you can remove using the deepest nested empty folder first.
$LogFile = 'C:\Users\User\RemovedEmptyFolders.log'
$TopDir = 'C:\Users\User\Test'
# first get a list of all folders below the $TopDir directory that are named 'Archiv' (FullNames only)
$archiveDirs = (Get-ChildItem -LiteralPath $TopDir -Filter 'Archiv' -Recurse -Directory -Force).FullName |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object -Property Length -Descending
# next, remove all empty subfolders in each of the $archiveDirs
$removed = foreach ($dir in $archiveDirs) {
(Get-ChildItem -LiteralPath $dir -Directory -Force) |
# sort on the FullName.Length property in Descending order to get 'deepest-nesting-first'
Sort-Object #{Expression = {$_.FullName.Length}} -Descending |
ForEach-Object {
# if this folder is empty, remove it and output its FullName for the log
if (#($_.GetFileSystemInfos()).Count -eq 0) {
$_.FullName
Remove-Item -LiteralPath $_.FullName -Force
}
}
# next remove the 'Archiv' folder that is now possibly empty too
if (#(Get-ChildItem -LiteralPath $dir -Force).Count -eq 0) {
# output this folders fullname and delete
$dir
Remove-Item -LiteralPath $dir -Force
}
}
$removed | Set-Content -Path $LogFile -PassThru # write your log file. -PassThru also writes the output on screen
Not sure a CSV is needed, I think a simple text file will suffice as it's just a list.
Anyway, here's (although not the most elegant) a solution which will also delete "nested empty directories". Meaning if a directory only contains empty directorIS, it will also get deleted
$TopDir = "C:\Test" #Top level directory to scan
$EmptyDirListReport = "C:\EmptyDirList.txt" #Text file location to store a file with the list of deleted directorues
if (Test-Path -Path $EmptyDirListReport -PathType Leaf)
{
Remove-Item -Path $EmptyDirListReport -Force
}
$EmptyDirList = ""
Do
{
$EmptyDirList = Get-ChildItem -Path $TopDir -Recurse | Where-Object -FilterScript { $_.PSIsContainer } | Where-Object -FilterScript { ((Get-ChildItem -Path $_.FullName).Count -eq 0) } | Select-Object -ExpandProperty FullName
if ($EmptyDirList)
{
$EmptyDirList | Out-File -FilePath $EmptyDirListReport -Append
$EmptyDirList | Remove-Item -Force
}
} while ($EmptyDirList)
This should do the trick, should works with nested too.
$result=(Get-ChildItem -Filter "Archiv" -Recurse -Directory $topdir | Sort-Object #{Expression = {$_.FullName.Length}} -Descending | ForEach-Object {
if ((Get-ChildItem -Attributes d,h,a $_.fullname).count -eq 0){
$_
rmdir $_.FullName
}
})
$result | select Fullname |ConvertTo-Csv |Out-File $Logfile
You can do this with a one-liner:
> Get-ChildItem -Recurse dir -filter Archiv |
Where-Object {($_ | Get-ChildItem).count -eq 0} |
Remove-Item
Although, for some reason, if you have nested Archiv files like Archiv/Archiv, you need to run the line several times.

Count the number of files in the directory as well as the number of folders

Currently , I can export the list to a text file and separate them by share name.
My question is : I want to be able to count the number of files in the directory as well as the number of folders into a separate text file.
I'd like to do in this format for text file , $hostname-$sharename-count.txt
For example:
My desired output:
1000 #Folder count
150 #File count
Here is what I have so far:
$outputDir = 'C:\Output'
$Shares = Get-WmiObject Win32_Share -Filter "not name like '%$'"
$re = ($Shares | ForEach-Object {[Regex]::Escape($_.Path)}) -join '|'
foreach ($Share in $Shares) {
$result = (Get-ChildItem -Path $Share.Path -File -Recurse | Select-Object -Expand FullName) -replace "^($re)\\"
# output the results per share in a text file
$fileOut = Join-Path -Path $outputDir -ChildPath ('{0}-{1}.txt' -f $env:COMPUTERNAME, $Share.Name)
$result | Out-File -FilePath $fileOut -Force
}
You can simply expand the code you have like below:
$outputDir = 'C:\Output'
$Shares = Get-WmiObject Win32_Share -Filter "not name like '%$'"
$re = ($Shares | ForEach-Object {[Regex]::Escape($_.Path)}) -join '|'
foreach ($Share in $Shares) {
$files = (Get-ChildItem -Path $Share.Path -File -Recurse | Select-Object -Expand FullName) -replace "^($re)\\"
# output the list of files per share in a text file
$fileOut = Join-Path -Path $outputDir -ChildPath ('{0}-{1}.txt' -f $env:COMPUTERNAME, $Share.Name)
$files | Out-File -FilePath $fileOut -Force
# output the count results for files and folders per share in a text file
$folders = Get-ChildItem -Path $Share.Path -Directory -Recurse
$content = 'Folders: {0}{1}Files: {2}' -f $folders.Count, [Environment]::NewLine, $files.Count
$fileOut = Join-Path -Path $outputDir -ChildPath ('{0}-{1}-count.txt' -f $env:COMPUTERNAME, $Share.Name)
$content | Out-File -FilePath $fileOut -Force
}
P.S. You can add switch -Force to the Get-ChildItem cmdlet to also get the hidden or system files listed if there are any such files inside the shares
If you just want to have a count, you could do something like this:
$resultForFiles = (Get-ChildItem -Path $Share.Path -File -Recurse | Select-Object -Expand FullName)
$resultForFolders = (Get-ChildItem -Path $Share.Path -Directory -Recurse | Select-Object -Expand FullName)
$resultForFiles.Count | Out-File "Path" -Append
$resultForFolders.Count | Out-File "Path" -Append
The -File switch for Get-ChildItem will only get files and the -Directory will only get folders
You can do this in just one line of code
Get-ChildItem | Measure-Object -Property Mode
The property Mode from Get-ChildItem tells you if you are getting folders, files or others.
You can also use get-help Measure-Object -Examples to check some useful examples on measuring files and folders

Detect missing filenames in folder

Every day we receive a zipfile from a number of clients. The filename consists of the following:
data_clientname_timestamp.zip
Where "data" is always the same text, "clientname" could be anything and "timestamp" is the file creation date.
The files are always in the same directory. The clientnames are always known in advance, so I know what files should be received.
The script should do the following:
List all files received (created) today
If a file from one or more clients is missing, write "file from client.. missing" to a file
I would like to list the clients in a variable, so those can easily be changed.
What I have so far:
$folder='C:\data'
Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )} | select name | out-file $folder\result.txt
But how to check the file for missing files?
Edit:
Testdata:
$Timestamp = (Get-Date).tostring(“yyyyMMddhhmmss”)
New-Item c:\Data -type Directory
New-Item c:\Data\Data_client1_$Timestamp.zip -type file
New-Item c:\Data\Data_client2_$Timestamp.zip -type file
New-Item c:\Data\Data_client3_$Timestamp.zip -type file
New-Item c:\Data\Data_client5_$Timestamp.zip -type file
New-Item c:\Data\Data_client6_$Timestamp.zip -type file
New-Item c:\Data\Data_client7_$Timestamp.zip -type file
exit
Script:
$folder='C:\Data'
$clients = #("client1", "client2", "client3", "client4", "client5", "client6", "client7")
$files = Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )}
$files | Select-Object Name | Out-File $folder\result.txt
$files | Where-Object { ($_.Name -replace '.+?_([^_]+).*', '$1') -notin $clients} | Out-File $folder\result2.txt
Start with defining a list of clients you would expect like:
$clients = #("client1", "client2")
Then retrieve all zip files and save it to a variable:
$files = Get-ChildItem $folder -recurse -include #("*.zip") |
Where-Object {($_.CreationTime -gt (Get-Date).Date )}
Export the existing files to your result.txt:
$files | Select-Object Name | Out-File $folder\result.txt
Now you can determine each missing client using the Where-Object cmdlet with a regex that grabs the clientname:
$fileClients = $files | ForEach-Object { ($_.Name -replace '.+?_([^_]+).*', '$1') }
Compare-Object $clients $fileClients | select -ExpandProperty InputObject | Out-File $folder\result2.txt
You need to have a list of your clients somewhere (such as in a CSV file named clients.csv) then you could loop through that list to check if a file is found for each client. For example:
$folder='C:\data'
$Clients = Import-CSV Clients.csv
$Files = Get-ChildItem $folder -recurse -include #("*.zip") | Where-Object {($_.CreationTime -gt (Get-Date).Date )} | select name
$Clients | ForEach-Object {
$Client = $_
$ClientCheck = $Files | Where-Object {$_ -like $Client}
If (-not $ClientCheck) {
Write-Warning "$Client is missing!"
}Else{
Write-Output $ClientCheck
}
} | out-file $folder\result.txt

powershell more than one operations using pipe

My script looks for all the files in folder1 and checks if this file exist in folder2. if it exists then I want to delete the file from folder2 and move the file from Folder1 to folder3.
$folder1 = "D:\folder1"
$folder2= "D:\folder2"
$folder3 = "D:\folder3"
$a = Get-ChildItem $folder1 | select -ExpandProperty basename
$a | foreach {
Get-ChildItem -Path $folder2 -filter *$_* -Recurse
}
Now if I use pipe at the end of for each loop I can either delete or move but not both. How do I handle this situation?
Neither move-item or remove-item output anything to the pipe. However both have a -PassThru switch parameter to allow further processing.
This will do the job:
$A | foreach { Get-ChildItem -Path $folder2 -filter $_ -Recurse} | foreach {Remove-Item $_.FullName; Copy-Item $folder1\$_.BaseName $Folder3}
Get-ChildItem -File -Include(Get-ChildItem -File $folder1) $folder2\* |
foreach { remove-item $_ ; move-item (join-path $folder1 $_.BaseName) -destination $folder3}