This page collects some recurring implementation patterns used in Nextflow applications. Feel free to contribute by opening a pull request in the GitHub repository at this link.

1. Basic patterns

1.1. Channel duplication

1.1.1. Problem

You need to you use the same channel as input in two or more processes.

1.1.2. Solution

In DSL2, you can just do it! The into operator is no longer needed.

1.1.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
process foo {
  input: path x
  script:
  """
  echo your_command --input $x
  """
}

process bar {
  input: path x
  script:
  """
  echo your_command --input $x
  """
}

workflow {
  input_ch = Channel.fromPath("$baseDir/data/prots/*_?.fa")

  foo(input_ch)
  bar(input_ch)
}

1.1.4. Run it

Use the the following command to execute the example:

nextflow run patterns/channel-duplication.nf

2. Scatter executions

2.1. Process per file path

2.1.1. Problem

You need to execute a task for each file that matches a glob pattern.

2.1.2. Solution

Use the Channel.fromPath method to create a channel emitting all files matching the glob pattern. Then, use the channel as input of the process implementing your task.

2.1.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
process foo {
  debug true
  input:
  path x

  script:
  """
  echo your_command --input $x
  """
}

workflow {
  foo("$baseDir/data/reads/*_1.fq.gz")
}

2.1.4. Run it

Use the the following command to execute the example:

nextflow run patterns/process-per-file-path.nf

2.2. Process per file chunk

2.2.1. Problem

You need to split one or more input files into chunks and execute a task for each of them.

2.2.2. Solution

Use the splitText operator to split a file into chunks of a given size. Then use the resulting channel as input for the process implementing your task.

Chunks are kept in memory by default. When splitting big files, specify the parameter file: true to save the chunks into files. See the documentation for details.

Splitter for specific file formats are available, e.g. splitFasta and splitFastq.

2.2.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
params.infile = "$baseDir/data/poem.txt"
params.size = 5

process foo {
  debug true
  input:
  file x

  script:
  """
  rev $x | rev
  """
}

workflow {
  Channel.fromPath(params.infile) \
    | splitText(by: params.size) \
    | foo
}

2.2.4. Run it

Use the the following command to execute the example:

nextflow run patterns/process-per-file-chunk.nf

2.3. Process per file pairs

2.3.1. Problem

You need to process the files in a directory, grouping them by pairs.

2.3.2. Solution

Use the Channel.fromFilePairs method to create a channel that emits file pairs matching a glob pattern. The pattern must match a common prefix in the paired file names.

The matching files are emitted as tuples in which the first element is the grouping key of the matching files and the second element is the file pair itself.

2.3.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
process foo {
  debug true

  input:
  tuple val(sampleId), file(reads)

  script:
  """
  echo your_command --sample $sampleId --reads $reads
  """
}

workflow {
  Channel.fromFilePairs("$baseDir/data/reads/*_{1,2}.fq.gz", checkIfExists:true) \
    | foo
}

2.3.4. Run it

nextflow run patterns/process-per-file-pairs.nf

2.3.5. Custom grouping strategy

When necessary, it is possible to define a custom grouping strategy. A common use case is for alignment BAM files (sample1.bam) that come along with their index file. The difficulty is that the index is sometimes called sample1.bai and sometimes sample1.bam.bai depending on the software used. The following example can accommodate both cases.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
process foo {
  debug true
  tag "$sampleId"

  input:
  tuple val(sampleId), file(bam)

  script:
  """
  echo your_command --sample ${sampleId} --bam ${sampleId}.bam
  """
}

workflow {
  Channel.fromFilePairs("$baseDir/data/alignment/*.{bam,bai}", checkIfExists:true) { file -> file.name.replaceAll(/.bam|.bai$/,'') } \
    | foo
}

2.3.6. Run it

nextflow run patterns/process-per-file-pairs-custom.nf

2.4. Process per file range

2.4.1. Problem

You need to execute a task over two or more series of files having a common index range.

2.4.2. Solution

Use the from method to define the range over which to repeat the task execution, then chain it with the map operator to associate each index with the corresponding input files. Finally, use the resulting channel as input for the process.

