powershell - 从作为任务运行的Powershell 2.0脚本获取错误输出

标签 powershell scheduled-tasks powershell-2.0

TL:DR实际问题在底部

我正在尝试解决Powershell v1.0脚本问题。该脚本基本上是从FTP站点下载文件,然后通过UNC将该文件放在远程服务器上,并通过电子邮件将任务的成功或失败发送给您。

该脚本作为具有通用ID的任务运行,该ID为Domain Admin,但不用于登录系统,因此运行该服务器的服务器不包含其配置文件。

如果我为该用户执行runas并通过命令行执行脚本,那么它将完美无缺。但是,如果我尝试将其作为任务运行,则会立即退出。如果我打开runas命令提示符并在命令行中运行预定任务vi,我得到的一切就是:

成功:尝试运行计划的任务“任务名称”。

我尝试将变量值写入文本文件以查看发生了什么,但是即使将它们作为执行的第一步而编写,它也不会写入。

我想要做的是捕获在尝试运行脚本和/或将变量信息写入文本文件时通常会看到的所有脚本错误消息。

有什么办法吗?顺便说一句,我通过使用以下参数调用powershell来完成:

-file -ExecutionPolicy绕过“d:\ datscript \ myscript.ps1”

-I've tried -command instead of -file.
-I've tried "d:\datscript\myscript.ps1 5>&1 test.txt"
-I've tried "d:\datscript\myscript.ps1 9>&1 test.txt"
-I've tried "d:\datscript\myscript.ps1 | out-file d:\datscript\test.txt"

没事。我敢肯定,我可以修复所有错误,但是我会把头撞在墙上,以获取某种故障信息。

-更新:这是脚本的副本减去详细信息-
#-------------------------------------------------------------------------------------------------------------------------------------------------------------

#
#Variable Declaration
#
#$path = Path on local server to downlaod DAT to
#$olddat = Old/last DAT downloaded
#$currentdat = Next DAT number
#$ftpsite = McAfee FTP site. Update if path changes
#$ftpuser = FTP user (anon login)
#$ftppass = FTP password (anon login)
#$tempstring = Manipulation variable
#$gotdat = Boolean if updated DAT exists
#$success = Status if a new DAT exists and has been downloaded (used for email notification).
#$thetime = Variable use dto hold time of day manipulation.

$path = "\\myservername\ftproot\pub\mcafee\datfiles\"
$olddat = ""
$currentdat =""
$ftpsite = "ftp://ftp.nai.com/virusdefs/4.x/"
$ftpuser = "something"
$ftppass = "anything"
$tempstring =""
$gotdat = "False"
$success = ""
$thetime = ""



#
#Normalized functions handles UNC paths
#
function Get-NormalizedFileSystemPath
{
    <#
    .Synopsis
       Normalizes file system paths.
    .DESCRIPTION
       Normalizes file system paths.  This is similar to what the Resolve-Path cmdlet does, except Get-NormalizedFileSystemPath also properly handles UNC paths and converts 8.3 short names to long paths.
    .PARAMETER Path
       The path or paths to be normalized.
    .PARAMETER IncludeProviderPrefix
       If this switch is passed, normalized paths will be prefixed with 'FileSystem::'.  This allows them to be reliably passed to cmdlets such as Get-Content, Get-Item, etc, regardless of Powershell's current location.
    .EXAMPLE
       Get-NormalizedFileSystemPath -Path '\\server\share\.\SomeFolder\..\SomeOtherFolder\File.txt'

       Returns '\\server\share\SomeOtherFolder\File.txt'
    .EXAMPLE
       '\\server\c$\.\SomeFolder\..\PROGRA~1' | Get-NormalizedFileSystemPath -IncludeProviderPrefix

       Assuming you can access the c$ share on \\server, and PROGRA~1 is the short name for "Program Files" (which is common), returns:

       'FileSystem::\\server\c$\Program Files'
    .INPUTS
       String
    .OUTPUTS
       String
    .NOTES
       Paths passed to this command cannot contain wildcards; these will be treated as invalid characters by the .NET Framework classes which do the work of validating and normalizing the path.
    .LINK
       Resolve-Path
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('PSPath', 'FullName')]
        [string[]]
        $Path,

        [switch]
        $IncludeProviderPrefix
    )

    process
    {
        foreach ($_path in $Path)
        {
            $_resolved = $_path

            if ($_resolved -match '^([^:]+)::')
            {
                $providerName = $matches[1]

                if ($providerName -ne 'FileSystem')
                {
                    Write-Error "Only FileSystem paths may be passed to Get-NormalizedFileSystemPath.  Value '$_path' is for provider '$providerName'."
                    continue
                }

                $_resolved = $_resolved.Substring($matches[0].Length)
            }

            if (-not [System.IO.Path]::IsPathRooted($_resolved))
            {
                $_resolved = Join-Path -Path $PSCmdlet.SessionState.Path.CurrentFileSystemLocation -ChildPath $_resolved
            }

            try
            {
                $dirInfo = New-Object System.IO.DirectoryInfo($_resolved)
            }
            catch
            {
                $exception = $_.Exception
                while ($null -ne $exception.InnerException)
                {
                    $exception = $exception.InnerException
                }

                Write-Error "Value '$_path' could not be parsed as a FileSystem path: $($exception.Message)"

                continue
            }

            $_resolved = $dirInfo.FullName

            if ($IncludeProviderPrefix)
            {
                $_resolved = "FileSystem::$_resolved"
            }

            Write-Output $_resolved
        }
    } # process

} # function Get-NormalizedFileSystemPath

