【问题标题】:PowerShell Script in Task Scheduler with Async Function具有异步功能的任务计划程序中的 PowerShell 脚本
【发布时间】:2022-12-16 06:21:36
【问题描述】:

我有一个相当大的 PowerShell 脚本,我已将其分成两个单独的脚本。第一个脚本以调用异步函数结束,然后退出。

该函数检查在前面的脚本中生成的电子邮件导出的状态,执行一个 WHILE 循环,它每六十秒调用一次 Google 以查看导出是否准备就绪。导出准备就绪后,它会更新几个 SQL 数据库,我的第二个脚本知道接管。

当我在 shell/控制台中运行“第一个脚本”时,这 100% 的时间都有效。但我已经开始注意到,当我的计划任务在任务计划程序中被触发时……什么也没有发生。我有大量的日志记录,所以我知道参数正在被发送到异步函数,但它似乎只是弹出而不是继续循环遍历 WHILE 并每六十秒检查一次。

我觉得我已经在这里进行了谷歌搜索的尽职调查,但是......我是否缺少任务计划程序作业以确保包含 WHILE 循环的函数能够正常运行?

在下方编辑

为了更好地解释我在做什么,我将包括从我的函数中剥离的代码以及从下面的主脚本调用函数。

首先,调用“script_01”末尾的函数。

# Let's send the Google Vault export information over to our function.
        Try {
            $VaultParameters = @{
                employee_name = "$employee_name"
                export_name = "$export_name"
                sql_id = "$sql_id"
                vault_status_id = "$vault_status_id"
            }
            VaultExport @VaultParameters

            $LoggingParameters = @{
                logfile = "C:\script_logs\log.log"
                log = "INFO: Sent the Google Vault export information over to our async function."
            }
            EventLogging @LoggingParameters
        } Catch {
            $LoggingParameters = @{
                logfile = "C:\script_logs\log.log"
                log = "ERROR: Could not send the Google Vault export information over to our async function.`n$_"
            }
            EventLogging @LoggingParameters
        }

现在是功能本身。它很大...

function VaultExport {
    param (
        [cmdletbinding()]
        [parameter()]
        [string]$employee_name,
        [parameter()]
        [string]$export_name,
        [parameter()]
        [string]$sql_id,
        [parameter()]
        [string]$vault_status_id
    )

    $scriptBlock = {
        param ($employee_name,$export_name,$sql_id,$vault_status_id)
        Import-Module SimplySQL
        $logfile = "C:\script_logs\log.log"
        $now = (Get-Date).tostring("MM-dd-yyyy hh:mm:ss")

        # Let's define our MySQL database credentials for later use.
        # DEFINING SQL CREDS HERE

        # Let's generate secure credentials for our MySQL 'terms' db.
        # GENERATING SECURE CREDS HERE

        # And now we'll connect to our SQL db...
        # CONNECTING TO SQL HERE

        $vault_ready = "no"
        Add-Content $logfile "$now INFO: Beginning the WHILE loop while $export_name completes..."
        while ($vault_ready -eq "no") {
            $vault_status = gam info export "Email Exports" "$export_name"
            $vault_status = $vault_status -Match "COMPLETED"
            $vault_status = $vault_status -split(": ")
            $vault_status = $vault_status[1]
            if ($vault_status -eq "COMPLETED") {
                $vault_ready = "yes"
                $completed = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
                Invoke-SqlUpdate -Query "UPDATE ``table`` SET ``vault_status`` = '$vault_status', ``vault_completed`` = '$completed' WHERE ``id`` = '$vault_status_id'"
                Invoke-SqlUpdate -Query "UPDATE ``table`` SET ``vault_status`` = '1' WHERE ``id`` = '$sql_id'"
                $now = (Get-Date).tostring("MM-dd-yyyy hh:mm:ss")
                Add-Content $logfile "$now INFO: $export_name is ready to download. Updated vault_status in our dbs."
            } else {
                $vault_status = gam info export "Email Exports" "$export_name"
                $vault_status = $vault_status -Match "IN_PROGRESS"
                $vault_status = $vault_status -split(": ")
                $vault_status = $vault_status[1]
                Invoke-SqlUpdate -Query "UPDATE ``table`` SET ``vault_status`` = '$vault_status' WHERE ``id`` = '$vault_status_id'"
                $now = (Get-Date).tostring("MM-dd-yyyy hh:mm:ss")
                Add-Content $logfile "$now INFO: $export_name is not yet ready: ($vault_status). Checking again in sixty seconds."
                Start-Sleep 60
            }
        }
    }
    Start-Job -ScriptBlock $scriptBlock -ArgumentList @($employee_name,$export_name,$sql_id,$vault_status_id)
}
exit

