Merging multiple csv into one Excel file without having Excel installed - powershell

I am working on a script where I am trying to merge multiple csv into a single Excel file with csv filename as different sheet.
All the csv files have the same number of columns and name.
I don't have Excel installed on my server, so I've written this code using ImportExcel:
#Install-Module ImportExcel -scope CurrentUser
$path="C:\Scripts" #target folder
cd $path;
$csvs = Get-ChildItem .\* -Include *.csv
$csvCount = $csvs.Count
Write-Host "Detected the following CSV files: ($csvCount)"
foreach ($csv in $csvs) {
Write-Host " -"$csv.Name
}
$excelFileName = $path + "\" + $(get-date -f yyyyMMdd) + "_combined-data.xlsx"
Write-Host "Creating: $excelFileName"
foreach ($csv in $csvs) {
$csvPath = $path + $csv.Name
$worksheetName = $csv.Name.Replace(".csv","")
Write-Host " - Adding $worksheetName to $excelFileName"
Import-Csv -Path $csvPath | Export-Excel -Path $excelFileName -WorkSheetname $worksheetName
The script is taking time and executing without any issue but the Excel sheet is not being generated.
Please can you help me find and fix the issue in this script?

If you want to add a worksheet per CSV to your Excel file the code should look something like this:
$path = "D:\Testing"
$csvs = Get-ChildItem $path -Filter *.csv
$excelFileName = Join-Path $path -ChildPath "$(Get-Date -f yyyyMMdd)_combined-data.xlsx"
foreach ($csv in $csvs)
{
$props = #{
WorksheetName = $csv.BaseName
Path = $excelFileName
}
Import-Csv -Path $csv |
Export-Excel #props
}
If you want to do all in memory (more efficient than the example above), it's a bit more complicated. You need to use -PassThru.
Note, this works on ImportExcel 7.1.2, make sure you have the last up to date version.
$path = "D:\Testing"
$csvs = Get-ChildItem $path -Filter *.csv
$excelFileName = Join-Path $path -ChildPath "$(Get-Date -f yyyyMMdd)_combined-data.xlsx"
foreach ($csv in $csvs)
{
$content = Import-Csv -Path $csv
$props = #{
WorksheetName = $csv.BaseName
PassThru = $true
InputObject = $content
}
if(-not $xlsx)
{
$props.Path = $excelFileName
$xlsx = Export-Excel #props
continue
}
$props.ExcelPackage = $xlsx
Export-Excel #props > $null
}
Close-ExcelPackage $xlsx
If you want all CSV files on the same worksheet, assuming they all have the same headers:
$path = "D:\Testing"
$csvs = Get-ChildItem $path -Filter *.csv
$excelFileName = Join-Path $path -ChildPath "$(Get-Date -f yyyyMMdd)_combined-data.xlsx"
Import-Csv -Path $csvs |
Export-Excel -WorksheetName 'Merged' -Path $excelFileName

Related

How can I get the time and date my PowerShell script deletes a file

I am using the following script to read a list of file names which are then deleted. Is there a way can get an output of the date and time each file is deleted?
$targetFolder = "D:\" $fileList = "C:\DeleteList.txt" Get-ChildItem
-Path "$targetFolder\*" -Recurse -Include #(Get-Content $fileList) | Remove-Item -Verbose
Thanks for any help.
You could keep track of the files that are deleted and the time of deletion by outputting an object with the file's fullname and current date.
This output can then be saved as structured CSV file
$targetFolder = "D:\"
$fileList = Get-Content -Path "C:\DeleteList.txt"
$deleted = Get-ChildItem -Path $targetFolder -Recurse -Include $fileList | ForEach-Object {
# output an object with the current date and the file FullName
$_ | Select-Object #{Name = 'DeletedOn'; Expression = {(Get-Date)}}, FullName
$_ | Remove-Item -WhatIf
}
# output on screen
$deleted | Format-Table -AutoSize
# output to csv file
$deleted | Export-Csv -Path 'C:\RemovedFiles.csv' -NoTypeInformation
Remove the -WhatIf safety-switch if you are satisfied with the results shown on screen.
Would this work?
$targetFolder = "D:"
$fileList = "C:\DeleteList.txt"
$Files = Get-ChildItem -Path "$targetFolder" -Recurse -Include #(Get-Content $fileList)
# Once you have the desires files stored in the $Files variable, then run a Foreach loop.
$Obj = #() # create an array called $Obj
Foreach ($File in $Files)
{
# store info in hash table
$hash = #{
DateTime = (get-date)
fileName = $File.name
fullpath = $File.fullname
}
Write-Host "deleting file $($file.name)" -for cyan
Remove-Item $File.fullname # *** BE VERY CAREFUL!!!***
# record information in an array called $Obj
$Obj += New-Object psobject -Property $hash
}
$Obj | select fileName, DateTime | Export-csv C:\...

