How do I consolidate copy and rename commands into one using Powershell? - powershell

Currently I am using PS to copy files from a network location based on a CSV file, then I am renaming them using a variation of the same data. This requires that I run two separate commands.
How do I consolidate these commands into one?
Copy:
import-csv C:\TEST\test.csv | foreach {copy-item -path $_.npath -destination 'C:\TEST\'}
Paste:
import-csv C:\TEST\test.csv | foreach {rename-item -path $_.lpath -newname $_.newalias}
Notice that the -path trigger in each case refers to a separate variable header, npath vs. lpath which correspond to the network file location, and then a local file location which have been manually entered.
On the same note, how could I concatenate this variable to constant data. If I have a variable fn which represents the file name and another path, could I theoretically do:
foreach {rename-item -path 'C:\TEST\' + $_.fn
Or:
foreach {rename-item -path $_.path + $_.fn

Just append the two commands
import-csv C:\TEST\test.csv | foreach {copy-item -path $_.npath -destination 'C:\TEST\';rename-item -path $_.lpath -newname $_.newalias }
for your second question there are lots of ways to append string
C:(...)WindowsPowerShell>$data = "bob"
C:(...)WindowsPowerShell>echo "this is a $data"
C:(...)WindowsPowerShell>$concat = "hi" + " george"
C:(...)WindowsPowerShell>$concat
hi george
C:(...)WindowsPowerShell>[string]::Format("{0} {1}","string 1","string 2")
string 1 string 2

Related

Bulk renaming files with different extensions in order using powershell

is there a way to bulk rename items such that a folder with the items arranged in order would have their name changed into numbers with zero padding regardless of extension?
for example, a folder with files named:
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
would end up like this:
01.jpg
02.jpg
03.jpg
04.png
05.png
06.png
07.png
08.jpg
09.jpg
10.mp4
i had a script i found somewhere that can rename files in alphabetical order. however, it seems to only accepts conventionally bulk renamed files (done by selecting all the files, and renaming them such that they read "file (1).jpg" etc), which messes up the ordering when dealing with differing file extensions. it also doesn't seem to rename files with variations in their file names. here is what the code looked like:
Get-ChildItem -Path C:\Directory -Filter file* | % {
$matched = $_.BaseName -match "\((?<number>\d+)\)"
if (-not $matched) {break;}
[int]$number = $Matches["number"]
Rename-Item -Path $_.FullName -NewName "$($number.ToString("000"))$($_.Extension)"
}
If your intent is to rename the files based on the ending digits of their BaseName you can use Get-ChildItem in combination with Where-Object for filtering them and then pipe this result to Rename-Item using a delay-bind script block.
Needles to say, this code does not handle file collision. If there is more than one file with the same ending digits and the same extension this will error out.
Get-ChildItem -Filter file* | Where-Object { $_.BaseName -match '\d+$' } |
Rename-Item -NewName {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
To test the code you can use the following:
#'
file1.jpg
file2.jpg
file3.jpg
file4.png
file5.png
file6.png
file7.png
file8.jpg
file9.jpg
file10.mp4
'# -split '\r?\n' -as [System.IO.FileInfo[]] | ForEach-Object {
$basename = '{0:00}' -f [int][regex]::Match($_.BaseName, '\d+$').Value
$basename + $_.Extension
}
You could just use the number of files found in the folder to create the appropriate 'numbering' format for renaming them.
$files = (Get-ChildItem -Path 'D:\Test' -File) | Sort-Object Name
# depending on the number of files, create a formating template
# to get the number of leading zeros correct.
# example: 645 files would create this format: '{0:000}{1}'
$format = '{0:' + '0' * ($files.Count).ToString().Length + '}{1}'
# a counter for the index number
$index = 1
# now loop over the files and rename them
foreach ($file in $files) {
$file | Rename-Item -NewName ($format -f $index++, $file.Extension) -WhatIf
}
The -WhatIf switch is a safety measure. With this, no file gets actually renamed, you will only see in the console what WOULD happen. Once you are content with that, remove the -WhatIf switch from the code and run again to rename all your files in the folder

Compress File per file, same name

I hope you are all safe in this time of COVID-19.
I'm trying to generate a script that goes to the directory and compresses each file to .zip with the same name as the file, for example:
sample.txt -> sample.zip
sample2.txt -> sample2.zip
but I'm having difficulties, I'm not that used to powershell, I'm learning and improving this script. In the end it will be a script that deletes files older than X days, compresses files and makes them upload in ftp .. the part of excluding with more than X I've already managed it for days, now I grabbed a little bit on this one.
Last try at moment.
param
(
#Future accept input
[string] $InputFolder,
[string] $OutputFolder
)
#test folder
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
$Name2 = Get-ChildItem $InputFolder -Filter '*.csv'| select Name
Set-Variable SET_SIZE -option Constant -value 1
$i = 0
$zipSet = 0
Get-ChildItem $InputFolder | ForEach-Object {
$zipSetName = ($Name2[1]) + ".zip "
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
$i++;
$Name2++
if ($i -eq $SET_SIZE) {
$i = 0;
$zipSet++;
}
}
You can simplify things a bit, and it looks like most of the issues are because in your script example $Name2 will contain a different set of items than the Get-ChildItem $InputFolder will return in the loop (i.e. may have other objects other than .csv files).
The best way to deal with things is to use variables with the full file object (i.e. you don't need to use |select name). So I get all the CSV file objects right away and store in the variable $CsvFiles.
We can additionally use the special variable $_ inside the ForEach-Object which represents the current object. We also can use $_.BaseName to give us the name without the extension (assuming that's what you want, otherwise use $_Name to get a zip with the name like xyz.csv).
So a simplified version of the code can be:
$InputFolder= "C:\Temp\teste"
$OutputFolder="C:\Temp\teste"
#Get files to process
$CsvFiles = Get-ChildItem $InputFolder -Filter '*.csv'
#loop through all files to zip
$CsvFiles | ForEach-Object {
$zipSetName = $_.BaseName + ".zip"
Compress-Archive -Path $_.FullName -DestinationPath "$OutputFolder\$zipSetName"
}

concatenate columnar output in PowerShell

I want to use PowerShell to generate a list of commands to move files from one location to another. (I'm sure PowersSell could actually do the moving, but I'd like to see the list of commands first ... and yes I know about -WhatIf).
The files are in a series of subfolders one layer down, and need moved to a corresponding series of subfolders on another host. The subfolders have 8-digit identifiers. I need a series of commands like
move c:\certs\40139686\22_05_2018_16_23_Tyre-Calligraphy.jpg \\vcintra2012\images\40139686\Import\22_05_2018_16_23_Tyre-Calligraphy.jpg
move c:\certs\40152609\19_02_2018_11_34_Express.JPG \\vcintra2012\images\40152609\Import\19_02_2018_11_34_Express.JPG
The file needs to go into the \Import subdirectory of the corresponding 8-digit-identifier folder.
The following Powershell will generate the data that I need
dir -Directory |
Select -ExpandProperty Name |
dir -File |
Select-Object -Property Name, #{N='Parent';E={$_.Directory -replace 'C:\\certs\\', ''}}
40139686 22_05_2018_16_23_Tyre-Calligraphy.jpg
40152609 19_02_2018_11_34_Express.JPG
40152609 Express.JPG
40180489 27_11_2018_11_09_Appointment tuesday 5th.jpg
but I am stuck on how to take that data and generate the concatenated string which in PHP would look like this
move c:\certs\$Parent\$Name \\vcintra2012\images\$Parent\Import\$Name
(OK, the backslashes would likely need escaped but hopefully it is clear what I want)
I just don't know to do this sort of concatenation of columnar output - any SO refs I look at e.g.
How do I concatenate strings and variables in PowerShell?
are not about how to do this.
I think I need to pipe the output to an expression that effects the concatenation, perhaps using -join, but I don't know how to refer to $Parent and $Name on the far side of the pipe?
Pipe your output into a ForEach-Object loop where you build the command strings using the format operator (-f):
... | ForEach-Object {
'move c:\certs\{0}\{1} \\vcintra2012\images\{0}\Import\{1}' -f $_.Parent, $_.Name
}
Another approach:
$source = 'C:\certs'
$destination = '\\vcintra2012\images'
Get-ChildItem -Path $source -Depth 1 -Recurse -File | ForEach-Object {
$targetPath = [System.IO.Path]::Combine($destination, $_.Directory.Name , 'Import')
if (!(Test-Path -Path $targetPath -PathType Container)) {
New-Item -Path $targetPath -ItemType Directory | Out-Null
}
$_ | Move-Item -Destination $targetPath
}

Append file name using CSV

I'm trying to rename files that match values in column one of a csv adding the value in column 3 to the beginning of the file name leaving the rest of the file name intact. Here is what I have so far. I cant seem to figure out the Rename-Item.
# Common Paths
$PathRoot = "C:\Temp\somefiles" #Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Get each file and rename it
ForEach($row in $ClientAccounts)
{
$CurrentClientTaxId = $row[-1].TaxId
$CurrentClientName = $row[-1].ClientName
#loop through files
$FileExists = Test-Path -Path "$PathTotal\*$CurrentClientLB_Number*" #See if there is a file.
If ($FileExists -eq $true) #The file does exist.
{
#ReName File
Rename-Item -Path $PathRoot -NewName {$CurrentClientName + " " + $_.name}
}
}
Lets suppose your CSV file looks similar to this:
"LB_Number","TaxId","ClientName"
"987654","12345","Microsoft"
"321456","91234","Apple"
"741852","81234","HP"
Column 1 has the portion of the existing file name to match
Column 3 has the client name you want to prepend to the file name
Then your function could be something like this:
# Common Paths
$PathRoot = "C:\Temp\somefiles" # Where the files are to rename
# Get csv file
$ClientAccounts = Import-CSV -path "\\server\some\path\to\csv\file.csv"
# Loop through all clients in the CSV
foreach($client in $ClientAccounts) {
$CurrentClientLB_Number = $client.LB_Number
$CurrentClientTaxId = $client.TaxId # unused...??
$CurrentClientName = $client.ClientName
# get the file(s) using wildcards (there can be more than one)
# and rename them
Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File | ForEach-Object {
$_ | Rename-Item -NewName ($CurrentClientName + " " + $_.Name)
}
# Curly braces work also, although this is not very common practice:
# Get-ChildItem -Path "$PathRoot\*$CurrentClientLB_Number*" -File |
# Rename-Item -NewName { ($CurrentClientName + " " + $_.Name) }
}
I use the -File parameter with Get-ChildItem so the function will only return files; not directories. If you are using PowerShell version 2.0, you need to replace that with | Where-Object { !$_.PSIsContainer }.

Powershell change extension

I have a problem with change extension of a file. I need to write a script which is replicating data, but data have two files. Filename is not a string, so we can't use normal -replace
I need to get from
filename.number.extension
this form
filename.number.otherextension
We try to use a split, but this command show us things like below
filename
number
otherextension
Thanks for any ideas,
[System.IO.Path]::ChangeExtension("test.old",".new")
You probably want something like the -replace operator:
'filename.number.extension' -replace 'extension$','otherextension'
The $ is regular expression syntax meaning end of line. This should ensure that the -replace does not match "extension" appearing elsewhere in the filename.
A simple Utility Function
<#
# Renames all files under the given path (recursively) whose extension matches $OldExtension.
# Changes the extension to $NewExtension
#>
function ChangeFileExtensions([string] $Path, [string] $OldExtension, [string] $NewExtension) {
Get-ChildItem -Path $Path -Filter "*.$OldExtension" -Recurse | ForEach-Object {
$Destination = Join-Path -Path $_.Directory.FullName -ChildPath $_.Name.Replace($OldExtension, $NewExtension)
Move-Item -Path $_.FullName -Destination $Destination -Force
}
}
Usage
ChangeFileExtensions -Path "c:\myfolder\mysubfolder" -OldExtension "extension" -NewExtension "otherextension"
But it can do more than just this. If you had the following files in the same folder as your script
example.sample.csv
example.txt
mysubfolder/
myfile.sample.csv
myfile.txt
this script would rename all the .sample.csv files to .txt files in the given folder and all subfolders and overwrite any existing files with those names.
# Replaces all .sample.csv files with .txt extensions in c:\myfolder and in c:\myfolder\mysubfolder
ChangeFileExtensions -Path "c:\myfolder" -OldExtension "sample.csv" -NewExtension "txt"
If you don't want it to be recursive (affecting subfolders) just change
"*.$OldExtension" -Recurse | ForEach-Object
to
"*.$OldExtension" | ForEach-Object
This could work:
Get-ChildItem 'C:\Users\Administrator\Downloads\text files\more text\*' *.txt | rename-item -newname { [io.path]::ChangeExtension($_.name, "doc") }
You can remove the last item with the the [0..-1] slice and add the new extension to that
(("filename.number.extension" -split "\.")[0..-1] -join '.') +".otherextension"