2.4.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
process foo {
  debug true
  tag "$sampleId"

  input:
  tuple val(sampleId), file(indels), file(snps)

  """
  echo foo_command --this $indels --that $snps
  """
}

workflow {
  Channel.from(1..23) \
    | map { chr -> ["sample${chr}", file("/some/path/foo.${chr}.indels.vcf"), file("/other/path/foo.snvs.${chr}.vcf")] } \
    | foo
}

2.4.4. Run it

nextflow run patterns/process-per-file-range.nf

2.5. Process per CSV record

2.5.1. Problem

You need to execute a task for each record in one or more CSV files.

2.5.2. Solution

Read the CSV file line-by-line using the splitCsv operator, then use the map operator to return a tuple with the required field for each line and convert any string path to a file path object using the file function. Finally use the resulting channel as input for the process.

2.5.3. Code

Given the file index.csv with the following content:

sampleId read1 read2

FC816RLABXX

reads/110101_I315_FC816RLABXX_L1_HUMrutRGXDIAAPE_1.fq.gz

reads/110101_I315_FC816RLABXX_L1_HUMrutRGXDIAAPE_2.fq.gz

FC812MWABXX

reads/110105_I186_FC812MWABXX_L8_HUMrutRGVDIABPE_1.fq.gz

reads/110105_I186_FC812MWABXX_L8_HUMrutRGVDIABPE_2.fq.gz

FC81DE8ABXX

reads/110121_I288_FC81DE8ABXX_L3_HUMrutRGXDIAAPE_1.fq.gz

reads/110121_I288_FC81DE8ABXX_L3_HUMrutRGXDIAAPE_2.fq.gz

FC81DB5ABXX

reads/110122_I329_FC81DB5ABXX_L6_HUMrutRGVDIAAPE_1.fq.gz

reads/110122_I329_FC81DB5ABXX_L6_HUMrutRGVDIAAPE_2.fq.gz

FC819P0ABXX

reads/110128_I481_FC819P0ABXX_L5_HUMrutRGWDIAAPE_1.fq.gz

reads/110128_I481_FC819P0ABXX_L5_HUMrutRGWDIAAPE_2.fq.gz

This workflow parses the file and executes a process for each line:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
params.index = "$baseDir/data/index.csv"

process foo {
    debug true
    input:
    tuple val(sampleId), file(read1), file(read2)

    script:
    """
    echo your_command --sample $sampleId --reads $read1 $read2
    """
}

workflow {
    Channel.fromPath(params.index) \
        | splitCsv(header:true) \
        | map { row-> tuple(row.sampleId, file(row.read1), file(row.read2)) } \
        | foo
}
Relative paths are resolved by the file function against the execution directory. In practice, it is preferable to use absolute file paths.

2.5.4. Run it

Use the the following command to execute the example:

nextflow run patterns/process-per-csv-record.nf

2.6. Process per file output

2.6.1. Problem

A task in your workflow produces two or more files at time. A downstream task needs to process each of these files independently.

2.6.2. Solution

Use the flatten operator to transform the outputs of the upstream process to a channel that emits each file separately. Then use this channel as input for the downstream process.

2.6.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
process foo {
  output:
  path '*.txt'

  script:
  '''
  echo Hello there! > file1.txt
  echo What a beautiful day > file2.txt
  echo I hope you are having fun! > file3.txt
  '''
}

process bar {
  debug true
  input:
  path x

  script:
  """
  cat $x
  """
}

workflow {
  foo | flatten | bar
}

2.6.4. Run it

Use the the following command to execute the example:

nextflow run patterns/process-per-file-output.nf

3. Gather results

3.1. Process all outputs altogether

3.1.1. Problem

You need to process all the outputs of an upstream task altogether.

3.1.2. Solution

Use the collect operator to gather all the outputs produced by the upstream task and emit them as a single output. Then use the resulting channel as input for the downstream task.

3.1.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
process foo {
  input:
  path x
  output:
  path 'file.fq'
  script:
  """
  < $x zcat > file.fq
  """
}

