
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
/XXXX
son 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_99
→C:\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_123
y otra que se llama def_123
simplemente 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
$Destination
Variable - Obtenga todos los elementos en una carpeta con
Get-ChildItem
(si necesita que esto sea recursivo, agregue el-Recurse
modificador, si solo desea agregar ciertos tipos de archivos-Filter *.txt
o cualquier otra extensión) - recorrer todos los archivos encontrados con
ForEach-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$Ending
variable - Calcule el nombre de la carpeta formateando la variable final a un nombre de carpeta con
-f
- Utilícelo
Join-Path
para 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 con
Copy-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
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_%x
a%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 objeto • Método String.Split() • Ruta de unión • Ruta de prueba • Copiar 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 unGridView
para 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 $SubFolder
en el portapapeles, luego reemplazando $SubFolder
la 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.