简体   繁体   中英

How do I copy a list of files and rename them in a PowerShell Loop

We are copying a long list of files from their different directories into a single location (same server). Once there, I need to rename them.

I was able to move the files until I found out that there are duplicates in the list of file names to move (and rename). It would not allow me to copy the file multiple times into the same destination.

Here is the list of file names after the move:

"10.csv",
"11.csv",
"12.csv",
"13.csv",
"14.csv",
"15.csv",
"16.csv",
"17.csv",
"18.csv",
"19.csv",
"20.csv",
"Invoices_Export(16) - Copy.csv" (this one's name should be "Zebra.csv")

I wrote a couple of foreach loops, but it is not working exactly correctly.

The script moves the files just fine. It is the rename that is not working the way I want. The first file does not rename; the other files rename. However, they leave the moved file in place too.

This script requires a csv that has 3 columns:

  1. Path of the file, including the file name (eg. c:\\temp\\smefile.txt )
  2. Destination of the file, including the file name (eg. c:\\temp\\smefile.txt )
  3. New name of the file. Just the name and extention.
# Variables
$Path = (import-csv C:\temp\Test-CSV.csv).Path
$Dest = (import-csv C:\temp\Test-CSV.csv).Destination
$NN = (import-csv C:\temp\Test-CSV.csv).NewName

#Script
foreach ($D in $Dest) {
    $i -eq 0
    Foreach ($P in $Path) {
        Copy-Item $P -destination C:\Temp\TestDestination -force
    }

    rename-item -path "$D" -newname $NN[$i] -force
    $i += 1
}

There were no error per se, just not the outcome that I expected.

Welcome to Stack Overflow!

There are a couple ways to approach the duplicate names situation:

  1. Check if the file exists already in the destination with Test-Path . If it does, start a while loop that appends a number to the end of the name and check if that exists. Increment the number you append after each check with Test-Path . Keep looping until Test-Path comes back $false and then break out of the loop.
  2. Write an error message and skip that row in the CSV.

I'm going to show a refactored version of your script with approach #2 above:

$csv = Import-Csv 'C:\temp\Test-CSV.csv'

foreach ($row in $csv)
{
    $fullDestinationPath = Join-Path -Path $row.Destination -ChildPath $row.NewName

    if (Test-Path $fullDestinationPath)
    {
        Write-Error ("The path '$fullDestinationPath' already exists. " + 
                     "Skipping row for $($row.Path).")

        continue
    }

    # You may also want to check if $row.Path exists before attempting to copy it

    Copy-Item -Path $row.Path -Destination $fullDestinationPath
}

Now that your question is answered, here are some thoughts for improving your code:

  1. Avoid using acronyms and abbreviations in identifiers (variable names, function names, etc.) when possible. Remember that code is written for humans and someone else has to be able to understand your code; make everything as obvious as possible. Someone else will have to read your code eventually, even if it's Future-You™!
  2. Don't Repeat Yourself (called the "DRY" principle). As Lee_daily mentioned in the comments, you don't need to import the CSV file three times. Import it once into a variable and then use the variable to access the properties.
  3. Try to be consistent. PowerShell is case-insensitive, but you should pick a style and stick to it (ie ForEach or foreach , Rename-Item or rename-item , etc.). I would recommend PascalCase as PowerShell cmdlets are all in PascalCase.
  4. Wrap literal paths in single quotes (or double quotes if you need string interpolation). Paths can have spaces in them and without quotes, PowerShell interprets a space as you are passing another argument.
  5. $i -eq 0 is not an assignment statement, it is a boolean expression. When you run $i -eq 0 , PowerShell will return $true or $false because you are asking it if the value stored in $i is 0 . To assign the value 0 to $i , you need to write it like this: $i = 0 .
  6. There's nothing wrong with $i += 1 , but it could be shortened to $i++ , if you want to.
  7. When you can, try to check for common issues that may come up with your code. Always think about what can go wrong. " If I copy a file, what can go wrong? Does the source file or folder exist? Is the name pulled from the CSV a valid path name or does it contain characters that are invalid in a path (like : ) ?" This is called defensive programming and it will save you so so many headaches. As with anything in life, be careful not to go overboard. Only check for likely scenarios; rare edge-cases should just raise errors.
  8. Write some decent logs so you can see what happened at runtime. PowerShell provides a pair of great cmdlets called Start-Transcript and Stop-Transcript . These cmdlets log all the output that was sent to the PowerShell console window, in addition to some system information like the version of PowerShell installed on the machine. Very handy!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM