How can I redirect PowerShell output when run from Task Scheduler?
Asked Answered
T

10

69

When running a simple PowerShell script from Task Scheduler, I would like to redirect the output to a file.

There is a long thread about this very topic here, yet it's not clear if they reached the most appropriate solution in the end. I'm interested if anyone on Stack Overflow has also solved this problem, and how they did it?

Triacid answered 9/12, 2012 at 3:35 Comment(0)
T
46

Here is the command that worked for me. I didn't like the idea of redirecting the output in the script, since it would make it difficult to run manually.

powershell -windowstyle minimized -c "powershell -c .\myscript.ps1 -verbose >> \\server\myscript.log 2>&1"
Triacid answered 11/12, 2012 at 20:20 Comment(9)
You might want to use -Noninteractive as well to avoid any prompts.Falconet
How do you add a timestamp to this?Waylon
If anybody knows how to do this with spaces in the file paths I'd love to know how. I can't get the parser to parse double quotes, single quotes or escaped quotes properly.Archon
If you would like to parse it with spaces, check this questionWrac
how do you add timestamp to this in task scheduler?Jilljillana
This looks like you're invoking powershell from within powershell?Scissile
You can easily add $(get-date -f yyyy-MM-dd).log to get the date. Check out this link for other format options, @Jilljillana long time ago so I suppose that you already found the answerPolice
Yeah, like @JamieHanrahan noticed, what's the purpose of invoking powershell twice?Insipience
I'm pretty confident you don't need to do the inner invocation of powershell. The key points of this solution are: (1) pass the outer powershell exe a command (-c) not a file (2) do the redirection to a logfile within that command, so powershell handles it not cmd.exe.Incorporated
D
48

I use the transcript feature to help with this. Just include it in your code and it outputs all (would be) screen content to a log file.

Start-Transcript -path $LogFile -append

<Body of the script>

Stop-Transcript
Doublebank answered 24/6, 2013 at 8:22 Comment(5)
I'm seeing different behavior for transcripts when I launch the command form Task Scheduler (as an argument to powershell.exe). Specifically, calls to Write-Host seem to send my text somewhere the transcript doesn't capture, although the redirect to Out-Host seems to work. I'm running PowerShell 4.0 on Windows Server 2012 R2 if that matters.Septillion
I had to use Write-Output rather than Write-Host.Wesleywesleyan
@JayCarlton you shoudlnt be using write-host but write-output instead as the creator of powershell says: jsnover.com/blog/2013/12/07/write-host-considered-harmful that said, I havent tested if it will make a difference in your log but it should.Deferral
Cool, neither does my powershell script seem to do anything in the registry (due to rights) from the Task Scheduler, or can it write to disk with the above logging enabled. Running as my user, with or without 'run with maximum ?capabilities?rights?'Mathew
One issue I see with this, is if I'm trying to capture the write-host output during a foreach-object in a pipe, I can't switch it to write-output otherwise it would be written to the Success output streamWhitechapel
T
46

Here is the command that worked for me. I didn't like the idea of redirecting the output in the script, since it would make it difficult to run manually.

powershell -windowstyle minimized -c "powershell -c .\myscript.ps1 -verbose >> \\server\myscript.log 2>&1"
Triacid answered 11/12, 2012 at 20:20 Comment(9)
You might want to use -Noninteractive as well to avoid any prompts.Falconet
How do you add a timestamp to this?Waylon
If anybody knows how to do this with spaces in the file paths I'd love to know how. I can't get the parser to parse double quotes, single quotes or escaped quotes properly.Archon
If you would like to parse it with spaces, check this questionWrac
how do you add timestamp to this in task scheduler?Jilljillana
This looks like you're invoking powershell from within powershell?Scissile
You can easily add $(get-date -f yyyy-MM-dd).log to get the date. Check out this link for other format options, @Jilljillana long time ago so I suppose that you already found the answerPolice
Yeah, like @JamieHanrahan noticed, what's the purpose of invoking powershell twice?Insipience
I'm pretty confident you don't need to do the inner invocation of powershell. The key points of this solution are: (1) pass the outer powershell exe a command (-c) not a file (2) do the redirection to a logfile within that command, so powershell handles it not cmd.exe.Incorporated
A
34

