Using powershell archive individual files with winrar command line - powershell

I am struggling to archive file using PowerShell command with winrar
Folder has some text files N:\Download: abc.txt, xyz.txt
i would like to get output: N:\Download: abc.rar, xyz.rar and delete the text file after archive.
and how can I set the compressed level maximum?
https://documentation.help/WinRAR/HELPSwM.htm
here is my sample script
$Source = get-ChildItem -path N:\Download -filter "*.txt"
$Rarfile = "C:\Program Files\WinRAR\WinRAR.exe"
$Dest = "N:\Download"
&$WinRar a $Source.Fullname $Dest

From the documentation,
To delete the files after archiving, use the -df switch
To set the compression level use the -m<n> switch
Note, you have defined the full path to the winrar.exe file in variable $Rarfile, but later on you use undefined variable $WinRar..
If you want to create a .rar file for each source file separately, you will need a loop to be able to construct the output .rar filename
Try
$WinRar = "C:\Program Files\WinRAR\WinRAR.exe"
$Dest = "N:\Download"
(Get-ChildItem -Path 'N:\Download' -Filter '*.txt') | ForEach-Object {
$outFile = Join-Path -Path $Dest -ChildPath ('{0}.rar' -f $_.BaseName)
& $WinRar a -m4 -df $outFile $($_.Fullname)
# or use
# & $WinRar m -m4 $outFile $($_.Fullname)
}
If you do not want WinRar to include the folder structure of the files, append the -ep switch to the command

Related

Powershell: Find Folders with (Name) and Foreach Copy to Location Preserve Directory Structure

