Copiar archivos en ciertos directorios según el nombre del archivo

Copiar archivos en ciertos directorios según el nombre del archivo

Realmente no tengo ningún punto de contacto real con Batch o PowerShell, sin embargo, he configurado un directorio de empleados para el cual ahora estoy buscando una solución, donde los documentos se copian automáticamente en el directorio antes mencionado mediante un script:

  • Los documentos que se van a copiar se encuentran actualmente todos en una carpeta y deben copiarse en la subcarpeta respectiva del directorio.
  • Los nombres de los archivos de los documentos se estructuran de la siguiente manera, donde XX/ XXXXson las variables que desempeñan un papel en la asignación:
    • ABCD_123456_1234567_12345_XX.pdf
      ABCD_123456_1234567_12345_XXXX.pdf
  • Los directorios de destino también los contienen, actualmente hay alrededor de cincuenta diferentes.

¿Es posible señalar únicamente las variables entre el tercer guión bajo y la extensión del archivo, ya que varían, posiblemente agregando una variable de tres o cinco dígitos?

  • Ya utilicé la función de búsqueda, pero no encontré nada que corresponda exactamente al ejemplo dado:
    • C:\ABCD_123456_1234567_12345_99\\11/17/19/1991\Directory\99_ABCD\
      C:\ABCD_123456_1234567_12345_9999\\11/17/19/1991\Directory\9999_ABCD\
    • Posiblemente:
      C:\ABCD_123456_1234567_12345_999\\11/17/19/1991\ directory\999_ABCD\
      C:\ABCD_123456_1234567_12345_99999\\11/17/19/1991\Directory\99999_ABCD\

Intenté configurar esto con $FileName-split, lo que realmente no funcionó, pero logré completar los nombres de los archivos para contarlos, como en 30:27:

  • C:\ABCD_123456_1234567_12345_99C:\ABCD_123456_1234567_12345_0099
  • Estoy teniendo dificultades para analizar las carpetas de destino, con todas las carpetas existentes y las variables en sus nombres de archivo, llegando hasta aquí:
    $Directory = "C:\Folder\" 
    $FileNames = (Get-Item $Directory).GetFiles()
    
    foreach($FileName in $FileNames) {
      $Destination = "\\10.10.10.10\destinationfolder"
      { Move-Item -Path $FileName -Destination $Destination }
    }
    

¿Hay alguna manera de mover los archivos por coincidencia o tengo que analizar también todas las carpetas de destino contándolas para configurar la parte que se va a verificar?

Esto puede visualizarlo.

Respuesta1

Esta sería una solución PowerShell:

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Head = ($_.BaseName -split '_')[0]
    $Tail = ($_.BaseName -split '_')[-1]
    $DestinationPath = Join-Path $Destination ("{0}_{1}" -f $Head, $Tail)
    New-Item -Path $DestinationPath -ItemType Directory -ErrorAction SilentlyContinue
    Copy-Item $_.FullName $DestinationPath -Force
}

Actualizar:

Siguiendo tus comentarios, supongo que esto es más lo que quieres. Sin embargo, debes asegurarte de que cada final tenga exactamente una carpeta que coincida. por ejemplo si hay una carpeta que se llama abc_123y otra que se llama def_123simplemente tomará la primera, porque según tus comentarios solo tomamos en consideración el final

$Destination = "\\10.10.10.10\destinationfolder"
Get-ChildItem "C:\Folder" | ForEach-Object {
    $Tail = ($_.BaseName -split '_')[-1]
    $Filter = "*_{0}" -f $Tail
    $DestDir = Get-ChildItem $Destination -Filter $Filter -Directory | Select-Object -ExpandProperty FullName -First 1
    if ($DestDir) {
        Copy-Item $_.FullName $DestDir -Force
    } else {
        "No Directory was found that matched the Filter {0} in Directory {1}" -f $Filter, $Destination | Write-Host
    }
}

Hace lo siguiente:

  • Primero configure la carpeta de destino en la $DestinationVariable
  • Obtenga todos los elementos en una carpeta con Get-ChildItem(si necesita que esto sea recursivo, agregue el -Recursemodificador, si solo desea agregar ciertos tipos de archivos -Filter *.txto cualquier otra extensión)
  • recorrer todos los archivos encontrados conForEach-Object
  • divida el nombre base (que es el nombre de archivo sin extensión) _y obtenga la última parte del nombre base y guárdelo en la $Endingvariable
  • Calcule el nombre de la carpeta formateando la variable final a un nombre de carpeta con-f
  • Utilícelo Join-Pathpara conectar el destino y la carpeta calculada y guardarlo en$Destinationpath
  • Cree la carpeta si no existe con New-Item. Si ya existe, esta línea no sobrescribe la carpeta existente.
  • Copie el elemento a la carpeta de destino conCopy-Item

