I'm having ongoing trouble with a script I've written that is (supposed) to do the following.
I have one folder with a number of csv files, and I want to copy the latest file with the company name into another folder, and rename it.
It is in the current format:
21Feb17070051_CompanyName_Sent21022017
I want it in the following format:
CompanyName21022017
So I have the following powershell script to do this:
## Declare variables ##
$DateStamp = get-date -uformat "%Y%m%d"
$csv_dest = "C:\Dest"
$csv_path = "C:\Location"
## Copy latest Company CSV file ##
get-childitem -path $csv_path -Filter "*Company*.csv" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 |
copy-item -Destination $csv_dest
## Rename the file that has been moved ##
get-childitem -path $csv_dest -Filter "*Company*.csv" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 | rename-item $file -NewName {"Company" + $DateStamp + ".csv"}
The file seems to copy ok, but the rename fails -
Rename-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Powershell Scripts\MoveCompanyFiles.ps1:20 char:41
+ select-object -last 1 | rename-item $file -NewName {"CompanyName" + $DateSt ...
I think it is something to do with the order in which powershell works, or the fact it can't see the .csv in the $file variable. There are other files (text files, batch files) in the destination, in case that affects things.
Any help in where I'm going wrong would be appreciated.
As wOxxOm answered, you need to remove $file from Rename-Item as it is not defined and the cmdlet already receives the inputobject through the pipeline.
I would also suggest that you combine the two operations by passing through the fileinfo-object for the copied file to Rename-Item. Ex:
## Declare variables ##
$DateStamp = get-date -uformat "%Y%m%d"
$csv_dest = "C:\Dest"
$csv_path = "C:\Location"
## Copy and rename latest Company CSV file ##
Get-ChildItem -Path $csv_path -Filter "*Company*.csv" |
Where-Object { -not $_.PSIsContainer } |
Sort-Object -Property CreationTime |
Select-Object -Last 1 |
Copy-Item -Destination $csv_dest -PassThru |
Rename-Item -NewName {"Company" + $DateStamp + ".csv"}
You can rename and copy in a single command. Just use Copy-Item Command and give new path and name as -Destination parameter value. It will copy and rename the file. You can find an example below.
$source_path = "c:\devops\test"
$destination_path = "c:\devops\test\"
$file_name_pattern = "*.nupkg"
get-childitem -path $source_path -Filter $file_name_pattern |
Copy-Item -Destination { $destination_path + $_.Name.Split("-")[0] + ".nupkg"}
Related
I've tried to rename a csv with powershell and then move it automatically to another folder, when there's no file.
Originally, the csv-name looks like this: import_9999_2020-08-13_132238.csv but the part with 9999 can also include just 2 or 3 digits.
My actual Code looks like:
#Import of path and target-path
$path = "\\network-path\subfolder\subfolder\subfolder\subfolder\subfolder1\"
$target_path = "\\network-path\subfolder\subfolder\subfolder\subfolder\subfolder2\"
#endless loop
$a=$true
while($a -eq $true){
$Files = gci $path
$TargetFiles = gci $target_path
#wait 5 minutes if path is empty
if(($Files).Count -eq 0){
sleep -Seconds 300
}
#if path is filled with one or more files
else {
#if file in target-path is processed (from another program)
if(($TargetFiles).count -eq 0){
#rename and move the latest file
get-childitem -path $path -Filter "import_*.csv"|
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 |
Rename-Item -NewName {($_.Name.Substring(0,($_.Name.Length)-22))+".csv"} |
Move-Item -Destination $target_path +"$($_.Name).csv"
}
sleep -Seconds 20
}
}
It works partly and renames the csv, but it doesn't move it to the target-path. The path is correct, i've copied it from the Windows-Explorer.
Any Ideas, why the program doesn't work completely? Thanks
For better readability, I would split the code where you find the original file, rename it and move it to the target path into several lines.
Also, Move-Item can rename the file aswell, so no need to do a Rename-Item first:
$file = Get-ChildItem -Path $path -Filter "import_*.csv" -File |
Sort-Object -Property $_.LastWriteTime |
Select-Object -Last 1
# create the new name for the file.
# or use regex: $newName = '{0}{1}' -f ($file.BaseName -replace '(_\d{4}-\d{2}-\d{2}_\d+)$'), $file.Extension
$newName = '{0}{1}' -f (($file.BaseName -split '_')[0..1] -join '_'), $file.Extension
# now move the file and rename at the same time
$file | Move-Item -Destination (Join-Path -path $target_path -ChildPath $newName)
I changed CreationTime to LastWriteTime to really get the latest file.
I also added the -File switch to the Get-ChildItem cmdlet, because only for old PowerShell versions you need to use where-object { -not $_.PSIsContainer }
If you want to pass it down the pipeline add -Passthru to the Rename-Item cmdlet
Somefile.txt | Rename-Item -NewName {$_.basename + "abc" + $_.ext} | # nothing in the pipeline
Somefile.txt | Rename-Item -NewName {$_.basename + "abc" + $_.ext} -Passthru | # now the new named fileinfo object is in the pipeline, contained in automatic variable $_
I want to copy a file to multiple destinations using a script that filters through a directory and selects the newest file in the $File_path then change its name and copies it to the $destination, the script i'm using is this:
$File_path = "C:\TEMP\export\liste\Text_Utf8\"
$destination = "C:\TEMP\export\C7E001"
get-childitem -path $File_path -Filter "Ges?*.txt" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1 | copy-item -Destination (join-path $destination "FRER3000CCFETES01_IN.DEV")
this only copies it to one location, is there a way to improve it to copy the same file to multiple locations? i have seen this thread but it seems different.
the other locations are as follow:
C:\TEMP\export\C7P001
C:\TEMP\export\C7F001
C:\TEMP\export\C7S001
and so on.
thank you.
Although my answer isn't very different to Peter's answer, This uses the LastWriteTime property to get the latest file and uses the FullName property of the file to copy in the Copy-Item cmdlet.
$File_path = "C:\TEMP\export\liste\Text_Utf8"
$destinations = "C:\TEMP\export\C7E001", "C:\TEMP\export\C7F001", "C:\TEMP\export\C7S001"
$fileToCopy = Get-ChildItem -Path $File_path -Filter "Ges*.txt" -File |
Sort-Object -Property $_.LastWriteTime |
Select-Object -Last 1
foreach ($dest in $destinations) {
Copy-Item -Path $fileToCopy.FullName -Destination (Join-Path -Path $dest -ChildPath "FRER3000CCFETES01_IN.DEV")
}
You can use an foreach object loop
$File_path = "C:\TEMP\export\liste\Text_Utf8\"
$destination = "C:\TEMP\export\C7E001", "C:\TEMP\export\C7P001", "C:\TEMP\export\C7F001", "C:\TEMP\export\C7S001"
$Files = get-childitem -path $File_path -Filter "Ges?*.txt" |
where-object { -not $_.PSIsContainer } |
sort-object -Property $_.CreationTime |
select-object -last 1
$Destination | Foreach-Object {copy-item $Files -Destination (join-path $_ "FRER3000CCFETES01_IN.DEV")}
Hello All,
I wish to replace only the old file with new file
I tried
Set-Location C:\contains_newfolder_contents\Old Folder
Get-ChildItem | ForEach-Object {
if ((Test-Path 'C:\contains_newfolder_contents\Sample Folder\$_' ) -and
(.$_.LastWriteTime -gt C:\contains_newfolder_contents\Sample Folder\$_.LastWriteTime' )) {
Copy-Item .\$_ -destination 'C:\contains_newfolder_contents\Sample Folder'
}
}
Kindly correct me!
Here's a one-line solution. I used different folder names to make the example easier to read.
Get-ChildItem C:\temp\destination|foreach-object {$sourceItem = (get-item "c:\temp\source\$($_.name)" -erroraction ignore); if ($sourceItem -and $sourceItem.LastWriteTime -gt $_.lastwritetime) {Copy-Item -path $sourceItem -dest $_.fullname -verbose}}
For each existing file, it finds the matching file in the source folder. $sourcItem will be null if there is no matching source item. It proceeds to compare the dates and copy if the source date is newer.
you can do it too :
Get-ChildItem "C:\contains_newfolder_contents\Old Folder" -file | sort LastWriteTime -Descending | select -First 1 | Copy-Item -Destination 'C:\contains_newfolder_contents\Sample Folder'
Instead of making several reads to the source, I propose you make a lookup table and then these simple commands will achieve the desired results.
$source = 'C:\temp\Source'
$destintation = 'C:\temp\Destination'
$lookup = Get-ChildItem $destintation | Group-Object -Property name -AsHashTable
Get-ChildItem -Path $source |
Where-Object {$_.lastwritetime -gt $lookup[$_.name].lastwritetime} |
Copy-Item -Destination $destintation
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
I created ps script to copy only files in the folder structure- recursive
cp $source.Text -Recurse -Container:$false -destination $destination.Text
$dirs = gci $destination.Text -directory -recurse | Where { (gci $_.fullName).count -eq 0 } | select -expandproperty FullName
$dirs | Foreach-Object { Remove-Item $_ }
it is working fine. but the problem i have files in the same names. it is not copying duplicated files. i need to rename if file already exist
source:
folderA--> xxx.txt,yyy.txt,
folderB-->xxx.txt,yyy.txt,zzz.txt,
folderc-->xxx.txt
destination (requirement)
xxx.txt
xxx1.txt
xxx2.txt
yyy.txt
yyy1.txt
zzz.txt
Here a solution where I use the Group-Object cmdlet to group all items by the filename. I then iterate over each group and if the group contains more then one file, I append _$ito it where $i starts by one and gets incremented:
$source = $source.Text
$destination = $destination.Text
Get-ChildItem $source -File -Recurse | Group-Object Name | ForEach-Object {
if ($_.Count -gt 1) { # rename duplicated files
$_.Group | ForEach-Object -Begin {$i = 1} -Process {
$newFileName = $_.Name -replace '(.*)\.(.*)', "`$1_$i.`$2"
$i++
Copy-Item -Path $_.FullName -Destination (Join-Path $destination $newFileName)
}
}
else # the filename is unique, just copy it.
{
$_.Group | Copy-Item -Destination $destination
}
}
Note:
You may change the -File to -Container:$false if your PowerShell version doesn't support it. Also note that the script doesn't look into the destination folder whether a file with the same name already exist.