Exporting Object and strings to CSV using Powershell

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

How to add headers to csv file

I am creating a script to get the file version of the dlls and export the output to a csv file , but I want to give particular headers to csv file. How should I do it ? I also need to add a new system date and time column to the csv file.
$j = 'C:\Program Files (x86)\anyfilepatha\'
$files = get-childitem $j -recurse -Include *.dll
$cvsdataFile = 'C:\Program Files\MySQL\Connector ODBC 8.0\dllsinfo.csv'
# Add-Content -Path "C:\Program Files\MySQL\Connector ODBC 8.0\dllsinfo.csv" -Value "Dlls" , "Version Name" , "Location"
$header = foreach ($i in $files)
{
if($i.Name -like '*Eclipsys*' -or $i.Name -like '*Helios*')
{
continue;
}
$verison = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($i).FileVersion
if($verison -eq $null)
{
$i.FullName | Out-File NoVersion.txt -append
}
else
{
Write-Host $i ----> $verison
"{0}`t{1}`t{2} " -f $i, [System.Diagnostics.FileVersionInfo]:: GetVersionInfo($i).FileVersion, $i.CreationTime, $i.FullName | Out-File -Append $cvsdataFile
}
}
You could do it with a one liner. You can exclude items with -Exclude parameter and PowerShell calculated properties for adding new columns. See more about calculated properties here.
Get-ChildItem -Path $j -File -Exclude '*Eclipsys*','*Helios*' -Include *.dll |
Select-Object -Property #{E={$_.versioninfo.Fileversion};l='Version'},#{E={Get-Date -f 'dd/mm/yyyy'};l='Date'},#{E={Get-Date -f 'hh:mm:ss ttt='};L='Time'}

PowerShell read csv file and create files accordingly

Basically, I have a CSV file with a lot of columns. Let's say it looks like this:
_username, platform_
username1, platformX1
username2, platformY
username3, platformX2
username4, platformX2
..., ...
I would like to write a script that goes through the file and for each platform, it creates a file with the specific username in a different folder, so I would have:
\platformX1\username1.file
\platformY\username2.file
\platformX2\username3.file, username4.file
etc etc...
I know I should use foreach with an if placed somewhere, but powershell is new for me, and I don't really know how to start it.
Here's something similar tweaked to what you want to do.
This created files of a specific size in your directory
$data = get-content "C:\Data\masteFile.csv"
$drcty = "C:\Data"
foreach($line in $data)
{
$a,$b = $line.Split("{,}")
$parent = $drcty+"\"+$a+"\"
$filename = $parent + $b.TRIM() +".txt"
write-host $filename
New-Item -ItemType directory -Path $parent
fsutil file createnew $filename 2000
}
this seems to do what you want. [grin]
# fake reading in a CSV file
# in real life, use Import-CSV
$InStuff = #'
_UserName, Platform_
username1, platformX1
username2, platformY
username3, platformX2
username4, platformX2
'# | ConvertFrom-Csv
$DestDir = $env:TEMP
$Extension = 'txt'
foreach ($IS_Item in $InStuff)
{
$TargetPath = Join-Path -Path $DestDir -ChildPath $IS_Item.Platform_
if (-not (Test-Path -LiteralPath $TargetPath))
{
# "$Null = " will supress unwanted output from New-Item
$Null = New-Item -Path $TargetPath -ItemType Directory
}
$FileName = $IS_Item._UserName, $Extension -join '.'
$FullFileName = Join-Path -Path $TargetPath -ChildPath $FileName
if (-not (Test-Path -LiteralPath $FullFileName))
{
# "$Null = " will supress unwanted output from New-Item
$Null = New-Item -Path $FullFileName -ItemType File
}
}
i think what it does is apparent, but i know i get ahead of where i otta be at times. so, if there are any questions, please feel free to ask ... [grin]
This worked for me.
$ErrorActionPreference = "Stop"
#Path of the CSV file resides
$csvData = Import-Csv -Path "$env:USERPROFILE\Desktop\data.csv"
$DestinationFolder = "C:\Stuffs"
foreach($record in $csvData)
{
$destPlatformFolder = $DestinationFolder + "\" + $record.platform_
if(-not(Test-Path -Path $destPlatformFolder)){
New-Item -Path $destPlatformFolder -ItemType Directory -Force | Out-Null
}
$destinationFile = $destPlatformFolder + "\" + $record._username
New-Item -Path $destinationFile -ItemType File -Force | Out-Null
}

