powershell 脚本从命令行运行正常,但从任务计划程序运行不正常

3htmauhk  于 2023-05-17  发布在  Shell
关注(0)|答案(1)|浏览(491)

我写了一个脚本,这样我就可以从播客播放器访问我新录制的媒体。脚本按预期从命令行运行,但从任务计划程序运行时,它无法从子文件夹获取完整路径。使用此错误路径的每次foreach传递都有多个错误。下面是一个这样错误

get-item : Cannot find path 'D:\Radio\202303100400.mp4' because it does not exist.
At C:\Users\jj9wo\mpxtorss.ps1:38 char:23
+ foreach ($folder in ((get-item $media).DirectoryName) | Select-Object ...
+                       ~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\Radio\202303100400.mp4:String) [Get-Item], ItemNotFoundException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand

文件的正确路径是D:\Radio\kmox\202303100400.mp4
这里是完整的脚本。有一对夫妇的事情,仍然需要工作,但它是完全运作,除了问题,我说。我知道我的脚本喜欢一行程序,缺乏足够的注解。我希望我的评论仅限于我的具体问题

$path= 'D:\Radio'
$site='mysite.com'
Set-Location -Path $path

if ($path -and $(Try{Test-Path $path.trim()} Catch{}) -and (((get-item $path).Attributes) -match "Directory")){
  $path = Get-Item $path
} else {Write-Host "mp2rss error: Invalid" ;exit}
$media = (Get-ChildItem -path $path -File -Recurse -Filter "*.mp*") | Where-Object `
{$_.extension -in ".mp3",".mp4"} | Sort-Object -Descending -Property LastWriteTime
if (!$media.Count){Write-Host "mp2rss error: No Media Found" ;exit}

# done verifying

function when_long($param){
  $param = ($param.AddMinutes(-($param.minute %5)).ToString("h:mmtt ddd, MMM d'th'") -replace
  '(?<!1)1th$','1st' -replace'(?<!1)2th$','2nd' -replace '(?<!1)3th$','3rd') -replace
  '(?<=:\d\d)AM','a' -replace '(?<=:\d\d)PM','p'
  return $param
}
function when_short($param){
  $param = ($param.ToString("ddd"),($param.AddMinutes(-($param.minute %5))).ToString("M/dd h:mmt").ToLower() -join " ")
  return $param
}

if ((Get-Volume -DriveLetter $path.ToString().Substring(0,1)).FileSystemLabel){
    $title_alt = (Get-Volume -DriveLetter $path.ToString().Substring(0,1)).FileSystemLabel
}else{$title_alt = "$env:COMPUTERNAME`($($path.FullName.Substring(0,1))`)"}

if ($path.FullName -eq $path.Root.FullName){
  $title = $title_alt
  $htm_out = "$($path)mpx.htm"
}else{
  $title = $path.BaseName
  $htm_out = "$path\mpx.htm"
}
$html = @("<html><head><title>$title</title></head><body>")

foreach ($folder in ((get-item $media).DirectoryName) | Select-Object -Unique){
  $ext = @('mp3','mp4')
  foreach ($ext in $ext){
    if ($media | Where-Object {$_.DirectoryName -in $folder -and $_.Extension -match $ext}){

      if ((Get-Item $folder).FullName -ne (Get-Item $folder).Root.FullName){
        $title = (Get-Item $folder).BaseName
      }else{$title = $title_alt}
      if ($ext -match 'mp4'){$title = "$title [V]"}
      $url = (([System.Uri]$folder).AbsoluteUri.TrimStart(([System.Uri]($path.ToString())).AbsoluteUri))
      if ($url){$url = "$site/$url/"}else{$url = "$site/"}
      $qty = (($media | Where-Object {$_.DirectoryName -in $folder -and $_.Extension -match $ext}) | Measure-Object).Count
      $whn = (when_long ($media | Where-Object {$_.DirectoryName -in $folder -and $_.Extension -match $ext} | Select-Object -First 1).CreationTime)

      $html += "<p><a href=`"http://$url$ext.rss`">$title</a><br>$qty files updated $whn</p>"
      $rss = @("<rss><channel><title>$title</title>")
      $rss += "<description>$qty files updated $whn</description>"

      foreach ($file in ($media | Where-Object {$_.DirectoryName -in $folder -and $_.Extension -match $ext})){
        if ($file.BaseName -match "^\d+$"){
          if ($file.BaseName -match "^\d{12}$"){
            $title = (when_short ([Datetime]::ParseExact($file.BaseName, 'yyyyMMddHHmm', $null)))
            $dsc = (when_long ([Datetime]::ParseExact($file.BaseName, 'yyyyMMddHHmm', $null)))
          }else{
            $title = (when_short $file.CreationTime)
            $dsc = (when_long $file.CreationTime)
          }
        }else{
          $title = $file.BaseName
          $dsc = (when_long $file.CreationTime)
        }
        $rss += "<item><title>$title</title>"
        $rss += "<description>$dsc</description>"
        $rss += "<enclosure url=`"http://$url$([uri]::EscapeDataString($file.Name))`"/></item>"
      }
      $rss += "</channel></rss>"
      $rss_out = "$folder\$ext.rss"
      if (-not (Test-Path $rss_out) -or (Compare-Object (Get-Content -Path $rss_out) $rss)){Out-File -InputObject $rss -FilePath $rss_out}
    }
  }
}
if (-not (Test-Path $htm_out) -or (Compare-Object (Get-Content -Path $htm_out) $html)){Out-File -InputObject $html -FilePath $htm_out}
hc2pp10m