process bar {
  debug true
  input:
  path '*.fq'
  script:
  """
  cat *.fq | head -n 50
  """
}

workflow {
  Channel.fromPath("$baseDir/data/reads/*_1.fq.gz", checkIfExists: true) \
    | foo \
    | collect \
    | bar
}

3.1.4. Run it

Use the the following command to execute the example:

nextflow run patterns/process-collect.nf

3.2. Process outputs into groups

3.2.1. Problem

You need to process in the same batch all files that have a matching key in the file name.

3.2.2. Solution

Use the map operator to associate each file with a key extracted from the file name. Then chain the resulting channel with the groupTuple operator to group together all files that have a matching key. Finally, use the resulting channel as input for the process.

3.2.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
params.reads = "$baseDir/data/reads/*"

process foo {
  debug true
  input:
  tuple val(key), file(samples)

  script:
  """
  echo your_command --batch $key --input $samples
  """
}

workflow {
  Channel.fromPath(params.reads, checkIfExists:true) \
    | map { file ->
      def key = file.name.toString().tokenize('_').get(0)
      return tuple(key, file)
    } \
    | groupTuple() \
    | foo
}

3.2.4. Run it

nextflow run patterns/process-into-groups.nf

3.3. Collect outputs into a file

3.3.1. Problem

You need to concatenate into a single file all output files produced by an upstream process.

3.3.2. Solution

Use the collectFile operator to merge all the output files into a single file.

3.3.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
process foo {
  input:
  path x
  output:
  path 'file.fq'
  script:
  """
  < $x zcat > file.fq
  """
}

workflow {
  Channel.fromPath("$baseDir/data/reads/*_1.fq.gz", checkIfExists: true) \
    | foo \
    | collectFile \
    | view
}

3.3.4. Run it

Use the the following command to execute the example:

nextflow run patterns/collect-into-file.nf

4. Organize outputs

4.1. Store process outputs

4.1.1. Problem

You need to store the outputs of one or more processes into a directory structure of your choice.

4.1.2. Solution

Use the publishDir directive to define a custom directory where the process outputs should be saved.

4.1.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
params.reads = "$baseDir/data/reads/*{1,2}.fq.gz"
params.outdir = 'my-results'

process foo {
  publishDir "$params.outdir/$sampleId"
  input:
  tuple val(sampleId), file(samples)
  output:
  path '*.fq'

  script:
  """
  < ${samples[0]} zcat > sample_1.fq
  < ${samples[1]} zcat > sample_2.fq
  """
}

workflow {
  Channel.fromFilePairs(params.reads, checkIfExists: true) \
    | foo
}

4.1.4. Run it

Run the script with the following command:

nextflow run patterns/publish-process-outputs.nf

4.2. Store outputs matching a glob pattern

4.2.1. Problem

A task in your workflow creates many output files that are required by a downstream task. You want to store some of those files into separate directories depending on the file name.

4.2.2. Solution

Use two or more publishDir directives to publish the output files into separate paths. For each directive specify a different glob pattern using the option pattern to store into each directory only the files that match the provided pattern.

4.2.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
params.reads = "$baseDir/data/reads/*_{1,2}.fq.gz"
params.outdir = 'my-results'

process foo {
  publishDir "$params.outdir/$sampleId/counts", pattern: "*_counts.txt"
  publishDir "$params.outdir/$sampleId/outlooks", pattern: '*_outlook.txt'
  publishDir "$params.outdir/$sampleId/", pattern: '*.fq'

  input:
    tuple val(sampleId), file('sample1.fq.gz'), file('sample2.fq.gz')
  output:
    path "*"
  script:
    """
    < sample1.fq.gz zcat > sample1.fq
    < sample2.fq.gz zcat > sample2.fq

    awk '{s++}END{print s/4}' sample1.fq > sample1_counts.txt
    awk '{s++}END{print s/4}' sample2.fq > sample2_counts.txt

    head -n 50 sample1.fq > sample1_outlook.txt
    head -n 50 sample2.fq > sample2_outlook.txt
    """
}