Got another multi-step process I'm looking to streamline. Basically, I'm looking to build a Powershell script to do three things:
Get-Childitem to look for folders with a specific name (we'll call it NAME1 as a placeholder)
For each folder it finds that has the name, I want it to output the full directory to a TXT file (so that in the end I wind up with a text file that has a list of the results it found, with their full paths; so if it finds folders with "NAME1" in five different subdirectories of the folder I give it, I want the full path beginning with the drive letter and ending with "NAME1")
Then I want it to take the list from the TXT file, and copy each file path to another drive and preserve directory structure
So basically, if it searches and finds this:
D:\TEST1\NAME1
D:\TEST7\NAME1
D:\TEST8\NAME1\
That's what I want to appear in the text file.
Then what I want it to do is to go through each line in the text file and plug the value into a Copy-Item (I'm thinking the source directory would get assigned to a variable), so that when it's all said and done, on the second drive I wind up with this:
E:\BACKUP\TEST1\NAME1
E:\BACKUP\TEST7\NAME1
E:\BACKUP\TEST8\NAME1\
So in short, I'm looking for a Get-Childitem that can define a series of paths, which Copy-Item can then use to back them up elsewhere.
I already have one way to do this, but the problem is it seems to copy everything every time, and since one of these drives is an SSD I only want to copy what's new/changed each time (not to mention that would save time when I need to run a backup):
$source = "C:\"
$target = "E:\BACKUP\"
$search = "NAME1"
$source_regex = [regex]::escape($source)
(gci $source -recurse | where {-not ($_.psiscontainer)} | select -expand fullname) -match "\\$search\\" |
foreach {
$file_dest = ($_ | split-path -parent) -replace $source_regex,$target
if (-not (test-path $file_dest)){mkdir $file_dest}
copy-item $_ -Destination $file_dest -force -verbose
}
If there's a way to do this that wouldn't require writing out a TXT file each time I'd be all for that, but I don't know a way to do this the way I'm looking for except a Copy-Item.
I'd be very grateful for any help I can get with this. Thanks all!
If I understand correctly, you want to copy all folders with a certain name, keeping the original folder structure in the destination path and copy only files that are newer than what is in the destination already.
Try
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest  /XO /S /R:0
}
If you don't want console output of robocopy you can silence it by appending 2>&1, so neither stdout nor stderr is echoed
If you want to keep a file after this with both the source paths and the destinations, I'd suggest doing
$source = 'C:\'
$target = 'E:\BACKUP\'
$search = 'NAME1'
$output = [System.Collections.Generic.List[object]]::new()
# -ErrorAction SilentlyContinue because in the C:\ disk you are bound to get Access Denied on some paths
Get-ChildItem -Path $source -Directory -Recurse -Filter $search -ErrorAction SilentlyContinue | ForEach-Object {
# construct the destination folder path
$dest = Join-Path -Path $target -ChildPath $_.FullName.Substring($source.Length)
# add an object to the output list
$output.Add([PsCustomObject]#{Source = $_.FullName; Destination = $dest })
# copy the folder including its files and subfolders (but not empty subfolders)
# for more switches see https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy
robocopy $_.FullName $dest  /XO /S /R:0
}
# write the output to csv file
$output | Export-Csv -Path 'E:\backup.csv' -NoTypeInformation

Copy-Item with overwrite?

Here is a section of code from a larger script. The goal is to recurse through a source directory, then copy all the files it finds into a destination directory, sorted into subdirectories by file extension. It works great the first time I run it. If I run it again, instead of overwriting existing files, it fails with this error on each file that already exists in the destination:
Copy-Item : Cannot overwrite the item with itself
I try, whenever possible, to write scripts that are idempotent but I havn't been able to figure this one out. I would prefer not to add a timestamp to the destination file's name; I'd hate to end up with thirty versions of the exact same file. Is there a way to do this without extra logic to check for a file's existance and delete it if it's already there?
## Parameters for source and destination directories.
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
# Build list of files to sort.
$Files = Get-ChildItem -Path $Source -Recurse | Where-Object { !$_.PSIsContainer }
# Copy the files in the list to destination folder, sorted in subfolders by extension.
foreach ($File in $Files) {
$Extension = $File.Extension.Replace(".","")
$ExtDestDir = "$Destination\$Extension"
# Check to see if the folder exists, if not create it
$Exists = Test-Path $ExtDestDir
if (!$Exists) {
# Create the directory because it doesn't exist
New-Item -Path $ExtDestDir -ItemType "Directory" | Out-Null
}
# Copy the file
Write-Host "Copying $File to $ExtDestDir"
Copy-Item -Path $File.FullName -Destination $ExtDestDir -Force
}
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
You are trying to copy files from a source directory to a sub directory of that source directory. The first time it works because that directory is empty. The second time it doesn't because you are enumerating files of that sub directory too and thus attempt to copy files over themselves.
If you really need to copy the files into a sub directory of the source directory, you have to exclude the destination directory from enumeration like this:
$Files = Get-ChildItem -Path $Source -Directory |
Where-Object { $_.FullName -ne $Destination } |
Get-ChildItem -File -Recurse
Using a second Get-ChildItem call at the beginning, which only enumerates first-level directories, is much faster than filtering the output of the Get-ChildItem -Recurse call, which would needlessly process each file of the destination directory.

How to rename large number of files using Powershell and a CSV

Ultimately, I need a solid PowerShell script that will take a folder with several hundred video files, import the existing file names into the program, lookup the new file name in a CSV, and rename it. The old filename is simply (ie. File1.mp4, File2.mp4, etc.) I would like to appended a date to the front of the file in the format of (YYYY-MM-DD).
For testing, I created a folder on my desktop with (10) text files, each with a unique file name.
My CSV file appears as follows:
Image of CSV
The "newfilename" column, was created by using the Concatenate command in Excel.
`(=CONCATENATE(TEXT(A2, "yyyy-mm-dd")," ", B2)`
As much as I would just like PowerShell to handle everything, I feel using Excel for most of this might be the best way.
In my testing, everything was in one folder. However, at work, I will have video files on one drive, and the script will have to be in a folder on my desktop. Because I am in a corporate network, I need a special batch file to run my scripts, which is nothing new. I just modify the script name, and away it goes!
So what commands do I need to do in order to have the script separate from the video files AND the CSV file?
Here is the code that I have so far. Everything works when it's in one folder.
PS C:\Users\ceran\Desktop\Rename Project> Import-Csv -Path .\MyFileList.csv | ForEach-Object {
>> $Src = Join-Path -Path $TargetDir -ChildPath $_.filename
>> $Dst = Join-Path -Path $TargetDir -ChildPath $_.newfilename
>> Rename-Item -Path $Src -NewName $Dst
>> }
Thanks in advance for the help!
Chris
I'm not sure what the date column is in your Excel file and if you want to rename all files in the folder, but if that is the case, you don't need a csv file at all and can do this:
$sourceFolder = 'X:\Path\to\the\video\files' # change this to the real path
Get-ChildItem -Path $sourceFolder -Filter '*.mp4' -File | # iterate through the files in the folder
Where-Object {$_.Name -notmatch '^\d{4}-\d{2}-\d{2}'} | # don't rename files that already start with the date
Rename-Item -NewName { '{0:yyyy-MM-dd} {1}' -f $_.LastWriteTime, $_.Name } -WhatIf
This uses parameter -Filter '*.mp4', to get only files with an .mp4 extension. For the files in your testfolder (Desktop\Rename Project), change this to -Filter '*.txt'.
If you want all files renamed, no matter what the extension, simply remove the Filter from the cmdlet.
Because of the -WhatIf switch, no file is actually renamed and the code just shows in the console what would happen. Once satisfied that this is OK, remove the -WhatIf
Hope that helps.
$targetdir="C:\path\to\where\our\file\directory\is"
$pathtocsv="c:\path\to\csv.csv"
Import-Csv -Path $pathtocsv | ForEach-Object {
$Src = Join-Path -Path $TargetDir -ChildPath $_.filename
$Dst = Join-Path -Path $TargetDir -ChildPath $_.newfilename
Rename-Item -Path $Src -NewName $Dst
}
Why would this not work in any situation?
By the way, if the csv had the columns path and newname, it could be piped directly to rename-item:
path,newname
file.txt,file2.txt
import-csv ren.csv | Rename-Item -whatif
What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/file.txt Destination: /Users/js/foo/file2.txt".

custom robocopy script in PowerShell

I would like to move all .txt files present in a source directory (including .txt present in subfolders) to a destination directory.
For eg:
Source directory: D:\OFFICE work\robtest\test1
Above directory also contains many subfolders.
Destination directory: D:\OFFICE work\robtest\test2
I would like to move only .txt files in source dir to the above mentioned destination directory, in such a way that only 3 .txt files must be present per sub folder (includes random folder creation) in the destination directory.
Below is the code I tried, but PowerShell says
The filename, directory name, or volume label syntax .(D:\OFFICE work\robtest\test1\testtest\testtest2\
New folder\test01112.txt\ ) is incorrect.
I am not sure why there is extra '/' after the above path in robocopy.
$fileperfolder = 3
$source = "D:\OFFICE work\robtest\test1";
$dest = "D:\OFFICE work\robtest\test2";
$folderName = [System.IO.Path]::GetRandomFileName();
$fileList = Get-ChildItem -Path $source -Filter *.txt -Recurse
Write-Host $fileList
$i = 0;
foreach ($file in $fileList) {
$destiny = $dest + "\" + $folderName
Write-Host $file.FullName;
Write-Host $destiny;
New-Item $destiny -Type Directory -Force
robocopy $file.FullName $destiny /mov
$i++;
if ($i -eq $fileperfolder) {
$folderName = [System.IO.Path]::GetRandomFileName();
$i = 0;
}
}
You're getting the error (and the "directory" in the output) because robocopy expects two folders as the first and second argument, optionally followed by a list of filename specs, but you're providing the path to a file as the source.
From the documentation (emphasis mine):
Syntax
robocopy <Source> <Destination> [<File>[ ...]] [<Options>]
Parameters
<Source> Specifies the path to the source directory.
<Destination> Specifies the path to the destination directory.
<File> Specifies the file or files to be copied. You can use wildcard characters (* or ?), if you want. If the File parameter is not specified, *.* is used as the default value.
<Options> Specifies options to be used with the robocopy command.
Since you're moving one file at a time and apparently don't want to maintain the directory structure I'd say robocopy isn't the right tool for what you're doing anyway.
Replace
robocopy $file.FullName $destiny /mov
with
Move-Item $file.FullName $destiny
and the code should do what you want.
robocopy $file.Directory $destiny $file /mov
solved this issue.

Powershell: processing files in two folders based on what's in them

I have two directories: c:\Rar and c:\unRared
Rar - contains hundreds of RAR'ed files. Only one file inside the RAR. File inside the archive is with *.TRN extension.
UnRared has unarchived files (hundreds of files with *.TRN extension)
I've been trying to create a Powershell script to extract only files that have not been extracted already.
can't you just supply parameters to whatever compression program you have telling it not to overwrite existing files?
Knocked this out on my own...well, not really, with the help of developer
$dir1='C:\temp\aaa'
$dir2='C:\temp\bbb'
$CmdPath = "C:\Program Files (x86)\WinRAR\RAR.exe"
$Files2Extract = get-childitem -path 'C:\temp\aaa' -recurse -name -filter *.rar
#$d2 = get-childitem -path $dir2 -recurse
foreach($file in $Files2Extract){
$justname = $dir2+'\\'+(split-path $file -leaf).split(".")[0]+'.trn'
if(!(Test-Path -path $justname)) {
&$CmdPath e $file $dir2
}
}