How to improve this Powershell script? - powershell

I wrote a powershell script that merges go pro video files if they have multiple files. It works when the videos are at the root drive i.e. C:, but otherwise not. The mergevideos.txt file is not created if I run the script from a different directory. Or the txt is created but it's empty. Not sure what's going on when run from a different directory.
So is there a way to fix these issues and refactor this code to make it better? Ideally I want the script to automatically look at the directory it's in or allows me to specify the directory it should work in so I can just call it from the same location but the videos can be anywhere.
$path = "C:/NewVideos/"
$oldvids = Get-ChildItem -Path $path *.mp4
foreach ($oldvid in $oldvids) {
$curpath = $oldvid.DirectoryName
$name = [System.IO.Path]::GetFileNameWithoutExtension($oldvid)
$ext = [System.IO.Path]::GetExtension($oldvid)
if ($name.StartsWith("GX01") -and !($name.EndsWith("_merged")))
{
$newvid = $curpath + $name + "_merged" + $ext
if ([System.IO.File]::Exists($newvid))
{
Write-Output "$name | ALREADY MERGED"
continue
}
$count = 1
for ($num = 2; $num -lt 10; $num++)
{
$nextpart = $name.Replace("GX01", "GX0" + $num)
if ([System.IO.File]::Exists($curpath + "/" + $nextpart + $ext))
{
$count = $num
}
}
if ($count -eq 1)
{
Write-Output "$name | SINGLE VIDEO"
continue
}
$mergefile = $curpath + "mergevideos.txt"
if (!(Test-Path $mergefile))
{
New-Item -path $curpath -name mergevideos.txt -type "file"
}
Clear-Content $mergefile
for ($num = 1; $num -le $count; $num++)
{
$nextpart = $name.Replace("GX01", "GX0" + $num)
$videofilename = "file '" + $nextpart + $ext + "'"
Add-Content $mergefile $videofilename
}
Write-Output "$name | MERGING $count VIDEOS"
ffmpeg -f concat -safe 0 -i $mergefile -c copy $newvid
}
}

Related

List every sub folder in reverse