The following works for me on Windows 7:

 powershell -command c:\temp\pscript.ps1 2>&1 > c:/temp/apickles.log

In the Windows 7 Task Scheduler GUI:

 program = "Powershell"
 arguments = "-command c:\temp\pscript.ps1 2>&1 > c:/temp/apickles.log"

Note that "-file" does not work because all parameters after the file name are interpreted as parameters to the file. Note that "2>&1" redirects the error output to the log file as well. Later versions of PowerShell do this in a slightly different way.

Aalto answered 4/3, 2014 at 6:58 Comment(4)
Thanks for the comment about Windows 7 Task Scheduler GUI "Arguments" - I guess that's the right place to put the output redirection options. None of the other answers mentioned this.Coltun
-1 for win10. It's not working for me in windows 10. This post has the same issue. social.technet.microsoft.com/Forums/windowsserver/en-US/…Downwind
One downside of "-Command" is that it is interpreted, so spaces or anything interpretable in the path to the ps1 file turn this into an error. So it's better to set the working directory separately and just use a (non-special-character) filename for -Command.Kahl
In the GUI, do not put quotation marks into the input field, only what is written between them (above). Use Working directory instead of path to logfile.Vishnu
R
4

A combination of all the previous answers really helped me out...

My problem was that I needed to execute a PowerShell script over WinRM as part of a Packer provisioner, and it kept failing due to rights issues. The way around this is to execute as a scheduled task, but I needed to pass arguments to the script and get the output as well to confirm everything passed.

This snippet worked well for me:

# Generate a unique ID for this execution
$guid = [guid]::NewGuid()
$logFile = "C:\Windows\Temp\$guid.log"

$argument = "-NoProfile -ExecutionPolicy unrestricted -Command ""& {""$ScriptPath"" $ScriptArgs} 2>&1 > $logFile"""

$a = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $argument
Register-ScheduledTask -TaskName $TaskName  -RunLevel Highest -User $username -Password $password -Action $a | Start-ScheduledTask
do {
    Start-Sleep -Seconds 30
    $task = Get-ScheduledTask -TaskName $TaskName
} while ($task.State -eq 4)

The full script can be found here:

https://gist.github.com/dev-rowbot/fa8b8dadf1b3731067a93065db3e1bba

Rafael answered 11/1, 2017 at 11:8 Comment(0)
L
3

It would be maybe easier to use Start-Transcript in order to log directly to a file:

# Get script name
$ScriptFullPath = $MyInvocation.MyCommand.Path
# Start logging stdout and stderr to file
Start-Transcript -Path "$ScriptFullPath.log" -Append

[Some PowerShell commands]

# Stop logging to file
Stop-Transcript

The log file will be in the same directory as the script.

Leonorleonora answered 26/1, 2018 at 9:55 Comment(0)
P
2

I would do:
Create a function to call your script and redirect the output of this function like this:

.ps1:

function test{
    # Your simple script commands
    ls c:\temp -Filter *.JPG
    ls z:\ # Non-existent directory
}

test *> c:\temp\log.txt

Here is the log file:

    Répertoire : C:\temp


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        07/06/2008     11:06     176275 HPIM1427.JPG
-a---        07/06/2008     11:06      69091 HPIM1428.JPG
-a---        07/06/2008     11:06     174661 HPIM1429.JPG


ls : Lecteur introuvable. Il n'existe aucun lecteur nommé « z ».
Au caractère C:\temp\test.ps1:14 : 1
+ ls z:\ #non existent dir
+ ~~~~~~
    + CategoryInfo          : ObjectNotFound: (z:String) [Get-ChildItem], Driv
   eNotFoundException
    + FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetC
   hildItemCommand

