简体   繁体   English

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

[英]Getting error output from a powershell 2.0 script running as a task

TL:DR actual question is at the bottom TL:DR实际问题在底部

I'm trying to troubleshoot a Powershell v1.0 script issue. 我正在尝试解决Powershell v1.0脚本问题。 The script basically downloads a file from an FTP site and puts it on a remote server via UNC and emails the success or failure of the task. 该脚本基本上是从FTP站点下载文件,然后通过UNC将该文件放到远程服务器上,并通过电子邮件将任务的成功或失败发送出去。

The script runs as a task with a generic ID that is a Domain Admin but is not used to log into systems so the server it runs off of does not contain a profile for it. 该脚本作为具有通用ID的任务运行,该ID为Domain Admin,但不用于登录系统,因此运行该服务器的服务器不包含其配置文件。

If I do a runas for that user and execute the script via command line it works flawlessly. 如果我为该用户执行runas并通过命令行执行脚本,那么它将完美无缺。 However, if I try to run it as a task it runs then exits instantly. 但是,如果我尝试将其作为任务运行,则会立即退出。 If I open a runas command prompt and run the scheduled task vi at he command line all I get back is: 如果我打开runas命令提示符并在他的命令行中运行计划的任务vi,我得到的一切就是:

SUCCESS: Attempted to run the scheduled task "Task Name". 成功:尝试运行计划的任务“任务名称”。

I've tried writing variable values to a text file to see what is going on but it never writes even when I write them as the very first step of execution. 我尝试将变量值写入文本文件以查看发生了什么,但是即使将它们作为执行的第一步而编写,它也不会写入。

What I want to do is capture any script error messages you would normally see when trying to run the script and/or write the variable information to a text file. 我想要做的是捕获在尝试运行脚本和/或将变量信息写入文本文件时通常会看到的所有脚本错误消息。

Is there any way to do this? 有什么办法吗? BTW I doing via calling powershell with the following arguments: 顺便说一句,我通过使用以下参数调用powershell来完成:

-file -ExecutionPolicy Bypass "d:\\datscript\\myscript.ps1" -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"

Nothing worked. 没事。 I'm sure I can fix whatever bug I have but I'm banging my head against the wall trying to get some kind of failure info. 我敢肯定,我可以修复所有错误,但是我会把头撞在墙上,以获取某种故障信息。

--Update: Here is a copy of the script minus details-- -更新:这是脚本的副本减去详细信息-

#-------------------------------------------------------------------------------------------------------------------------------------------------------------

#
#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 "      "

Apparently you have an error in starting Powershell, either because execution policy is different on the Powershell version you start, or on the account, or there is an access error on the scheduled task. 显然,由于启动的Powershell版本或帐户的执行策略不同,启动Powershell时出错,或者计划的任务存在访问错误。 To gather actual error, you can launch a task like so: 要收集实际错误,您可以启动一个任务,如下所示:

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

This way if there would be an error on starting Powershell, it will be logged in the c:\\windows\\temp\\test.log file. 这样,如果启动Powershell时出现错误,它将记录在c:\\windows\\temp\\test.log文件中。 If the issue is in execution policy, you can create and run (once) a task with the following: 如果问题出在执行策略中,则可以使用以下命令创建和运行(一次)任务:

powershell -command "Get-ExecutionPolicy -List | out-file c:/windows/temp/policy.txt; Set-ExecutionPolicy RemoteSigned -Scope LocalMachine -Force"

Running a task under the account you plan to run your main task will first get the policies in effect (so that if setting machine-level policy won't help, you'll know what scope to alter) and set machine-level policy to "RemoteSigned", the least restrictive level beyond allowing every script (highly not recommended, there are encoder scripts written on Powershell that can ruin your data). 在计划运行主任务的帐户下运行任务将首先使策略生效(这样,如果设置计算机级策略无济于事,您将知道要更改的范围)并将计算机级策略设置为“ RemoteSigned”,是除允许每个脚本之外的限制性最低的级别(强烈建议不要使用,在Powershell上编写的编码器脚本可能会破坏数据)。

Hope this helps. 希望这可以帮助。

UPDATE: If that's not policy, there might be some errors in properly writing the parameters for the task. 更新:如果这不是策略,则在正确编写任务参数时可能会出现一些错误。 You can do this: Create a .bat file with the string that launches your script and redirects output to say test1.txt , then change the scheduled task to cmd.exe -c launcher.bat >test2.txt , properly specifying the home folder. 您可以执行以下操作:使用启动脚本的字符串创建.bat文件,并将输出重定向到test1.txt ,然后将计划任务更改为cmd.exe -c launcher.bat >test2.txt ,正确指定主文件夹。 Run the task and review both files, at least one of them should contain an error that prevents your script from launching. 运行任务并查看两个文件,其中至少一个文件应包含一个错误,阻止脚本启动。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM