I need a script for windows to copy some files that contain a reference to a folder that has the name of the reference in their name.
Example: The file K:\examplefolder\sourcefolder\example3456file.pdf go to the folder K:\examplefolder\Destfolder\3456.
I created this:
$Src = 'K:\Escritorio\Scripts\Script_copiar_planos\Script OT\Origen'
$Dst = 'K:\Escritorio\Scripts\Script_copiar_planos\Script OT\Destino'
$file = 'K:\Escritorio\Scripts\Script_copiar_planos\Script OT\referencias.txt'
foreach ($referencia in Get-Content $file){
Get-ChildItem -Path $Src -Recurse -include *$referencia*.pdf -name -file | Copy-item -Destination $Dst\$referencia
}
In referencias.txt the references are listed like:
5678
91011
121314
For me apparently is okay, but when I go to execute the script it drops the following errors:
Copy-item : No se encuentra la ruta de acceso 'K:\Escritorio\Scripts\PDF-Con-81006600-en-el-nombre - copia (2).pdf' porque no existe.
En K:\Escritorio\Scripts\Script_copiar_planos\mover.ps1: 11 Carácter: 81
+ ... referencia*.pdf -name -file | Copy-item -Destination $Dst\$referencia
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (K:\Escritorio\S...- copia (2).pdf:String) [Copy-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.CopyItemCommand
When you pass argument -name to Get-ChildItem, it outputs only the file name portion of the path, e. g. "file1.pdf". This way Copy-Item has no information about the folder of the file and will use the working directory, whatever that happens to be before the call to Get-ChildItem.
Remove the argument -name to pass the full source path to Copy-Item:
Get-ChildItem -Path $Src -Recurse -include *$referencia*.pdf -file | Copy-item -Destination $Dst\$referencia
As a further optimization, you could replace -include by -filter, which is more efficient. From the docs:
Filters are more efficient than other parameters. The provider applies
filter when the cmdlet gets the objects rather than having PowerShell
filter the objects after they're retrieved.
Related
I need to batch copy a sort of file extensions from subfolders to specific folders for each location.
All folders have a 6 digit number
let's say folder Rood folder: BATCH
Subfolder 1: 000000
Subfolder 2: 111111
despite their extensions, most files have the name as the subfolder but some of them may have extra alphanumeric characters, therefore the script should grab only the ones that are not larger than 6 digits.
Example Subfolder1: 000000.pdf 000000.eps the script would need to grab all pdf within subfolders and export them to a PDF exclusive new folder, and the same would apply for eps files.
I know nothing about powershell but I know that something like this would work for an specific subfolder but I'm still missing the parts where it distributes them to a new PDFONLY and EPSONLY folders and the fact the I want to apply this to all the folders whiting the root folder.
Get-ChildItem -Path "C:\BATCH\*" -Include *.pdf,*.eps -Recurse | Copy-Item -Destination D:\
You can use Group-Object to group all the files by their extension and then loop over each group of objects, create a new folder with the desired name and lastly copy all objects of each group to their folders.
$target = 'D:\' # Set destination here
Get-ChildItem -Path C:\BATCH\* -Include *.py, *.ps1 -Recurse |
Group-Object Extension | ForEach-Object {
# Example of `$folderName` would be `PDF ONLY`
$folderName = '{0} ONLY' -f $_.Name.TrimStart('.').ToUpper()
$destination = Join-Path $target -ChildPath $folderName
# If the `$destination` folder doesn't exist
if(-not (Test-Path $destination)) {
# Create it
$null = New-Item $destination -ItemType Directory
}
Copy-Item -LiteralPath $_.Group.FullName -Destination $destination
}
Similar to Santiago's answer but with an extra filter and without grouping you can do this:
$destination = 'X:\somewhere'
Get-ChildItem -Path "C:\BATCH" -Include '*.pdf','*.eps' -File -Recurse |
Where-Object {$_.BaseName -match '^\d{6}$' } | # filter files with a BaseName of just 6 digits
ForEach-Object {
$targetPath = Join-Path -Path $destination -ChildPath ('{0}ONLY' -f $_.Extension.TrimStart(".").ToUpper())
# make sure the target path exists
# for directories, using the -Force switch either creates a new folder
# or returns the DirectoryInfo object of an existing folder.
$null = New-Item -Path $targetPath -ItemType Directory -Force
$_ | Copy-Item -Destination $targetPath
}
I modified PowerShell script from PowerShell - Batch change files encoding To UTF-8.
# Modified version of https://stackoverflow.com/q/18684793
[Threading.Thread]::CurrentThread.CurrentUICulture = 'en-US'
$Encoding = New-Object System.Text.UTF8Encoding($True) # If UTF8Encoding($False), It will be UTF-8 without BOM
$source = "C:\Users\AKULA\Desktop\SRC" # source directory
$destination = "C:\Users\AKULA\Desktop\DST" # destination directory
if (!(Test-Path $destination)) {
New-Item -Path $destination -ItemType Directory | Out-Null
}
# Delete all previously generated file
Get-ChildItem -Path $destination -Include * -File -Recurse | ForEach-Object {$_.Delete()}
# Recursively convert all files into UTF-8
foreach ($i in Get-ChildItem $source -Force -Recurse -Exclude "desktop.ini") {
if ($i.PSIsContainer) {
continue
}
$name = $i.Fullname.Replace($source, $destination)
$content = Get-Content $i.Fullname
if ($null -ne $content) {
[System.IO.File]::WriteAllLines($name, $content, $Encoding)
} else {
Write-Host "No content from: $i"
}
}
But after using it, I've found that PS cannot handle [ or ] well.
I made some test files that has diversity in name/content.
Get-Content : An object at the specified path C:\Users\AKULA\Desktop\SRC\FILENAME[[[[[[]]]]]]]].txt does not exist, or
has been filtered by the -Include or -Exclude parameter.
At C:\Users\AKULA\Desktop\Convert_to_UTF-8.ps1:24 char:16
+ $content = Get-Content $i.Fullname
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Since I cannot embed images in question, here is link of IMGUR album.
Full image list: https://imgur.com/a/aN1RG2L
These are what I've tested:
Test files have different names. Their name contains space, ',
[]. Also made up different language(Japanese, Korean).
These files have same content, encoded with UCS-2 BE BOM(UTF-16 BE) so
that I can check if it has re-encoded to UTF-8.
How can I make my script handle [ or ] in file name well?
tl;dr
Indeed, use of the -LiteralPath parameter is the best solution (in PowerShell (Core) v6+, you can shorten to -lp):
$content = Get-Content -LiteralPath $i.Fullname
-LiteralPath ensures that $i.Fullname is taken verbatim (literally); that is, [ and ] in the path are interpreted as themselves rather than having special meaning, as they would have as a -Path argument, due to being interpreted as a wildcard expression - note that -Path is positionally implied if you only pass a value (a string) as the first argument, as you did (Get-Content $i.FullName)
Note: This answer analogously applies to all cmdlets that have both -Path and -LiteralPath parameters, such as Set-Content, Out-File, and Set-Location.
As for what you tried:
$content = Get-Content $i.Fullname
is effectively the same as:
$content = Get-Content -Path $i.Fullname
That is, the (first) positional argument passed to Get-Content is implicitly bound to the
-Path parameter.
The -Path parameter accepts wildcard expressions to allow matching paths by patterns; in addition to support for * (any run of characters) and ? (exactly 1 character), [...] inside a wildcard pattern denotes a character set or range (e.g., [12] or [0-9]).
Therefore an actual path that contains [...], e.g., foo[10].txt, is not recognized as such, because the [10] is interpreted as a character set matching a single character that is either 1 or 0; that is foo[10].txt would match foo0.txt and foo1.txt, but not a file literally named foo[10].txt.
When (implicitly) using -Path, it is possible to escape [ and ] instances that should be interpreted verbatim, namely via the backtick (`), but note that this can get tricky to get right when quoting and/or variable references are involved.
If you know a path to be a literal path, it is best to form a habit of using -LiteralPath (which in PowerShell Core you can shorten to -lp).
However, if your path contains literal [ and ] and you also need wildcard matching, you must use `-escaping - see this answer.
There are at least two situations where the solution's good advice doesn't hold, unfortunately.
Selective error handling
Get-Content -LiteralPath "nobox[]" gives an error message and exception type as if wildcards are involved:
Get-Content : An object at the specified path box[] does not exist, or has been filtered by the -Include or -Exclude parameter.
At line:1 char:1
+ Get-Content -Path "nobox[]"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (System.String[]:String[]) [Get-Content], Exception
+ FullyQualifiedErrorId : ItemNotFound,Microsoft.PowerShell.Commands.GetContentCommand
whereas without the brackets, we get:
Get-Content : Cannot find path 'nobox' because it does not exist.
At line:1 char:1
+ Get-Content -LiteralPath "nobox"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (nobox:String) [Get-Content], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Therefore, to silently deal with an optional file, something like:
try {
$lines = Get-Content -LiteralPath $path -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
$lines = #()
}
chokes on paths with brackets.
Creating a hard or symbolic link
A minor and a major caveat:
The Path parameter, the name of the new item, "works like the LiteralPath parameter of other cmdlets", says the documentation of New-Item clearly, and that seems true and makes sense. Though I wish we could clarify that by writing -LiteralPath.
The Value parameter, the target of the link (also known as Target secretly in v5 and openly later), does not accept wildcard characters according to the same documentation, but that's a lie. The command:
New-Item -ItemType "HardLink" -Path "whatever" -Target "*"
makes Powershell squeal "Cannot set the location because path '*' resolved to multiple containers.".
So you always need the escapes for the target. If you have a file named "f[]", then this will display an error:
New-Item -ItemType "HardLink" -Path "whatever" -Target "f[]"
and this will create a link:
New-Item -ItemType "HardLink" -Path "f[2]" -Target ([WildcardPattern]::Escape("f[]"))
Same for ItemType "SymbolicLink".
I Want to replace certain character from my files
for example
i want to change > "original" -> "something else"
i use this script to replace it in one folder only using powershell
Dir |>> Rename-Item -NewName { $_.Name -replace "original","something " }
the issue is , with that code , that's only replace the files inside one folder and it don't replace them in the subfolders
i need it to replace it in file on the folder and in the sub folder files too , is that possible?
( i'm kinda new to this )
note : this code shows this error when there's any subfolders with the files
erorr =
Rename-Item : Source and destination path must be different.
At line:2 char:1
+ Rename-Item -NewName { $_.Name -replace "original","something " }
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : WriteError: (F:\folder-name\...ff\py-compat:String) [Rename-Item], IOException
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
Operating system : windows
info
Operating system model : windows 10
In case of directories the rename-item commandlet gets the same path as input and as value of the -newname parameter, so it errors out.
Adding the -verbose and -whatif switch parameters to the command will show that.
Use the -Path parameter to specify the base path you want to recurse, the -recurse switch parameter to recursively visit each subfolder, the -file switch parameter to filter out directories, and/or use the -filter parameter to include a subset of files.
Example:
get-childitem -Recurse -File -Path 'c:\temp' | rename-item -newname { $_.name -replace "original","replacement }
Since you can only view the C:\$RECYCLE.BIN Folder when you uncheck (Hide protected Operating System Files) you can't change the date modified through the usual way with PowerShell:
$file = Get-Item C:\Path\TO\File.txt
$file.LastWriteTime = (Get-Date)
or
$folder = Get-Item C:\folder1
$folder.LastWriteTime = (Get-Date)
When calling Get-Item C:\$RECYCLE.BIN, I get the following error:
Get-Item : Cannot find path 'C:\.bin' because it does not exist.
At line:1 char:9
+ $file = Get-Item C:\$Recycle.bin
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\.bin:String) [Get-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand
How would I go about changing the date modified of it?
As C:\$RECYCLE.BIN contains $, PowerShell tries to evaluate the variable $RECYCLE, which is empty. Thus, the resulting path C:\.bin cannot be found. Quote your path with single quotes (') to prevent PowerShell from evaluating your string and also add the -Force parameter as it is a system directory:
Get-Item -Path 'C:\$RECYCLE.BIN' -Force
You can then access your files and change the LastWriteTime as you already tried in your question:
$fileObject = Get-Item -Path 'C:\$RECYCLE.BIN\S-1-5-21-3323847664-626704455-790384891-1001\$RT8USDF.txt' -Force
$fileObject.LastWriteTime = Get-Date
I have windows 7
I have a folder name employeephotos about 100 pictures that all have name_last.
I would like to remove all the "_" from the files in the folder and make the files namelast.jpg.
I have try this command
PS U:\desktop\employeephotos> Dir | Rename-Item -NewName { $_.name replace"_",""}
Rename-Item : Source and destination path must be different.
At line:1 char:18
+ Dir | Rename-Item <<<< -NewName { $_.name -replace"_",""}
+ CategoryInfo : WriteError: (U:\desktop\employeephotos\New folder:String) [Rename-
+ FullyQualifiedErrorId : RenameItemIOError,Microsoft.PowerShell.Commands.RenameItemCommand
(From my comments)
Rename-Item is a PowerShell command, so you need to be working in PowerShell instead of the cmd.exe command prompt.
It might be picking up files with no _ in the name, the rename does not change the name, and they cannot be given the same name, so it fails. Try dir *_* at the start to only find and rename files with _ in the name.
You might try something like this:
Get-ChildItem -Filter "*_*" | Foreach-Object { Rename-Item -Path $_.Name -NewName $_.Name.Replace("_", "") -WhatIf }
If it produces the output you're looking for, then just pull the -WhatIf parameter off.