I'm trying to write a process that will operate on...
# help
a
I'm trying to write a process that will operate on an input file and produce an output file with a name created by replacing the extension of the input file with a new extension. In my small test inputs, the input is a channel containing 2 files. Before invoking the process I view() the channel and it looks right. However, in my process, which looks something like this:
Copy code
process foo {
    input:
        path inputBams
        val inputExtension
        val outputExtension
    output:
    path "${output_file}", emit: taggedAndTrimmedBams
    script:
    print("inputBams in foo: ${inputBams}")    
output_file = inputBams.replaceLast(inputExtension, outputExtension)
inputBams is not a channel and it is not a single file. It appears to be a BlankSeparatedList.
Copy code
inputBams in foo: SMRI_V11_rxn1.0.unmapped.bam SMRI_V11_rxn1.1.unmapped.bam
Execution cancelled -- Finishing pending tasks before exit
-[mccarroll/nextflow] Pipeline completed with errors-
ERROR ~ Error executing process > 'NEXTFLOW:align_locus_function_workflow:foo'

Caused by:
  No signature of method: nextflow.util.BlankSeparatedList.replaceLast() is applicable for argument types: (String, String) values: [unmapped.bam, unmapped_tagged_trimmed_filtered.bam]


All I want is for each input file <stuff>.unmapped.bam, produce an output file <stuff>.unmapped_tagged_trimmed_filtered.bam
What am I doing wrong?
t
What comes into the
process
is not (only) determined by the input parameters (although they are important and need to be compatible with how you use it), but how the
process
is invoked, because that's how you specify the channels that feed it. Are your extensions the same for each file? Because if they are, you don't necessarily need a separate channel for each, but could define those via
params
. (Using
params
in modules is frowned up, though, because it limits reuse of the module to pipelines with exactly that parameter. But it may still be helpful for testing.)
p
Since (as you note)
inputBams
is a list rather than a string, you'd need to call
replace
(I don't know what interfaces provide a
replaceLast
) on the
toString()
of each item separately, e.g.
Copy code
output_file = inputBams.collect( { it.toString().replace(inputExtension, outputExtension) })
but I don't know if a
path
output will be happy to receive a list. For most cases, it seems it should be possible to rather just do
Copy code
output_file = "*.${outputExtension}"
or just doing that directly in the
output
section. Assuming you really need to deal with different extensions at all.
a
Thanks, @pontus. That helps somewhat, in that I can do the file extension replacement now, but it's still not working. It looks like there is a problem in the way I am invoking the process, because when I print an input list of paths in another process, I get multiple print statements for each input file, whereas for this process all input files are passed to a single process instance. I'm pretty sure inputBams is a channel because I can call view() on it before invoking the process and a list of files in a work subdir are emitted. What's unusual about this inputBams channel is that the upstream process that produces it creates an arbitrary number of output files, so I use a glob:
Copy code
output:
    path "${libraryName}.[0-9]*.unmapped.bam", emit: splitBams
I'm guessing that there is something different about an output channel being produced this way. Tangentially, the reason I don't create the output file name in the
output:
section is that I want to refer to that value in the script itself. If I use a glob in the
output
section, I still need to create the filename somewhere.
p
I'm not sure I follow what you think is the problem or what shows it. Do you want a single process to get all inputs or do you want them passed once each? That's determined by your channel management before calling (i.e. you can have a channel emitting a single value being a list of many paths or a channel emitting multiple values with a single path each).
a
Woo-hoo! I just realized that the problem is that my upstream process with the glob produced a channel with a single item that was a list. Flattening it solved the problem. Thanks for not giving up on me!
🎉 1