How do I non-recursively gather folder sizes and their names? (Powershell)

Basically what I'm trying to do is gather users folder size from their network folder then export that to a .csv, directory structure looks something like this: network:\Department\user...User's-stuff
The script I have right now gets the department file name and the user's folder size, but not the user's name (folder name in the department). As for the TimeStamp, I'm not sure it's working correctly. It's meant to make a timestamp when it starts on the users in the next department so basically, all users in the same department will have the same timestamp.
This is what I have so far:
$root = "network"
$container= #()
$place = "C:\temp\"
$file = "DirectoryReport.csv"
Function Get-FolderSize
{
BEGIN{$fso = New-Object -comobject Scripting.FileSystemObject}
PROCESS
{
$prevDept = (Split-Path $path -leaf)
$path = $input.fullname
$folder = $fso.GetFolder($path)
$Volume = $prevDept + "-users"
$user = $folder.name #can't figure this part out...
$size = $folder."size(MB)"
if ( (Split-Path $path -leaf) -ne $prevDept)
{
$time = Get-Date -format M/d/yyy" "HH:mm #Probably wrong too..
}
return $current = [PSCustomObject]#{'Path' = $path; 'Users' = $user; 'Size(MB)' = ($size /1MB ); 'Volume' = $Volume; 'TimeStamp' = $time;}
}
}
$container += gci $root -Force -Directory -EA 0 | Get-FolderSize
$container
#Creating the .csv path
$placeCSV = $place + $file
#Checks if the file already exists
if ((test-path ($placeCSV)) -eq $true)
{
$file = "DirectoryReport" + [string](Get-Date -format MM.d.yyy.#h.mm.sstt) + ".csv"
rename-item -path $placeCSV -newname $file
$placeCSV = $place + $file
}
#Exports the CSV file to desired folder
$container | epcsv $placeCSV -NoTypeInformation -NoClobber
But in the CSV file the user and the timestamp are wrong. Thanks for any/all help
This really seems to be doing it the hard way. Why you wouldn't just use Get-ChildItem to do this almost makes this script seem a little masochistic to me, so I'm going to use that cmdlet instead of creating a comobject to do it.
I am a little confused as to why you wouldn't want to recurse for size, but ok, we'll go that route. This will get you your folders sizes, in MB.
#Get a listing of department folders
$Depts = GCI $root -force -Directory
#Loop through them
ForEach($Dept in $Depts){
$Users = #()
$Timestamp = Get-Date -Format "M/d/yyy HH:mm"
#Loop through each user for the current department
GCI $Dept -Directory |%{
$Users += [PSCustomObject]#{
User=$_.Name
Path=$_.FullName
"Size(MB)"=(GCI $_|Measure-Object -Sum Length|Select Sum)/1MB
Volume="$($Dept.Name)-Users"
TimeStamp=$Timestamp
}
}
}
#Rename output file if it exists
If(Test-Path "C:\Temp\DirectoryReport.csv"){
Rename-Item "C:\Temp\DirectoryReport.csv" "DirectoryReport.$(Get-Date -format MM.d.yyy.#h.mm.sstt).csv"
}
#Output file
$Users | Export-Csv "C:\Temp\DirectoryReport.csv" -NoTypeInformation
If you want to get the total size for all files within each user's folder, including files within subfolders, change the "Size(MB)"=(GCI $_|Measure-Object -Sum Length|Select Sum)/1MB to be recursive by replacing it with "Size(MB)"=(GCI $_ -recurse|Measure-Object -Sum Length|Select Sum)/1MB and that should have you good to go.