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
}
}
I'm trying to receive Verbose messages of every sorting step and run my script with -Verbose key, but I don't receive any verbose message from the conditionals (loops). I receive my messages only if I call them outside the conditionals. Please help
[CmdletBinding()]
param()
set-location "\test folder"
$listOfItems = Get-ChildItem | Select-Object -Property Name, Mode, LastWriteTime, #{label = "FileSize (MB)";expression = {$_.Length/1024/1024}}
$bufferItem = 0
for ($i = $listOfItems.Length - 1; $i -eq 0; $i--) {
for ($j = 0; $i -lt $i; $j++) {
if ($listOfItems[$j]."FileSize (MB)" -gt $listOfItems[$j + 1]."FileSize (MB)") {
Write-Verbose -Message "Transfer the value of the buffer variable to the element below the list"
continue
}
elseif ($listOfItems[$j]."FileSize (MB)" -lt $listOfItems[$j + 1]."FileSize (MB)") {
$bufferItem = $listOfItems[$j]
$listOfItems[$j] = $listOfItems[$j + 1]
$listOfItems[$j + 1] = $bufferItem
}
Write-Verbose -Message "Transfer the value of the buffer variable to the element below the list"
}
}
There's a bug in your inner loop condition:
for ($j = 0; $i -lt $i; $j++) {
Since $i -lt $i will never evaluate to $true, the inner loop body won't ever execute, and no Write-Verbose statement is ever reached.
Fix the loop condition and you'll find Write-Verbose works perfectly fine in conditional statements.
I have this here for loop:
$fromInput = 1
$toInput = 99
for ($i = $fromInput; $i -le $toInput; $i++) {
$destinationDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f #($i)
}
So it would loop through PC's from 1 to 99 but what I need now is to loop through a list of numbers that User inputs that are split
I am trying to do that with a foreach loop but it is not working for me like the one in the for loop:
$userInput = Read-Host "Input numbers divided by a comma [", "]"
$numberList = $userInput.split(", ")
foreach ($i in $numberList) {
$destinationDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f #($i) + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f #($i)
}
How do I make a foreach loop that takes the $userInput, splits it into $numberList and then loops for each of the numbers in the $numberList the way it is shown above.
I much appreciate your help as always!
The main issue is that you are applying formatting (d5) to a string that is intended for an integer type. You can simply cast to [int] to get the desired result.
foreach ($i in $numberList) {
$destinationDir = '\\pc' + '{0:d5}' -f [int]$i + "\$shareName\$dupDir"
$netUseDir = '\\pc' + '{0:d5}' -f [int]$i + "\$shareName"
$passObj = 'pass#' + '{0:d3}' -f [int]$i
}
Read-Host reads data as a [string]. If that data needs to be a different type for whatever reason, a conversion will be needed whether that be implicit or explicit.
First, for user input, I recommend you use something like this:
$userInput = Read-Host "Input numbers divided by a comma [", "]"
try
{
[int[]]$numberList = $userInput.split(',')
}
catch
{
'Input only numbers separated by commas.'
break
}
To explain why [int[]] is there and why a try {...} catch {...} statement:
We are attempting to convert a string into an array and convert the resulting elements into int. As a result we should be getting an array of integers, if this was not the case, meaning if the user inputs something different than numbers separated by commas, we would get an error which is captured and handled by the catch block.
In this case, converting the strings to integers is needed by showing you a simple example:
PS /> '{0:d5}' -f '1' # String formatting on a string
1
PS /> '{0:d5}' -f 1 # String formatting on an integer
00001
Now for looping, here are the 3 easy alternatives I see:
With for loop:
for($i=$numberList[0];$i -le $numberList.Count;$i++)
{
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $i
$netUseDir = "\\pc{0:d5}\$shareName" -f $i
$passObj = 'pass#{0:d3}' -f $i
}
With foreach loop:
foreach($i in $numberList)
{
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $i
$netUseDir = "\\pc{0:d5}\$shareName" -f $i
$passObj = 'pass#{0:d3}' -f $i
}
With ForEach-Object loop:
$numberList | ForEach-Object {
$destinationDir = "\\pc{0:d5}\$shareName\$dupDir" -f $_
$netUseDir = "\\pc{0:d5}\$shareName" -f $_
$passObj = 'pass#{0:d3}' -f $_
}
Below is my script and I want to pass the value if not null to my $weekDays variable in comma separated format but I want to remove the last comma, so please help me on this.
$a = "sun"
$b = "mon"
$c = $null
$d = $a,$b,$c
$weekDays = $null
Foreach ($i in $d)
{
if ($i)
{
$weekDays = $i
$weekDays = $weekDays + ","
Write-Host "$weekDays"
}
}
Output: sun,mon,
I want: sun, mon
No need to loop through the list yourself since there's already the -join operator for that purpose, but you need to remove the null elements first
($d | Where-Object { $_ -ne $null }) -join ", "
Where-Object (alias where) will filter out the null elements.
If you just want to exclude the last null item then use this
$d[0..($d.Length - 2)] -join ", "
Note that your code produces the below output
sun,
mon,
and not sun,mon, in the same line. To print with new lines like that you need to use
($d | where { $_ -ne $null }) -join ",`n"
$a = "sun"
$b = "mon"
$c = $null
$d = $a,$b,$c
$weekDays = $null
Foreach ($i in $d)
{
if ($i)
{
if (-not ([string]::IsNullOrEmpty($weekDays)))
{
$weekDays += ","
}
$weekDays = $weekDays + $i
}
}
Write-Host "$weekDays"
##Output : sun,mon, I want : sun, mon
Your variable is null at start and you want to prepend a comma in all subsequent cases, that is, when the variable is no longer null.
I was asking myself how easily you could convert an Array of Numbers Like = 1,2,3,6,7,8,9,12,13,15 into 1 String that "Minimizes" the numbers, so Like = "1-3,6-9,12-13,15".
I am probably overthinking it because right now I don't know how I could achieve this easily.
My Attempt:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$before
Foreach($num in $array){
If(($num-1) -eq $before){
# Here Im probably overthinking it because I don't know how I should continue
}else{
$before = $num
$newArray += $num
}
}
This should working, Code is self explaining, hopefully:
$array = #( 1,2,3,6,7,8,9,12,13,15 )
$result = "$($array[0])"
$last = $array[0]
for( $i = 1; $i -lt $array.Length; $i++ ) {
$current = $array[$i]
if( $current -eq $last + 1 ) {
if( !$result.EndsWith('-') ) {
$result += '-'
}
}
elseif( $result.EndsWith('-') ) {
$result += "$last,$current"
}
else {
$result += ",$current"
}
$last = $current
}
if( $result.EndsWith('-') ) {
$result += "$last"
}
$result = $result.Trim(',')
$result = '"' + $result.Replace(',', '","') +'"'
$result
I have a slightly different approach, but was a little too slow to answer. Here it is:
$newArray = ""
$array = 1,2,3,6,7,8,9,12,13,15
$i = 0
while($i -lt $array.Length)
{
$first = $array[$i]
$last = $array[$i]
# while the next number is the successor increment last
while ($array[$i]+1 -eq $array[$i+1] -and ($i -lt $array.Length))
{
$last = $array[++$i]
}
# if only one in the interval, output that
if ($first -eq $last)
{
$newArray += $first
}
else
{
# else output first and last
$newArray += "$first-$last"
}
# don't add the final comma
if ($i -ne $array.Length-1)
{
$newArray += ","
}
$i++
}
$newArray
Here is another approach to the problem. Firstly, you can group elements by index into a hashtable, using index - element as the key. Secondly, you need to sort the dictionary by key then collect the range strings split by "-" in an array. Finally, you can simply join this array by "," and output the result.
$array = 1, 2, 3, 6, 7, 8, 9, 12, 13, 15
$ranges = #{ }
for ($i = 0; $i -lt $array.Length; $i++) {
$key = $i - $array[$i]
if (-not ($ranges.ContainsKey($key))) {
$ranges[$key] = #()
}
$ranges[$key] += $array[$i]
}
$sequences = #()
$ranges.GetEnumerator() | Sort-Object -Property Key -Descending | ForEach-Object {
$sequence = $_.Value
$start = $sequence[0]
if ($sequence.Length -gt 1) {
$end = $sequence[-1]
$sequences += "$start-$end"
}
else {
$sequences += $start
}
}
Write-Output ($sequences -join ",")
Output:
1-3,6-9,12-13,15