【问题讨论】:

  • 如果您从 cmd 调用您的脚本,它会起作用吗?如果你从现有的 Powershell 会话运行它,我假设你在说 shell / 控制台时这样做,那么会话将保持打开状态并且异步过程将继续。从任务调度程序中,您的异步调用已完成,但由于它在另一个线程上,您需要保持主线程打开(例如:While / Start-Sleep -miliseconds 10 until the async function complete)。如果不这样做,主线程将在运行时退出并杀死子线程和异步函数。至少,这是我从你的帖子中得到的感觉。
  • 好的。超级有帮助……有点像我“不善于解释”的大脑在想什么。所以我需要找出当任务计划程序触发我的脚本时如何保持主线程/外壳打开......

标签: powershell function asynchronous windows-task-scheduler


【解决方案1】:

我不确定为什么你不让任务每 30 秒执行一次,而是让一个进程无限期运行并使用它自己的计时器

您为 WHILE 语句设置的条件得到满足,这就是循环不继续的原因。 将条件更改为永远不会满足的条件,问题就会消失,即

$Value1 = 0
WHILE($Value1 -ne 1){
#do things, never updating the $value1 variable
}

【讨论】:

  • 这是我在我的异步函数中所做的事情的简化版本。 $vault_ready = "no" while ($vault_ready -eq "no") { # perform the steps to check the current status of the Gmail export if ($status = "COMPLETE") { # update my SQL tables $vault_ready = "yes" } else { # move along start-sleep 60 } }
  • @DerekRitchison 我无法重现该问题。您如何定义计划任务操作?脚本是否有可能以受限语言模式运行并因此抛出异常并停止?您是否设置了无论指定用户是否登录都运行的任务?单击复选框以最高权限运行?
  • 嘿,谢谢你的跟进。为了让事情变得更容易一些,我用我的代码的净化版本编辑了原始帖子——包括我调用我的函数的主脚本,以及函数本身。至于你的问题:无论我的管理员帐户是否登录,它都设置为运行并且我选择了最高权限,是的。
  • @DerekRitchison 我建议在函数 VaultExport 上放置更多日志记录,以准确跟踪它被阻止的位置。您在函数调用上方有 TRY 和 CATCH 语句,这意味着如果函数中的任何内容抛出异常 PS 将移动到 catch 语句(即 invoke-sqlupdate 函数调用,由于缺少 cred 对象,我假设这是使用可能会失败的 SSPI)
  • 我知道当我在 cmd 中运行主脚本(调用此函数的脚本)时,Invoke-SqlUpdate 命令起作用。只有通过任务计划程序,所有内容才会弹出。我假设这是因为 Saga Pourpre 提到的:“从任务计划程序中,您的异步调用已完成,但由于它在另一个线程上,您需要保持主线程打开,”所以我需要想出一个保持主线程运行的方法,即使任务计划程序倾向于在主脚本完成后立即关闭 PowerShell 实例。
猜你喜欢
  • 2014-09-12
  • 2021-03-29
  • 2020-08-07
  • 2022-12-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多