1

I'm trying to download videos with a script into a predefined directory:

$save_dir = 'video'
$list_dir = "-o N:/$save_dir/%(upload_date)s_%(title)s_%(id)s.%(ext)s"
ForEach ($item in $list_dir) {
  Write-Host $item
  & yt-dlp $item `
  --cache-dir "$PSScriptRoot/bin/cache" `
  $(Get-Clipboard)
}

$list_dir being a single element array is a necessity (part of a larger script), hence the ForEach loop. Terminal output (note that you need to copy the video URL into clipboard first):

-o N:/video/%(upload_date)s_%(title)s_%(id)s.%(ext)s
[youtube] YbJOTdZBX1g: Downloading webpage
[info] YbJOTdZBX1g: Downloading 1 format(s): 248+251
[download] Destination:  N#\video\20181206_YouTube Rewind 2018 - Everyone Controls Rewind _ #YouTubeRewind_YbJOTdZBX1g.f248.webm

This ends up creating a directory called N# inside the directory the terminal is run from. Why is it replacing : after the drive letter with a #? It clearly knows it's a : based on the Write-Host ouput.

gargoylebident
  • 373
  • 1
  • 2
  • 12

1 Answers1

2

Why is it replacing : after the drive letter with a #?

It isn't PowerShell that is performing this substitution; by process of elimination, it must be yt-dlb

"-o N:/$save_dir/%(upload_date)s_%(title)s_%(id)s.%(ext)s"

You cannot pass both an option and its argument as a single string - PowerShell will pass it as a single, double-quoted argument (double-quoted due to the presence of at least one space).

The equivalent of passing & yt-dlp -o N:/$save_dir/%(upload_date)s_%(title)s_%(id)s.%(ext)s via a variable is to use an array variable, with the option name and its argument passed as separate elements:

$arr = '-o', "N:/$save_dir/%(upload_date)s_%(title)s_%(id)s.%(ext)s"
& yt-dlp $arr --cache-dir $PSScriptRoot/bin/cache (Get-Clipboard)

Note: The above uses a single array for brevity. To define multiple arrays hosted in a single array you can easily define a jagged array (an array whose elements are arrays too); using the example of a jagged array containing just one nested array (option+argument) pair, constructed via the unary form of ,, the array constructor operator:

$list_dir = , ('-o', "N:/$save_dir/%(upload_date)s_%(title)s_%(id)s.%(ext)s")
ForEach ($item in $list_dir) {
  & yt-dlp $item --cache-dir $PSScriptRoot/bin/cache (Get-Clipboard)
}

For multi-element jagged arrays, place , between the sub-arrays
($list_dir = ('-o', 'foo'), ('-o', 'bar')).

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • But then I can't use `$list_dir` as a single-element array. I tried putting `$arr` into `$list_dir` (to make a "2d array") but that does nothing. – gargoylebident Jun 13 '21 at 02:58
  • @stackexchange_account1111: You can use a jagged array: `$list_dir = ('-o', 'foo'), ('-o', 'bar')` – mklement0 Jun 13 '21 at 03:04
  • Can I have `(('-o', 'foo'))`? i.e. a single element array whose only element consists of two elements? Doesn't seem to be working – gargoylebident Jun 13 '21 at 03:09
  • @stackexchange_account1111: For that, you need to use the unary form of `,`, the [array constructor operator](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operators#comma-operator-): `, ('-o', 'foo')` – mklement0 Jun 13 '21 at 03:12
  • Any reason this doesn't work if `$list_dir` is assigned via a [ternary operator](https://stackoverflow.com/a/34311158/9289184)? E.g. `$list_dir = if ($true) { , ('-o', "N:/$save_dir_1/%(upload_date)s_%(title)s_%(id)s.%(ext)s") }` – gargoylebident Jun 14 '21 at 04:42
  • I'll accept this still, because it answers my question the way it was posed, but I do wonder why using a ternary operator breaks things. By breaks I mean `Write-Host` prints `-o` and `N:/...` on separate lines (undesirable) while just assigning prints both as one line (i.e. as a single element which is desirable). – gargoylebident Jun 14 '21 at 04:45
  • 1
    @stackexchange_account1111, your command doesn't use the PSv7+ [ternary operator](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Operators#ternary-operator--if-true--if-false), it uses an `if` statement as an expression. What the `{ ... }` block outputs is output to the _pipeline_ and therefore subject to _enumeration_, causing your wrapper array to be lost. To output a nested array, you need another wrapper array (note the additional `,`): `$list_dir = if ($true) { ,, ('-o', "N:/$save_dir_1/%(upload_date)s_%(title)s_%(id)s.%(ext)s") }` – mklement0 Jun 14 '21 at 05:28