Task Outputs

Declaring and evaluating task output parameters

The output section contains declarations that are exposed as outputs of the task after the successful execution of the instantiated command. An output declaration must be initialized, and its value is evaluated only after the task's command completes successfully, enabling any files generated by the command to be used to determine its value.

Example: outputs_task.wdl

version 1.1

task outputs {
  input {
    Int t
  }

  command <<<
  printf ~{t} > threshold.txt
  touch a.csv b.csv
  >>>

  output {
    Int threshold = read_int("threshold.txt")
    Array[File]+ csvs = glob("*.csv")
    Boolean two_csvs = length(csvs) == 2
  }
}

Example input:

{
  "outputs.t": 5
}

Example output:

{
  "outputs.threshold": 5,
  "outputs.two_csvs": true
}

Test config:

{
  "exclude_outputs": ["outputs.csvs"]
}

After the command is executed, the following outputs are expected to be found in the task execution directory:

  • A file called "threshold.txt", which contains one line that consists of only an integer and whitespace.
  • One or more files (as indicated by the + postfix quantifier) with the .csv extension in the working directory that are collected into an array by the glob function.

See the WDL Value Serialization section for more details.

ยงFiles and Optional Outputs

File outputs are represented as string paths.

A common pattern is to use a placeholder in a string expression to construct a file name as a function of the task input. For example:

Example: file_output_task.wdl

version 1.1

task file_output {
  input {
    String prefix
  }

  command <<<
    printf "hello" > ~{prefix}.hello
    printf "goodbye" > ~{prefix}.goodbye
  >>>

  output {
    Array[String] basenames = [basename("~{prefix}.hello"), basename("~{prefix}.goodbye")]
  }
}

Example input:

{
  "file_output.prefix": "foo"
}

Example output:

{
  "file_output.basenames": ["foo.hello", "foo.goodbye"]
}

Another common pattern is to use the glob function to define outputs that might contain zero, one, or many files.

Example: glob_task.wdl

version 1.1

task glob {
  input {
    Int num_files
  }

  command <<<
  for i in {1..~{num_files}}; do
    printf ${i} > file_${i}.txt
  done
  >>>

  output {
    Array[File] outfiles = glob("*.txt")
    Int last_file_contents = read_int(outfiles[num_files-1])
  }
}

Example input:

{
  "glob.num_files": 3
}

Example output:

{
  "glob.last_file_contents": 3
}

Test config:

{
  "exclude_outputs": ["glob.outfiles"]
}

Relative paths are interpreted relative to the execution directory, whereas absolute paths are interpreted in a container-dependent way.

Example: relative_and_absolute_task.wdl

version 1.1

task relative_and_absolute {
  command <<<
    mkdir -p my/path/to
    printf "something" > my/path/to/something.txt
  >>>

  output {
    String something = read_string("my/path/to/something.txt")
    # The following may or may not work depending on what the execution engine
    # supports.
    #
    # File bashrc = "/root/.bashrc"
  }

  runtime {
    container: "ubuntu:focal"
  }
}

Example input:

{}

Example output:

{
  "relative_and_absolute.something": "something"
}

All file outputs are required to exist, otherwise the task will fail. However, an output may be declared as optional (e.g., File? or Array[File?]), in which case the value will be undefined if the file does not exist.

Example: optional_output_task.wdl

version 1.1

task optional_output {
  input {
    Boolean make_example2
  }
  command <<<
    printf "1" > example1.txt
    if ~{make_example2}; then
      printf "2" > example2.txt
    fi
  >>>
  output {
    File example1 = "example1.txt"
    File? example2 = "example2.txt"
    Array[File?] file_array = ["example1.txt", "example2.txt"]
    Int file_array_len = length(select_all(file_array))
  }
}

Example input:

{
  "optional_output.make_example2": false
}

Example output:

{
  "optional_output.example2": null,
  "optional_output.file_array_len": 1,
  "optional_output.example1": "example1.txt",
  "optional_output.file_array": ["example1.txt", null]
}

Test config:

{
  "exclude_outputs": ["optional_output.example1", "optional_output.file_array"]
}

Executing the above task with make_example2 = true will result in the following outputs:

  • optional_output.example1 will resolve to aFile
  • optional_output.example2 will resolve to None
  • optional_output.file_array will resolve to [<File>, None]