#
#Get the number of the exisiting DAT file and increment for next DAT if the DAT's age is older than today.
# Otherwise, exit the program if DATs age is today.
#
$tempstring = "xdat.exe"


$env:Path = $env:Path + ";d:\datscript"
$path2 ="d:\datscript\debug.txt"
add-content $path2 $path
add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "


$path = Get-NormalizedFileSystemPath -Path $path

Set-Location -Path $path
$olddat = dir $path | %{$_.Name.substring(0, 4) }
$olddatfull = "$olddat" + "$tempstring"
if ( ((get-date) - (ls $olddatfull).LastWriteTime).day -lt 1)
    {
#***** Commented out for testing!
#        exit
    }
$currentdat =  [INT] $olddat
$currentdat++
$currentdat = "$currentdat" + "$tempstring"

add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "


#
#Connect to FTP site and get a current directory listing. 
#
[System.Net.FtpWebRequest]$ftp = [System.Net.WebRequest]::Create($ftpsite) 
$ftp.Method = [System.Net.WebRequestMethods+FTP]::ListDirectoryDetails

$response = $ftp.getresponse() 
$stream = $response.getresponsestream() 

$buffer = new-object System.Byte[] 1024 
$encoding = new-object System.Text.AsciiEncoding 

$outputBuffer = "" 
$foundMore = $false 

#
# Read all the data available from the ftp directory stream, writing it to the 
# output buffer when done. After that the buffer is searched to see if it cotains the expected
# lastest DAT.
#
do 
{ 
    ## Allow data to buffer for a bit 
    start-sleep -m 1000 

    ## Read what data is available 
    $foundmore = $false 
    $stream.ReadTimeout = 1000

    do 
    { 
        try 
        { 
            $read = $stream.Read($buffer, 0, 1024) 

            if($read -gt 0) 
            { 
                $foundmore = $true 
                $outputBuffer += ($encoding.GetString($buffer, 0, $read)) 
            } 
        } catch { $foundMore = $false; $read = 0 } 
    } while($read -gt 0) 
} while($foundmore)

$gotdat = $outputbuffer.Contains($currentdat)
$target = $path + $currentdat


#
# Downloads DATs and cleans up old DAT file. Returns status of the operation. 
# Return 1 = success
# Return 2 = Latest DAT not found and 4pm or later
# Return 3 = DAT available but did not download or is 0 bytes
# Return 4 = LatesT DAT not found and before 4pm
#
$success = 0
if ($gotdat -eq "True")
     {
        $ftpfile = $ftpsite + $ftppath + $currentdat
        write-host $ftpfile
        write-host $target
        $ftpclient = New-Object system.Net.WebClient
        $uri = New-Object System.Uri($ftpfile)
        $ftpclient.DownloadFile($uri, $target)
        Start-Sleep -s 30
        if ( ((get-date) - (ls $target).LastWriteTime).days -ge 1)
        {
            $success = 3
        }
        else
        {
            $testlength = (get-item $target).length
            if( (get-item $target).length -gt 0)
            {
                Remove-Item "$olddatfull"
                $success = 1
            }
            else
            {
                $success = 3
            }
        }
    }
    else
    {
        $thetime = Get-Date
        $thetime = $thetime.Hour
        if ($thetime -ge 16)
        {
            $success = 2   
        }
        else
        {
            $success = 4
            exit
        }
    }


#
# If successful download (success = 1) run push bat
#
if ($success -eq 1)
{
    Start-Process "cmd.exe"  "/c c:\scripts\mcafeepush.bat"
}


#Email structure
#
#Sends result email based on previous determination
#
#SMTP server name
$smtpServer = "emailserver.domain.com"

#Creating a Mail object
$msg = new-object Net.Mail.MailMessage

#Creating SMTP server object
$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$msg.From = "email1@domain.com"
$msg.ReplyTo = "email2@domain.com"
$msg.To.Add("email2@domain.com")
switch ($success)
    {
     1 {
        $msg.subject = "McAfee Dats $currentdat successful"
        $msg.body = ("DAT download completed successfully. Automaton v1.0")
        }
     2 {
        $msg.subject = "McAfee DATs Error"
        $msg.body = ("Looking for DAT $currentdat on the FTP site but I coud not find it. Human intervention may be required. Automaton v1.0")
        }
     3 {
        $msg.subject = "McAfee DATs Error"
        $msg.body = ("$currentdat is available for download but download has failed. Human intervention will be required. Automaton v1.0")
        }
     default {
        $msg.subject = "DAT Automaton Error"
        $msg.body = ("Something broke with the McAfee automation script. Human intervention will be required. Automaton v1.0")
        }
     }

#Sending email
$smtp.Send($msg)

#Needed to keep the program from exiting too fast.
Start-Sleep -s 30


#debugging stuff
add-content $path2 $olddat
add-content $path2 $currentdat
add-content $path2 $success
add-content $path2 "      "

最佳答案

显然,由于启动的Powershell版本或帐户上的执行策略不同,启动Powershell时出错,或者计划的任务存在访问错误。要收集实际错误,您可以启动一个任务,如下所示:

cmd /c "powershell.exe -file d:\datscript\myscript.ps1 test.txt 2>&1" >c:\windows\temp\test.log 2&>1

这样,如果启动Powershell时发生错误,它将记录在c:\windows\temp\test.log文件中。如果问题出在执行策略中,则可以使用以下命令创建和运行(一次)任务:
powershell -command "Get-ExecutionPolicy -List | out-file c:/windows/temp/policy.txt; Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force"

在计划运行主任务的帐户下运行任务将首先使策略生效(这样,如果设置计算机级策略无济于事,您将知道要更改的范围)并将计算机级策略设置为“RemoteSigned”,是除允许每个脚本之外的限制性最低的级别(强烈建议不要使用,在Powershell上编写的编码器脚本可能会破坏您的数据)。

希望这可以帮助。

更新:如果这不是策略,则在正确编写任务参数时可能会出现一些错误。您可以执行以下操作:使用启动脚本的字符串创建.bat文件,并将输出重定向为test1.txt,然后将计划的任务更改为cmd.exe -c launcher.bat >test2.txt,正确指定主文件夹。运行任务并查看两个文件,其中至少一个应包含一个错误,阻止您的脚本启动。

关于powershell - 从作为任务运行的Powershell 2.0脚本获取错误输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31327464/

相关文章:

powershell - 在 Azure VM 上使用 Powershell 下载不稳定的文件

android - 我想在android中的不同日期(星期一,星期二等)的特定时间安排任务

windows - 如何在系统启动后20分钟运行windows计划任务?

windows - 无法获取服务 Powershell 的状态

powershell - 检查 Powershell 中的范围

powershell-2.0 - 将文件复制到 lastwritetime -ge 3/26/2010 9 :00pm with Powershell

powershell - 没有 CmdletBinding() 动态参数不起作用

powershell - Windows 7 PowerShell复制项(如果较大)

sql-server - 我可以用 Azure Scheduler 替换 SQL 作业吗?

security - SSRS 2008 R2,场 - 负载平衡和 HTTP 状态 401 : Unauthorized