hc2pp10m1#

tl;dr
**没有 * 需要使用Get-Item的参数已经 * 是 * [System.IO.FileInfo](或[System.IO.DirectoryInfo])示例,例如Get-ChildItem的输出。

  • (Get-Item $media).DirectoryName替换为$media.DirectoryName
    在为 Windows PowerShell 编写的代码和 * 跨版本 * 代码中,需要额外的努力才能将FileInfo/DirectoryInfo * 用作字符串
    针对接受文件路径的 * cmdlet通过pipeline*传递FileInfo示例 ,该pipeline**会将FileInfo/DirectoryInfo示例的 * 完整路径 * 稳健绑定到目标cmdlet的-LiteralPath参数上-背景信息请参见this answer;例如:
# The [FileInfo] instance robustly binds to 
# Select-String -LiteralPath by its full path.
$someFileInfo | Select-String 'foo'

针对 * 外部程序/其他需要字符串 * 表示FileInfo示例的 * 全路径 * 的上下文,显式使用其.FullName属性**;例如:

cmd /c echo The full paths is: $someFileInfo.FullName

请继续阅读以获得解释。
正如Mathias R. Jessen所指出的,将Get-Item$media一起使用是“不必要的”,因为$media已经包含了[System.IO.FileInfo]示例,因为它包含了早期Get-ChildItem-File调用的输出。
(Get-Item $media).DirectoryName替换为$media.DirectoryName不仅更简单、更高效,而且实际上 * 隐含地解决了您的问题
也就是说,Get-Item $media在Windows PowerShell
中 * 无法 * 按预期 * 工作:

  • 在这个调用中,FileInfo示例被 * 字符串化 *。(此部分也适用于PowerShell (Core) 7+)。
  • 与管道输入不同,当FileInfo示例作为 arguments 传递时,它们被转换为 strings,因为它们绑定到(位置上)隐含的-Path参数,该参数是[string[]类型的。
  • 顺便说一句:如果你想防止意外地将路径解释为wildcard expressions,你必须显式地使用-LiteralPath参数(当通过管道提供FileInfo/DirectoryInfo instances _时,该参数是 * 隐式 * 绑定的)。

这对于包含[]字符的路径很重要,因为它们在PowerShell的通配符语言中是 * 元字符 *(用于定义字符范围和集合)。

  • 仅在 Windows PowerShell 中,这种字符串化 situationally 只会导致文件的 name.Name),而不是其 full path.FullName)用于获取字符串表示。
  • 幸运的是,PowerShell(Core)7+(.NET(Core))现在 * 一致 * 使用.FullName
  • 有关背景信息,请参见this answer
  • 是否发生.Name.FullName字符串化取决于 * Get-ChildItem调用的细节 *(请参阅链接的答案),在您的情况下,只发生.Name字符串化。
  • 因此,由于使用了-RecurseGet-Item将无法找到在$path的 * 子目录 * 中找到的任何FileInfo示例,因为由于.Name字符串化而丢失了路径信息。
  • 在本案中:
  • D:\Radio是您的 * 当前 * 位置,由于Set-Location $path调用。
  • 例如,递归的Get-ChildItem调用然后查找文件D:\Radio\kmox\202303100400.mp4,并将其FileInfo表示作为数组的一部分存储在$media
  • Get-Item $media调用中,该文件然后被字符串化为202303100400.mp4only(由.Name),在没有 path 信息的情况下,Get-Item试图根据 current 位置(目录)进行解析。
  • 也就是说,Get-Item查找D:\Radio\202303100400.mp4(缺少kmox路径组件)-它不存在,并导致您看到的错误。

相关问题