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

时间:2015-07-09 20:21:18

标签: powershell scheduled-tasks powershell-v2.0

TL:DR实际问题在底部

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

该脚本作为任务运行,其通用ID是域管理员,但不用于登录系统,因此运行它的服务器不包含其配置文件。

如果我为该用户执行runas并通过命令行执行脚本,它可以完美地运行。但是,如果我尝试将其作为任务运行,则会立即退出。如果我打开一个runas命令提示符并在命令行运行计划任务vi,我得到的全部是:

成功:尝试运行计划任务"任务名称"。

我尝试将变量值写入文本文件以查看正在发生的事情,但即使我将它们写为执行的第一步,它也永远不会写入。

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

有没有办法做到这一点?顺便说一句,我通过使用以下参数调用powershell来做:

-file -ExecutionPolicy Bypass" 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 "      "

1 个答案:

答案 0 :(得分:0)

显然,启动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,正确指定主文件夹。运行任务并查看这两个文件,其中至少有一个文件应包含阻止脚本启动的错误。

相关问题