You can control what do you want to output with the new V3 redirection operators:

Do-Something 3> warning.txt  # Writes warning output to warning.txt
Do-Something 4>> verbose.txt # Appends verbose.txt with the verbose output
Do-Something 5>&1            # Writes debug output to the output stream
Do-Something *> out.txt      # Redirects all streams (output, error, warning, verbose, and debug) to out.txt
Paleozoology answered 9/12, 2012 at 7:36 Comment(1)
But does *> work when running from a Scheduled Task? Because the question implies that it doesnt.Acidity
A
2

I tested the following on Windows Server 2016, only call powershell once. This way you can have folder names with a space:

Powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle minimized "scriptName.ps1 *>> '\\servername\sharename\folder name with space\logfilename.log'"
Amylaceous answered 25/1, 2021 at 3:27 Comment(0)
B
1

I couldn't get the suggested answers to work for me unless I added an & before the script filename. When testing from cmd prompt, it works without the & but needs it from task scheduler.

  • OS: Windows Server 2022
  • PS: Powershell 7.4
  • Task Scheduler:
    • Program/script:"C:\Program Files\PowerShell\7\pwsh.exe"

    • Add arguments:-windowstyle minimized -Noninteractive -command "& 'C:\myscript\myscript.ps1' -subscriptionname 'My-Sub' -localpath '\\myserver\Share\Stuff' *> $(new-item -force -path $('C:\myscript\_Log\' + $(get-date -f 'yyyy') + '\' + $(get-date -f 'MM') + '\' + $(get-date -f 'dd') + '\' + 'myscript-' + $(get-date -f 'yyyyMMdd_HHmmss') + 'copyjob' + '_console.txt'))"

This captures all streams to something like this:c:\myscript\_Log\2024\02\18\myscript-20240218_101600copyjob_console.txt

Notes:

  • & Needed before script filename.
  • *> Redirects all output streams like warnings and errors.
  • $(get-date -f '...') Formats datetime to specified format.
  • new-item -force -path ... Will create the specified path if it doesn't exist.
    • Need to also enclose it all in $() so it appears as a single target for the redirect: *> $(new-item -force -path ...)
Boughpot answered 18/2 at 15:49 Comment(1)
Good findings 👍. Consider storing logs and transcripts in $ENV:ProgramData or another protected area instead of C:\myscript to make it harder to manipulate/erase the log files.Dodecasyllable
C
0

PowerShell 3 onwards allows individual streams (out, verbose, and error) to be redirected. However, Task Scheduler does not understand them. It's PowerShell that does the redirection.

@cmcginty's solution halfway works, since PowerShell is invoking PowerShell. It only supports standard streams (error and out). If you want to use all streams you need to use:

Program or script: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

Argument: -windowstyle minimized –NonInteractive –NoProfile -c "powershell -c path_to_ps1.ps1 4>>verbose.log 5>>debug.log"

Start in: path_to_ps1

Cyclopean answered 8/6, 2018 at 21:59 Comment(0)
C
0

I'm inspired to write this answer by a comment-question from Anders (How do you add a timestamp?), and by the multiple replies that invoke PowerShell twice, which I would argue is not required. The task scheduler program is clearly "powershell.exe" and for the arguments I would suggest simply:

-noninteractive -c "scriptpath.ps1 *>>logpath.txt"

To keep a file per day, then:

-noninteractive -c "scriptpath.ps1 *>>logpath-$((get-date).ToString('yyyy-MM-dd')).txt"

And if you do want a log file with timestamp, you could remove the second ">" (two indicating append to any existing file) and expand the time format:

-noninteractive -c "scriptpath.ps1 *>logpath-$((get-date).ToString('yyyy-MM-dd_HH-mm-ss-fff')).txt"
Churchman answered 16/11, 2020 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.