I am trying to output to a csv file the result that I am getting from "Get-ChildItem"
I need to save only the LastWriteTime and the Name.
This is the Get-ChildItem output
I am trying something like
$pc = Get-WmiObject Win32_ComputerSystem
$file = Get-ChildItem -Path S:\Docker\RCT\repo\*xml -Recurse -Force
$DeviceObj = New-Object PSObject -Property #{
DeviceName = $pc.Name
FileName = $file.Name
FileDate = $file.LastWriteTime
}
$DeviceObjList += $DeviceObj
$DeviceObjList | Export-Csv -Path "$current_path\FileStatus.csv" -NoTypeInformation -Encoding UTF8
But my csv is not saving the file name and also the timestamp
Use a ForEach-Object to loop though your $file object
$pc = Get-WmiObject Win32_ComputerSystem
$file = Get-ChildItem -Path S:\Docker\RCT\repo\*xml -Recurse -Force
$file | ForEach-Object {
$DeviceObj = New-Object PSObject -Property #{
DeviceName = $pc.Name
FileName = $_.Name
FileDate = $_.LastWriteTime
}
$DeviceObjList += $DeviceObj
}
$DeviceObjList | Export-Csv -Path "$current_path\FileStatus.csv" -NoTypeInformation -Encoding UTF8
..but in fact, this is more efficient:
$pc = Get-WmiObject Win32_ComputerSystem
$file = Get-ChildItem -Path S:\Docker\RCT\repo\*xml -Recurse -Force
$DeviceObjList = $file | ForEach-Object {
[pscustomobject]#{
DeviceName = $pc.Name
FileName = $_.Name
FileDate = $_.LastWriteTime
}
}
$DeviceObjList | Export-Csv -Path "$current_path\FileStatus.csv" -NoTypeInformation -Encoding UTF8
You Don't need WMI to get the computer name, use the built-in variable $env:COMPUTERNAME
Use Calculated Properties to add the Computer name to the results.
Iteration not required here, just add the Export-Csv to the pipeline
So you can do that:
$file = Get-ChildItem -Path S:\Docker\RCT\repo\*xml -Recurse -Force
$File | Select #{N="DeviceName";E={$env:COMPUTERNAME}},Name,LastWriteTime |
Export-Csv -Path "$current_path\FileStatus.csv" -NoTypeInformation -Encoding UTF8
And if you want to make it shorter, you can use aliases and don't define any variables
gci S:\Docker\RCT\repo\*xml -R -Fo |
select #{N="DeviceName";E={$env:COMPUTERNAME}},Name,LastWriteTime |
epcsv "$current_path\FileStatus.csv" -NoT -En UTF8
Related
I have this script I received to check folders and subfolders on a network drive. I wonder how it could be modified into checking only folders and subfolder and write in the CSV if there is any folder with more then 40.000 files in it and the number of files. The image show a sample output from the script as it is now and I do not need it to show any files as it currently do.
$dir = "D:\test"
$results = #()
gci $dir -Recurse -Depth 1 | % {
$temp = [ordered]#{
NAME = $_
SIZE = "{0:N2} MB" -f ((gci $_.Fullname -Recurse | measure -Property Length -Sum -ErrorAction SilentlyContinue).Sum / 1MB)
FILE_COUNT = (gci -File $_.FullName -Recurse | measure | select -ExpandProperty Count)
FOLDER_COUNT = (gci -Directory $_.FullName -Recurse | measure | select -ExpandProperty Count)
DIRECTORY_PATH = $_.Fullname
}
$results += New-Object PSObject -Property $temp
}
$results | export-csv -Path "C:\temp\output.csv" -NoTypeInformation
Instead of executing so many Get-ChildItem cmdlets, here's an approach that uses robocopy to do the heavy lifting of counting the number of files, folders and total sizes:
# set the rootfolder to search
$dir = 'D:\test'
# switches for robocopy
$roboSwitches = '/L','/E','/NJH','/BYTES','/NC','/NP','/NFL','/XJ','/R:0','/W:0','/MT:16'
# regex to parse the output from robocopy
$regEx = '\s*Total\s*Copied\s*Skipped\s*Mismatch\s*FAILED\s*Extras' +
'\s*Dirs\s*:\s*(?<DirCount>\d+)(?:\s+\d+){3}\s+(?<DirFailed>\d+)\s+\d+' +
'\s*Files\s*:\s*(?<FileCount>\d+)(?:\s+\d+){3}\s+(?<FileFailed>\d+)\s+\d+' +
'\s*Bytes\s*:\s*(?<ByteCount>\d+)(?:\s+\d+){3}\s+(?<BytesFailed>\d+)\s+\d+'
# loop through the directories directly under $dir
$result = Get-ChildItem -Path $dir -Directory | ForEach-Object {
$path = $_.FullName # or if you like $_.Name
$summary = (robocopy.exe $_.FullName NULL $roboSwitches | Select-Object -Last 8) -join [Environment]::NewLine
if ($summary -match $regEx) {
$numFiles = [int64] $Matches['FileCount']
if ($numFiles -gt 40000) {
[PsCustomObject]#{
PATH = $path
SIZE = [int64] $Matches['ByteCount']
FILE_COUNT = [int64] $Matches['FileCount']
FOLDER_COUNT = [int64] $Matches['DirCount']
}
}
}
else {
Write-Warning -Message "Path '$path' output from robocopy was in an unexpected format."
}
}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path "C:\temp\output.csv" -NoTypeInformation
it's me again, as i mentioned yesterday i'm new to Powershell (now 3 days) and i hope you can help me again.
What I want:
I want to merge different txt-files into one csv-file
PLUS every line which is added should start with the actual date (yyyy-mm-dd) and the filename.
Expectation_Image
WhatIamActuallyGetting_Image
So what I've got so far:
New-Item Shoppinglist_$(get-date -f yyyyMMdd_hhmm).csv -ItemType File
$txtFiles = Get-ChildItem -Name *.txt
$desiredColumns = 'Date','Filename','Substance','Information','Comment'
ForEach ($file in $txtFiles) {
$csv = Import-Csv -path $file -Delimiter "`t"
$outcsv=$csv | Select-Object $desiredColumns
#I Think the mistake is somewhere here, but i habe no idea to fix it. :(
Select-Object *, #{Name = 'Date'; Expression = {(Get-Date -format s)}}
Select-Object *, #{Name = 'Filename'; Expression = {(GetFileName)}}
$outcsv | Export-Csv Shoppinglist_$(get-date -f yyyyMMdd_hhmm).csv -NoTypeInformation -Delimiter ";" -Append
}
I hope there is someone outside in the world who can help me. :)
You are right to use calculated properties, but are overthinking this a bit.
Also, Get-ChildItem returns FileInfo or DirectoryInfo objects. (unless you specify switch -Name, in that case it returns only the names of the items in the path).
These objects have useful properties, such as FullName, Name, LastWriteTime, etc.
Since you only want files returned, you can use the -File switch.
This assumes both input files have the exact same columns as in your example:
# the folder where the input files are and where the output csv file should be saved
$path = 'D:\Test'
$today = '{0:yyyy-MM-dd}' -f (Get-Date)
$txtFiles = Get-ChildItem -Path $path -Filter '*.txt' -File
$csv = foreach ($file in $txtFiles) {
Import-Csv -Path $file.FullName -Delimiter "`t" |
Select-Object #{Name = 'Date'; Expression = {$today}},
#{Name = 'Filename'; Expression = {$file.Name}}, *
}
$fileOut = Join-Path -Path $path -ChildPath ('Shoppinglist_{0:yyyyMMdd_HHmm}.csv' -f (Get-Date))
$csv | Export-Csv -Path $fileOut -Delimiter ";" -NoTypeInformation
This assumes both input files have at least the 3 desired columns: 'Substance','Information' and 'Comment'
# the folder where the input files are and where the output csv file should be saved
$path = 'D:\Test'
$today = '{0:yyyy-MM-dd}' -f (Get-Date)
$txtFiles = Get-ChildItem -Path $path -Filter '*.txt' -File
$csv = foreach ($file in $txtFiles) {
Import-Csv -Path $file.FullName -Delimiter "`t" |
Select-Object #{Name = 'Date'; Expression = {$today}},
#{Name = 'Filename'; Expression = {$file.Name}},
Substance, Information, Comment
}
$fileOut = Join-Path -Path $path -ChildPath ('Shoppinglist_{0:yyyyMMdd_HHmm}.csv' -f (Get-Date))
$csv | Export-Csv -Path $fileOut -Delimiter ";" -NoTypeInformation
If you are using a PowerShell version below 3.0, you cannot use the -File switch. Instead then use: $txtFiles = Get-ChildItem -Path $path -Filter '*.txt' | Where-Object { !$_.PSIsContainer }
Is there a way I can export the list to a text file and separate them by share name somehow, not single file?
I'd like to do in this format, "$hostname-$sharename.txt".
Here is what I have so far:
$Shares = Get-WmiObject Win32_Share -Filter "not name like '%$'" |
Select-Object -Expand Path
$re = ($Shares | ForEach-Object {[Regex]::Escape($_)}) -join '|'
$results = foreach ($Share in $Shares) {
(Get-ChildItem $Share -Recurse | Select-Object -Expand FullName) -replace "^($re)\\"
}
$results | Out-File -FilePath "C:\Output\$($env:computername)-$sharename.txt"
If I understand the question correctly, all you are missing in your code is the sharename part for the various output files.
Below will hopefully do what you want:
$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
}
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
The purpose of this code is to transfer files from one location to another and to log whether the transfer was a success or a failure.
Everything works except I am having issues with the log. I want the log to be in CSV format and there to be 3 columns: success/failure, from location, and to location. This is outputting the results all into rows with one column.
I've tried the Export-Csv option but that looks for objects/properties so only displays the length(I have strings too). Add-content works but there is only one column. Any suggestions?
#LOCATION OF CSV
$csv = Import-Csv C:\test2.csv
#SPECIFY DATE (EXAMPLE-DELETE FILES > 7 YEARS. 7 YEARS=2555 DAYS SO YOU WOULD ENTER "-2555" BELOW)
$Daysback = "-1"
#FILE DESTINATION
$storagedestination = "C:\Users\mark\Documents\Test2"
#LOG LOCATION
$loglocation = "C:\Users\mark\Documents\filetransferlog.csv"
$s = "SUCCESS"
$f = "FAIL"
$CurrentDate = Get-Date
foreach ($line in $csv) {
$Path = $line | Select-Object -ExpandProperty FullName
$DatetoDelete = $CurrentDate.AddDays($DaysBack)
$objects = Get-ChildItem $Path -Recurse | Select-Object FullName, CreationTime, LastWriteTime, LastAccessTime | Where-Object { $_.LastWriteTime -lt $DatetoDelete }
foreach ($object in $objects) {
try
{
$sourceRoot = $object | Select-Object -ExpandProperty FullName
Copy-Item -Path $sourceRoot -Recurse -Destination $storagedestination
Remove-Item -Path $sourceRoot -Force -Recurse
$temp = $s, $sourceRoot, $storagedestination
$temp | add-content $loglocation
}
catch
{
$temp2 = $f, $sourceRoot, $storagedestination
$temp2 | add-content $loglocation
}
}
}
All your | Select-Object -ExpandProperty are superfluous, simply attach the property name to the variable name => $Path = $line.FullName
Why calculate $DatetoDelete inside the foreach every time?
Output the success/fail to a [PSCustomObject] and gather them in a variable assigned directly to the foreach.
Untested:
$csv = Import-Csv C:\test2.csv
$Daysback = "-1"
$destination = "C:\Users\mark\Documents\Test2"
$loglocation = "C:\Users\mark\Documents\filetransferlog.csv"
$s = "SUCCESS"
$f = "FAIL"
$CurrentDate = Get-Date
$DatetoDelete = $CurrentDate.Date.AddDays($DaysBack)
$Log = foreach ($line in $csv) {
$objects = Get-ChildItem $line.FullName -Rec |
Where-Object LastWriteTime -lt $DatetoDelete
foreach ($object in $objects) {
$Result = $s
$sourceRoot = $object.FullName
try {
Copy-Item -Path $sourceRoot -Recurse -Destination $destination
Remove-Item -Path $sourceRoot -Recurse -Force
} catch {
$Result = $f
}
[PSCustomObject]#{
'Success/Fail' = $Result
Source = $sourceRoot
Destination = $destination
}
}
}
$Log | Export-Csv $loglocation -NoTypeInformation