workflow {
  Channel.fromFilePairs(params.reads, checkIfExists: true, flat: true) \
    | foo
}

4.2.4. Run it

Run the script with the following command:

nextflow run patterns/publish-matching-glob.nf

4.3. Rename process outputs

4.3.1. Problem

You need to save the outputs of a process to a directory, giving each file a name of your choice.

4.3.2. Solution

The publishDir allows you to save the process outputs in a directory of your choice.

Specify the saveAs parameter to give each file a name of your choice, providing a custom rule as a closure.

4.3.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
process foo {
  publishDir 'results', saveAs: { filename -> "foo_$filename" }

  output:
  path '*.txt'

  '''
  touch this.txt
  touch that.txt
  '''
}

workflow {
  foo()
}

4.3.4. Run it

nextflow run patterns/publish-rename-outputs.nf

4.3.5. Save outputs in a sub-directory

The same pattern can be used to store specific files in separate directories depending on the actual name.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
process foo {
  publishDir 'results', saveAs: { filename -> filename.endsWith(".zip") ? "zips/$filename" : filename }

  output:
  path '*'

  '''
  touch this.txt
  touch that.zip
  '''
}

workflow {
  foo()
}
Relative paths are resolved against the publishDir store path. Use an absolute path to store files in a directory outside the publishDir store path.

4.3.6. Run it

nextflow run patterns/publish-rename-outputs-subdirs.nf

5. Other

5.1. Get process work directory

5.1.1. Problem

A tool needs the explicit path of the current task work directory.

5.1.2. Solution

Use the $PWD Bash variable or the pwd command to retrieve the task working directory path.

Make sure to escape the $ variable placeholder when the command script is enclosed in double quote characters.

5.1.3. Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
process foo {
  debug true
  script:
  """
  echo foo task path: \$PWD
  """
}

process bar {
  debug true
  script:
  '''
  echo bar task path: $PWD
  '''
}

workflow {
  foo()
  bar()
}

5.1.4. Run it

The command run the script with an empty channel:

nextflow run patterns/process-get-workdir.nf

Use the following command to provide the same script some input files, that prevents the process from being executed:

nextflow run patterns/process-get-workdir.nf --inputs ../data/prots/\*

5.2. Ignore failing process

5.2.1. Problem

A task is expected to fail in some cases. You want to ignore the failure and continue the execution of the remaining tasks in the workflow.

5.2.2. Solution

Use the process directive errorStrategy 'ignore' to ignore the error condition.

5.2.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
process foo {
  errorStrategy 'ignore'
  script:
  '''
  echo This is going to fail!
  exit 1
  '''
}

process bar {
  script:
  '''
  echo OK
  '''
}

workflow {
  foo()
  bar()
}

5.2.4. Run it

Run the script with the following command:

nextflow run patterns/ignore-failing-process.nf

5.3. State dependency

5.3.1. Problem

You need to synchronize the execution of two processes for which there isn’t a data dependency, so that process bar is executed after the completion of process foo.

5.3.2. Solution

Add an output channel to process foo that produces a ready signal. Then pass this channel as input to process bar in order to trigger its execution when foo completes.

5.3.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
process foo {
    output:
    val true
    script:
    """
    echo your_command_here
    """
}

process bar {
    input:
    val ready
    path fq
    script:
    """
    echo other_commad_here --reads $fq
    """
}

workflow {
    reads_ch = Channel.fromPath("$baseDir/data/reads/11010*.fq.gz", checkIfExists:true)

    foo()
    bar(foo.out, reads_ch)
}

5.3.4. Run it

Run the example using this command:

nextflow run patterns/state-dependency.nf

6. Advanced patterns

6.1. Conditional resources definition

6.1.1. Problem

A task in your workflow needs to use some amount of computing resources (e.g. memory) that depends on the size or the name of one or more input files.

6.1.2. Solution

Declare the resource requirements (memory, cpus, etc.) in a dynamic manner using a closure.

The closure computes the required amount of resources using the file attributes (e.g. size) of the inputs declared in the process definition.