Respuesta2

No entiendo tu pregunta pero si quieres copiar y pegar un archivo como este

ABCD_123456_1234567_12345_1.txt copy into 1_ABCD
ABCD_123456_1234567_12345_2.txt copy into 2_ABCD
ABCD_123456_1234567_12345_3.txt copy into 3_ABCD
ABCD_123456_1234567_12345_9999.txt copy into 9999_ABCD

Entonces tal vez este comando cmd pueda ayudarte

echo D|for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

segundo comando con la misma salida:

for /l %x in (1,1,20) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

puedes cambiar 20a9999 producción

Este comando creará una carpeta si no existe y luego pegará el archivo y si el archivo existe dentro de una carpeta, lo sobrescribirá.

ACTUALIZAR:

Comando para buscar nombres de archivos sin extensión(y agregar el resultado al nombre del archivo sin texto.txt)

for /f %x in ('dir /b') do @echo %~nx>>filenamewithoutext.txt

Leer el archivo de salida y guardar variables en un archivo nuevo variables.txt

for /f "tokens=5 delims=_" %x in (filenamewithoutext.txt) do @echo %x>>variables.txt

Leer variables y copiar archivos en diferentes carpetas

for /f %x in (variables.txt) do @xcopy ABCD_123456_1234567_12345_%x.txt ABC_%x\ /Y /Q

puedes cambiar ABC_%xa%x_ABC

Respuesta3

Mientras leo tu pregunta, creo que esto debería funcionar:

$Source      = 'C:\Folder\'
$Destination = '\\10.10.10.10\destinationfolder\'

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? { ($BaseName = $_.BaseName) -match '_' } | ForEach{
    $Head       = $BaseName.Split('_')[0]
    $Tail       = $BaseName.Split('_')[-1]       ### (Index of -1) -> Last array element
    $SubFolder  = "$Tail`_$Head"                 ### *** Escaped <underscore> required ***
    If ( Test-Path ( $FullPath = Join-Path $Destination $SubFolder ) ) {
        Copy-Item -Literal $_.FullName $FullPath
    } Else {
        '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
    }
}

Para cada objetoMétodo String.Split()Ruta de uniónRuta de pruebaCopiar elemento


Depuración

Utilicé esta variación del código para solucionar problemas de manipulación de cadenas, lo que descubrió el problema del uso de un guión bajo en cadenas de expansión. Pone todas las variables intermedias en unGridViewpara escaneo rápido:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ? BaseName -match '_' | ForEach{
    $Head       = $_.BaseName.Split('_')[0]
    $Tail       = $_.BaseName.Split('_')[-1]
    $SubFolder  = "$Tail`_$Head"
    $FullPath   = Join-Path $Destination $SubFolder

    [PSCustomObject]@{
        'Head'       = $Head
        'Tail'       = $Tail
        'SubFolder'  = $SubFolder
        'FullPath'   = $FullPath
        'SubExists'  = Test-Path $FullPath
    }
} | Out-gridview

Mejoramiento

Las variables intermedias son excelentes durante el desarrollo/pruebas/depuración, pero pueden ralentizar las cosas dentro de un bucle ejecutado tantas veces. Entonces, una vez que las asignaciones parecieron sólidas, trabajé hacia atrás, primero copiando la expresión que define $SubFolderen el portapapeles, luego reemplazando $SubFolderla aparición de $FullPath = ...con su definición entre paréntesis, luego hago lo mismo para las variables dentro de esa definición. También cesión combinada con primer uso. El resultado fue:

Get-ChildItem -Path $Source -FIlter *.pdf -File | ForEach{
If ( ($BaseName = $_.BaseName) -match '_' ) {
        If ( Test-Path ( $FullPath=Join-Path $Destination "$($BaseName.Split('_')[-1])`_$($BaseName.Split('_')[0])" ) ) {
            Copy-Item -Literal $_.FullName $FullPath
        } Else {
            '"{0}" not copied because the directory "{1}" does not exist' -f $_.Name , $FullPath | Write-Output
        }
    }
}

Es un infierno leerlo, pero debería valer la pena para grandes conjuntos de datos.

información relacionada