I have a UNC path
$path = "\\ad.testxyz.com\corp\technology\software\data\iso"
I need to apply permissions to the following paths:
\\ad.testxyz.com\corp\technology\software\data
\\ad.testxyz.com\corp\technology\software
\\ad.testxyz.com\corp\technology
And I don't want to do anything to \\ad.testxyz.com\corp\technology, or higher.
Here is my code so far. This "works", but I've run into a snag with directories that have spaces in them.
$path = "\\ad.domain.com\corp\technology\test\information\software"
#$path = "\\ad.domain.com\corp\technology\payer information\extracts\item load"
# Split string by "\", remove empty elements
$root = (($path.Split("\")).Split('',[System.StringSplitOptions]::RemoveEmptyEntries))
# Recursively list all top level folders in [path]. (Excludes DFS root)
for ($i=2; $i -lt $root.Length - 1; $i++) {
# Recursively build every directory up the tree
$leaf = "\" + $root[$i]
$branch += $leaf
$trunk = "\\" + $root[0] + "\" + $root[1] + $branch
# do work
Write-Host $trunk
}
Outputs:
\\ad.domain.com\corp\technology
\\ad.domain.com\corp\technology\test
\\ad.domain.com\corp\technology\test\information
But if I try and use a path with spaces in it, I get the following
\\ad.domain.com\corp\technology
\\ad.domain.com\corp\technology\payer
\\ad.domain.com\corp\technology\payer\information
\\ad.domain.com\corp\technology\payer\information\extracts
\\ad.domain.com\corp\technology\payer\information\extracts\item
Figured it out. This works for anyone else.
$path = "\\ad.domain.com\corp\technology\test\payer information\software"
# Split string by "\", remove null elements
$tree = $path.Split([system.io.path]::DirectorySeparatorChar) | ? {$_}
# Recursively list all top level folders in [path]. (Excludes DFS root)
For ($i=2; $i -lt $tree.Length - 1; $i++) {
# Recursively build every directory up the tree
$leaf = "\" + $tree[$i]
$branch += $leaf
$trunk = "\\" + $tree[0] + "\" + $tree[1] + $branch
# do work
Write-Host $trunk
}
Outputs:
\\ad.domain.com\corp\technology
\\ad.domain.com\corp\technology\test
\\ad.domain.com\corp\technology\test\payer information
there are builtin ways to handle path manipulation. [grin]
the simplest would be to use the .Parent property of the DirInfo object you get back from Get-ChildItem or from Get-Item when run against a filesystem. that likely cannot be used since it seems you only have strings to work with.
the next easiest is to use Split-Path and the -Parent switch to jump up one level at a time. that is what the below code does.
$SamplePathList = #(
'\\ad.domain.com\corp\technology\test\information\software'
'\\ad.domain.com\corp\technology\payer information\extracts\item load'
)
$StopAt = '\\ad.domain.com\corp\technology'
$Results = foreach ($SPL_Item in $SamplePathList)
{
$TempPath = $SPL_Item
# send starting path to $Results
$TempPath
while ($TempPath -ne $StopAt)
{
$TempPath = Split-Path -Path $TempPath -Parent
# send to $Results
$TempPath
}
}
$Results
output ...
\\ad.domain.com\corp\technology\test\information\software
\\ad.domain.com\corp\technology\test\information
\\ad.domain.com\corp\technology\test
\\ad.domain.com\corp\technology
\\ad.domain.com\corp\technology\payer information\extracts\item load
\\ad.domain.com\corp\technology\payer information\extracts
\\ad.domain.com\corp\technology\payer information
\\ad.domain.com\corp\technology

Identifying best photos from a photo collection

I borrowed code from this website and it looks helpful to me as a photographer. Since I do not know PowerShell I would like to ask if this code looks OK. I do not want to lose my photos by mistake.
$source_to_use_as_reference = "C:\photos\mytrip_to_hawai\Best\"
$destination_to_copy = "C:\photos\mytrip_to_hawai\Best\Best_CR2\"
$location_to_find_CR2_files = "C:\photos\mytrip_to_hawai\CR2\"
# these are the codes to find CR2 files that matches with JPG files and copy
# them to new destination
cls
$count_all = 0
$count_matches = 0
$a = Get-ChildItem -Path $source_to_use_as_reference -Recurse -File
foreach ($item in $a) {
$count_all += 1
if ($item.Name -match "JPG") {
$name_as_CR2 = $item.Name.Replace('JPG','CR2')
$location_and_cr2_name = $location_to_find_CR2_files + $name_as_CR2
if (Test-Path -Path $location_and_cr2_name ) {
$destination_and_CR2_name = $destination_to_copy + $name_as_CR2
if (Test-Path -Path $destination_and_CR2_name) {
Write-Output "already exists I skipped ... " $destination_and_CR2_name
} else {
$count_matches += 1
Write-Host "I found it " $destination_and_CR2_name
Copy-Item -Path $location_and_cr2_name -Destination $destination_to_copy
}
} else {
}
}
}
Write-Output "$count_matches matches found and files copied to destination $destination_to_copy"
There's nothing sinister in that script, it simply identifies, counts and copies JPGs and CR2 files to a 2nd location.

Powershell adding file sizes, unexpected result

I am currently working on a powershell script to copy a random selection of songs from my NAS onto an SD card. As an added complication, I can have no more than 512 songs per folder and also obviously, need to stop the process before I run out of free space on the card.
I have written a nearly complete script (with reduced amounts of songs for testing purposes), but am struggling with keeping track of the total size of the files I have copied. As an example, a test run with a total of 112MB of files gives a recorded value (in $copied_size) of 1245. I don't know what that value means, it doesn't seem to be a realistic value of GB, Gb, MB or Mb. I am obviously missing something here. Any ideas?
Here is the script, I haven't put in the size restriction yet:
$j = 1
$i = 0
$files_per_folder = 5
$sd_card_size = 15920000000
$copied_size = 0
$my_path = '\\WDMYCLOUD\Public\Shared Music'
$random = Get-Random -Count 100 -InputObject (1..200)
For ($j=1; $j -le 5; $j++)
{
md ("F:\" + $j)
$list = Get-ChildItem -Path $my_path | ?{$_.PSIsContainer -eq $false -and $_.Extension -eq '.mp3'}
For ($i=0; $i -le $files_per_folder - 1; $i++)
{
Copy-Item -Path ($my_path + "\" + $list[$random[(($J - 1) * $files_per_folder) +$i]]) -Destination ('F:\' + $j)
$copied_size = $copied_size + ($my_path + "\" + $list[$random[(($J - 1) * $files_per_folder) +$i]]).length
}
}
Write-Host "Copied Size = " $copied_size
Here's a way to solve your problem using some more powershell-like patterns. It compares the current file to be copied with space remaining and will exit out of the top-level loop if that condition is true.
#requires -Version 3
$path = '\\share\Public\Shared Music'
$filesPerFolder = 5
$copySize = 0
$random = 1..200 | Get-Random -Count 100
$files = Get-ChildItem -Path $path -File -Filter *.mp3
:main
for ($i = 1; $i -le 5; $i++) {
$dest = New-Item -Path F:\$i -ItemType Directory -Force
for ($j = 0; $j -le $filesPerFolder; $j++) {
$file = $files[$random[(($j - 1) * $filesPerFolder) + $i]]
if ((Get-PSDrive -Name F).Free -lt $file.Length) {
break main
}
$file | Copy-Item -Destination $dest\
$copySize += $file.Length
}
}

Cannot figure out why part of my copy commands are working and some are not

Code is at the bottom. Simply put, when I run the code, my .ps1 files get moved no problem, but for some reason any file with "Lec" as a part of its name will pop up this error message:
cp : Cannot find path 'C:\Users\Administrator\Documents\Win213x_Lec_filename.docx' because it does not
exist.
I do not understand why this is happening when it recognizes the file name, and I double checked the exact file is in the directory with the exact name, but my ps1 files have no issue.
$sub1 = "Lectures"
$sub2 = "Labs"
$sub3 = "Assignment"
$sub4 = "Scripts"
$DirectoryName = "test"
$Win213CopyFiles = ls C:\Users\Administrator\Documents\Win213Copy
$count = 0
foreach ($i in $Win213CopyFiles)
{
if ($i -match ".*Lec.*")
{
cp $i C:\Users\Administrator\Documents\test\Lectures
$count = $count + 1
}
elseif ($i -match ".*Lab.*")
{
cp $i C:\Users\Administrator\Documents\$DirectoryName\$sub2
$count = $count + 1
}
elseif ($i -match ".*Assign.*")
{
cp $i C:\Users\Administrator\Documents\$DirectoryName\$sub3
$count = $count + 1
}
elseif ($i -match ".*.ps1")
{
cp $i C:\Users\Administrator\Documents\$DirectoryName\$sub4
$count = $count + 1
}
Write-host "$i"
}
## Step 9: Display a message "<$count> files moved"
###################################
Write-host "$count files moved"
Copy-Item expects String as input. So it calls the ToString() method of the FileInfo object $i. That returns only the file name, not full path. As source directory is not specified the current working directory is used. Solution is to use full path found in fullname property:
cp $i.fullname C:\Users\Administrator\Documents\test\Lectures
From pipeline Copy-Item can handle FileInfo objects correctly, using the fullname property. Which you should remember not to drop if using Select-Object.

Powershell incorrect parameter

So I am trying to pass the file directory to the function convert at the bottom. When I run the script I receive the output:
Succes
C:\test 2\00000027627-00001\PROCESSING CHECKLIST
convert : Invalid Parameter - 2\00000027627-00001\PROCESSING
At C:\Users\pmanca\Desktop\msbxmlreader.ps1:35 char:13
+ convert([string]$hostdirectoryPlusLoanPlusDocType)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Invalid Paramet...0001\PROCESSING:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
It appears to be cutting off the file path after C:\test. Could the file path be too long? even so i wouldn't imagine getting the error at the function call and instead somewhere in the function when it couldn't resolve the path.
#? is a shortcut for where object and % is a shortcut for foreach-object
cls
#count 1 = Loan Number
#count 4 = Cust Num
#count 5 = Document Type
$hostdirectory = "C:\test 2"
$count = 0
$file = (Select-xml -Path "$hostdirectory\index.xml" -XPath / ).Node
$test = $file.ExportedResult.Docs.ExportedDoc.Doc.UdiValues.UdiValue.Value# | Select-Object {$_.UdiValue.Value.InnerXML} -Unique #|? {$_.UdiValue.Name -eq "Loan Number"}
$test | ForEach-Object{
$count++
# Write-Host $_.innerxml "----" $count
if($count -eq 1){
[string]$xmlHold = $_.InnerXML
$hostdirectoryPlusLoan = "$hostdirectory\$xmlHold"
if(!(test-path $hostdirectoryPlusLoan)){
New-Item $hostdirectoryPlusLoan -ItemType directory
}
}
if($count -eq 5){
[string]$xmlHold = $_.InnerXML
$hostdirectoryPlusLoanPlusDocType = "$hostdirectoryPlusLoan\$xmlHold"
if(!(test-path $hostdirectoryPlusLoanPlusDocType)){
New-Item $hostdirectoryPlusLoanPlusDocType -ItemType directory
}
if(Test-Path "$hostdirectory\$xmlhold.pdf"){
$check = Copy-Item "$hostdirectory\$xmlHold.pdf" -Destination $hostdirectoryPlusLoanPlusDocType -ErrorAction SilentlyContinue
if(-not $?) {write-warning "Copy Failed"; Write-Host $Error[0].exception.message}
else {write-host "Succes"}
Write-Host $hostdirectoryPlusLoanPlusDocType
convert([string]$hostdirectoryPlusLoanPlusDocType)
}
}
if($count -ge 8){
$count = 0
# Write-Host "-----------------------------------"
}
}
function convert([string]$inputDirectory){
write-host $inputDirectory
#Variable to hold current input directory
$InputPathFilter = $InputDirectory + '\*.pdf'
#Variable to hold the list of PDFs from the current input directory
$PDFList = #(gci $InputPathFilter | foreach {write-output $_.name})
#Loop through list of PDF files to convert to TIF image files.
for ($j=0; $j -lt $PDFList.count; $j++) {
#Each PDF will go into its own directory/batch
#Create a variable with only the file name
$FileName = $PDFList[$j] -replace(".pdf",'')
#Variable of the full path to the current PDF
$InputPath = $InputDirectory + '\' + $PDFList[$j]
#Variable to hold output path of each TIF file. Filename format is that used by batches in ScerIS (i.e. 00010001.tif, 00010002.tif, etc...)
$OutputFullDirectory = $inputlDirectory + '\' + $FileName + "_" + "{0:D4}" -f + '1' + '%04d.tif'
#Calls Ghostscript command line executable to process each PDF file into a group of single page TIF image files
&'C:\Program Files\gs\gs9.14\bin\gswin64c.exe' -sDEVICE=tiffg4 -dBATCH -dNOPAUSE -q -r600 "-sOutputFile=$OutputFullDirectory" "$InputPath"
#Increment the counter for the loop
$DocCounter = $j + 1
#Rename the current pdf so that it isn't processed again.
$RenamePath = $PdfList[$j] -replace("pdf", "pd_")
Rename-Item $InputPath $RenamePath -Force
}
}
First : In PowerShell, when you call a function you must not use parenthesis :
convert $hostdirectoryPlusLoanPlusDocType
or as suggested in comment
convert -inputDirectory $hostdirectoryPlusLoanPlusDocType
but not :
convert([string]$hostdirectoryPlusLoanPlusDocType)
Second : Your function should be declarated first and use after :
function toto ($var)
{
Write-Host $var
}
toto "voila"
and not
toto "voila"
function toto ($var)
{
Write-Host $var
}