6.1.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
process foo {
    memory { reads.size() < 70.KB ? 1.GB : 5.GB }

    input:
    path reads

    """
    echo your_command_here --in ${reads} --mem=${task.memory.giga}
    """
}

workflow {
    Channel.fromPath("$baseDir/data/reads/*_1.fq.gz", checkIfExists:true) \
        | foo
}

6.1.4. Run it

nextflow run patterns/conditional-resources.nf

6.2. Conditional process executions

6.2.1. Problem

One of two different tasks should be executed based on some condition, and a third task should process the results of the selected task.

6.2.2. Solution

Simply execute either process using if/else statements on the condition. Define a channel, e.g. omega_ch, which emits the output of the selected process in each case. Then, execute the third process with this output channel.

Or, use a ternary expression and a pipe to keep things short and sweet.

6.2.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
params.flag = false

process foo {
  output:
  path 'x.txt'

  script:
  '''
  echo foo > x.txt
  '''
}

process bar {
  output:
  path 'x.txt'

  script:
  '''
  echo bar > x.txt
  '''
}

process omega {
  debug true
  input:
  path x

  script:
  """
  cat $x
  """
}

workflow {
  // the long way
  if ( params.flag ) {
    bar()
    omega_ch = bar.out
  }
  else {
    foo()
    omega_ch = foo.out
  }

  omega(omega_ch)

  // the short way
  (params.flag ? bar : foo) | omega
}

6.2.4. Run it

Use the the following command to execute the example:

nextflow run patterns/conditional-process.nf

The processes foo and omega are executed. Run the same command with the --flag command line option.

nextflow run patterns/conditional-process.nf --flag

This time the processes bar and omega are executed.

6.2.5. Alternative solution

Create an input channel for each process that is either populated with data or an empty channel. Each process will execute only if its input channel has data.

Then use the mix operator to create a new channel that emits the outputs produced by the two processes, and use it as the input for the third process.

6.2.6. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
params.flag = false

process foo {
  input:
  val x

  output:
  path 'x.txt'

  script:
  """
  echo $x > x.txt
  """
}

process bar {
  input:
  val(b)

  output:
  path 'x.txt'

  script:
  """
  echo $b > x.txt
  """
}

process omega {
  debug true
  input:
  path x

  script:
  """
  cat $x
  """
}

workflow {
  (foo_ch, bar_ch) = params.flag
    ? [ Channel.empty(), Channel.from(1,2,3) ]
    : [ Channel.from(4,5,6), Channel.empty() ]

  foo(foo_ch)
  bar(bar_ch)

  foo.out | mix(bar.out) | omega
}

6.2.7. Run it

nextflow run patterns/conditional-process2.nf

6.3. Skip process execution

6.3.1. Problem

You have two sequential tasks in your workflow. When an optional flag is specified, the first task should be skipped and its input(s) should be processed by the second task.

6.3.2. Solution

Use an empty channel, created in a conditional expression, to skip the first process execution when an optional parameter is specified.

Then, define the second process input as a mix of the first process output (when executed) and the input channel.

6.3.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
params.skip = false
params.input = "$baseDir/data/reads/sample.fq.gz"

process foo {
  input:
  path x

  output:
  file('*.fastq')

  script:
  """
  < $x zcat > ${x.simpleName}.fastq
  """
}

process bar {
  debug true

  input:
  path x

  script:
  """
  echo your_command --input $x
  """
}

workflow {
  input_ch = Channel.fromPath(params.input)

  (foo_ch, bar_ch) = params.skip
    ? [Channel.empty(), input_ch]
    : [input_ch, Channel.empty()]

  foo_ch | foo | mix(bar_ch) | bar
}

6.3.4. Run it

Use the the following command to execute the example:

nextflow run patterns/skip-process-execution.nf

The processes foo and bar are executed. Run the same command with the --skip command line option:

nextflow run patterns/skip-process-execution.nf --skip

This time only the bar process is executed. = Feedback loop

6.3.5. Problem

You need to repeat a process or workflow multiple times, using the output from the previous iteration as the input to the next iteration.

6.3.6. Solution

This feature is experimental and may change in the future.

