PowerShell

让你现有的 PowerShell 脚本,拥有它们从未有过的调度能力。

Dagu 把 PowerShell、pwsh 和 cmd.exe 作为普通的工作流步骤运行,现有脚本保持原样,同时获得执行顺序、重试、按步骤日志,以及可在浏览器中打开的运行历史。

同一工作流中并用 PowerShell、pwsh 与 cmd.exe
按工作流或按步骤选择 shell
一个脚本的输出可供下一个使用
作为 Windows 服务运行
01

选一次 shell,必要时按步骤覆盖

工作流声明它的 shell,所有步骤继承它。未声明时 Dagu 依次优先 PowerShell、pwsh、cmd.exe,因此在一台机器上写好的工作流在另一台上的行为是可预期的。

  • shell 接受字符串或数组;数组形式可以避免 -NoProfile 这类参数的引号问题
  • 步骤可以把输出捕获到命名变量中,后续步骤和前置条件都能读取
  • preconditions 用该值控制步骤,因此只有服务确实处于停止状态时才会重启
检查服务,仅在停止时重启
# service-health.yaml
schedule: "*/15 * * * *"
max_active_runs: 1
shell: powershell -NoProfile

steps:
  - id: check_service
    run: |
      (Get-Service -Name 'MyAppSvc').Status
    output: SVC_STATUS

  - id: restart_if_stopped
    run: Restart-Service -Name 'MyAppSvc'
    depends: check_service
    preconditions:
      - condition: "${SVC_STATUS}"
        expected: "Stopped"
    retry_policy:
      limit: 2
      interval_sec: 30

mail_on:
  failure: true
02

PowerShell 与 cmd.exe 共处一个工作流

不会因为换了调度器就去重写遗留的批处理文件。步骤可以覆盖工作流的 shell,所以一个以 PowerShell 为主的工作流依然能调用那个用了十年的 .bat。

  • 步骤上的 shell 覆盖要写在 with.shell 里;裸的 shell 字段不能与 run 并用,校验器会拒绝
  • 若需要完全不经 shell 解析的精确参数列表,使用带 command 与 args 的结构化 exec 步骤
  • 无论各步骤用了哪种 shell,工作流仍然只有一个调度、一份历史和一条通知路径

Windows 路径包含反斜杠,在 YAML 中请优先使用块标量或单引号。在双引号字符串里,反斜杠后接字符会被当作转义,而不是路径分隔符。

一个文件里的 PowerShell、cmd 与直接 exec
# maintenance.yaml
schedule: "0 3 * * *"
shell: ["powershell", "-NoProfile"]

steps:
  - id: report_disk
    run: Get-PSDrive -PSProvider FileSystem | Out-String
    output: DISK_REPORT

  - id: legacy_job
    run: |
      @echo off
      call C:\ops\legacy\run.bat
    with:
      shell: cmd
    depends: report_disk

  - id: direct_exec
    action: exec
    with:
      command: C:\Windows\System32\cmd.exe
      args:
        - /c
        - echo
        - done
    depends: legacy_job

mail_on:
  failure: true
03

多行脚本保持可读

步骤可以容纳完整的脚本块而不只是一行命令,这样管道和筛选就能保持 PowerShell 作者习惯的书写形式。

  • 块标量保留换行,因此跨行的管道能原样保留
  • retry_policy 作用于整个步骤,适合访问不稳定网络路径或占用中文件的脚本
  • pwsh 与 Windows PowerShell 按名称选择,同时安装两者的主机也能按预期运行
定时执行的日志归档脚本
# archive-logs.yaml
schedule: "30 1 * * *"
shell: pwsh -NoProfile

steps:
  - id: archive
    run: |
      $cutoff = (Get-Date).AddDays(-30)
      Get-ChildItem -Path 'D:\logs' -Filter *.log |
        Where-Object { $_.LastWriteTime -lt $cutoff } |
        Compress-Archive -DestinationPath 'D:\archive\logs.zip' -Update
    retry_policy:
      limit: 2
      interval_sec: 60

  - id: verify
    run: Test-Path 'D:\archive\logs.zip'
    depends: archive

mail_on:
  failure: true
04

它从哪里运行

只有调度器本身在运行,调度才有意义。Windows 安装程序可以把 Dagu 注册为服务,于是工作流随机器启动,而不是随某个登录会话。

  • PowerShell 安装程序通过版本固定的 WinSW 包装器注册 Windows 服务,可用 Get-Service 验证
  • Windows 版本提供 amd64、386 和 arm64 构建
  • Web 控制台从任意浏览器展示运行历史与按步骤输出,查看作业无需远程桌面会话

Windows Server 2019 及更高版本是稳妥目标。在缺少 AF_UNIX 支持的旧版本上工作流仍会运行,但实时状态与停止控制的套接字不可用,Dagu 会记录警告。

FAQ

Practical questions before adopting

我需要把脚本改写成 YAML 吗?

不需要。YAML 描述的是何时运行、依赖什么、失败后怎么办。脚本本身仍是 .ps1 文件,调用方式和任务计划程序调用它时一样。

如何把一个脚本的值传给下一个?

给产出步骤设置输出名,然后在后续步骤中引用。同样的引用也可用于前置条件,从而实现「除非前面的检查返回特定值,否则跳过该步骤」。

同一个工作流能同时用 Windows PowerShell 和 pwsh 吗?

可以。把其中一个设为工作流默认,在需要的步骤上覆盖为另一个。两者都按可执行文件名选择,因此同时安装两者的主机会按预期运行每个步骤。

为什么校验器拒绝我在步骤上写的 shell 字段?

裸的 shell 字段不能与同一步骤的 run 并用,请把覆盖写到 with.shell 下。运行 dagu validate 可以在调度触发之前发现这个问题。

在 Linux 上也能用吗?

能。pwsh 可在 Linux 和 macOS 上运行,同一份工作流定义同样适用。只有调用 cmd.exe 或 Windows 专有 cmdlet 的步骤才是 Windows 特有的。

Next step

Start with one workflow.

Install Dagu, move one script that runs on cron today into YAML, and decide from a real run history.