Use the recurse method on a process or workflow to execute it iteratively. In order to use this feature, the process or workflow must have identical input and output definitions, and any initial values must be Groovy values or value channels — queue channels are not supported (yet).

You can use the times operator to perform a fixed number of iterations, or the until operator to iterate until some condition is satisfied.

6.3.7. Code

For an iterative process:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
nextflow.preview.recursion=true

params.data = "$baseDir/data/hello.txt"

process foo {
  input:
    path 'input.txt'
  output:
    path 'result.txt'
  script:
    """
    cat input.txt > result.txt
    echo "Task ${task.index} was here" >> result.txt
    """
}

workflow {
  // perform a fixed number of iterations
  foo
    .recurse(file(params.data))
    .times(10)

  // iterate until some condition is satisfied
  foo
    .recurse(file(params.data))
    .until { it -> it.size() > 100 }

  foo
    .out
    .view(it -> it.text)
}

For an iterative workflow:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
nextflow.preview.recursion=true

params.input = "$baseDir/data/hello.txt"

process tick {
  input:
    path 'input.txt'
  output:
    path 'result.txt'
  script:
    """
    cat input.txt > result.txt
    echo "Task ${task.index} : tick" >> result.txt
    """
}

process tock {
  input:
    path 'input.txt'
  output:
    path 'result.txt'
  script:
    """
    cat input.txt > result.txt
    echo "Task ${task.index} : tock" >> result.txt
    """
}

workflow clock {
  take: infile
  main:
    infile | tick | tock
  emit:
    tock.out
}

workflow {
  clock
    .recurse(file(params.input))
    .until { it -> it.size() > 100 }

  clock
    .out
    .view(it -> it.text)
}

6.3.8. Run it

Use the the following command to execute the example:

# iterative process
nextflow run patterns/feedback-loop-process.nf

# iterative workflow
nextflow run patterns/feedback-loop-workflow.nf

6.4. Optional input

6.4.1. Problem

One or more processes have an optional input file.

6.4.2. Solution

Use a special file name to mark the absence of the file parameter.

6.4.3. Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
params.inputs = "$baseDir/data/prots/*{1,2,3}.fa"
params.filter = 'NO_FILE'

process foo {
  debug true
  input:
  path seq
  path opt

  script:
  def filter = opt.name != 'NO_FILE' ? "--filter $opt" : ''
  """
  echo your_commad --input $seq $filter
  """
}

workflow {
  prots_ch = Channel.fromPath(params.inputs, checkIfExists:true)
  opt_file = file(params.filter)

  foo(prots_ch, opt_file)
}

6.4.4. Run it

Run the script with the following command:

nextflow run patterns/optional-input.nf

Run the same script providing an optional file input:

nextflow run patterns/optional-input.nf --filter foo.txt

6.5. Optional output

6.5.1. Problem

A task in your workflow is expected to not create an output file in some circumstances.

6.5.2. Solution

Declare such output as an optional file.

6.5.3. Code

1
2
3
4
5
6
7
8
9
process foo {
  output:
  path 'foo.txt', optional: true

  script:
  '''
  your_command
  '''
}

6.5.4. Run it

Use the the following command to execute the example:

nextflow run patterns/optional-output.nf

6.6. Execute when empty

6.6.1. Problem

You need to execute a process if a channel is empty.

6.6.2. Solution

Use the ifEmpty operator to emit a marker value to trigger the execution of the process.

6.6.3. Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
params.inputs = ''

process foo {
  debug true
  input:
  val x
  when:
  x == 'EMPTY'

  script:
  '''
  echo hello
  '''
}

workflow {
  reads_ch = params.inputs
    ? Channel.fromPath(params.inputs, checkIfExists:true)
    : Channel.empty()

  reads_ch \
    | ifEmpty { 'EMPTY' } \
    | foo
}

6.6.4. Run it

Use the following command to run the script with an empty channel:

nextflow run patterns/process-when-empty.nf

Use the following command to provide the same script some input files, which prevents the process from being executed:

nextflow run patterns/process-when-empty.nf --inputs ../data/prots/\*