2.3. 表达式#
如果需要对输入参数进行处理,请添加需求 InlineJavascriptRequirement. 然后,在任何可以合法引用参数的地方,您都可以提供一 JavaScript 片段,让 CWL 运行程序计算其值。
重要
JavaScript 表达式应当仅在绝对必要时使用。要处理文件名、扩展名、路径等,请优先考虑能否使用 basename、nameroot、nameext 等 File 内置属性。对此,请参阅优良习惯一览。
expression.cwl##!/usr/bin/env cwl-runner
cwlVersion: v1.2
class: CommandLineTool
baseCommand: echo
requirements:
InlineJavascriptRequirement: {}
inputs: []
outputs:
example_out:
type: stdout
stdout: output.txt
arguments:
- prefix: -A
valueFrom: $(1+1)
- prefix: -B
valueFrom: $("/foo/bar/baz".split('/').slice(-1)[0])
- prefix: -C
valueFrom: |
${
var r = [];
for (var i = 10; i >= 1; i--) {
r.push(i);
}
return r;
}
由于此工具不需要任何 inputs(输入),因此运行的时候可以使用一个几乎为空的作业文件:
empty.yml#{}
empty.yml 的内容是一个空 JSON 对象的描述。JSON 对象描述是用大括号 {} 围起来的,因此,表示空对象仅需一对空括号就够了。
然后我们可以运行 expression.cwl:
expression.cwl#$ cwltool expression.cwl empty.yml
INFO /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/envs/latest/bin/cwltool 3.2.20260411152607
INFO Resolved 'expression.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/expression.cwl'
INFO [job expression.cwl] /tmp/ywd57jq_$ echo \
-A \
2 \
-B \
baz \
-C \
10 \
9 \
8 \
7 \
6 \
5 \
4 \
3 \
2 \
1 > /tmp/ywd57jq_/output.txt
INFO [job expression.cwl] completed success
{
"example_out": {
"location": "file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/output.txt",
"basename": "output.txt",
"class": "File",
"checksum": "sha1$a739a6ff72d660d32111265e508ed2fc91f01a7c",
"size": 36,
"path": "/home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/output.txt"
}
}INFO Final process status is success
$ cat output.txt
-A 2 -B baz -C 10 9 8 7 6 5 4 3 2 1
请注意,如上例所示,requirements(要求)可通过映射语法来提供:
requirements:
InlineJavascriptRequirement: {}
也可以作为一个数组,每个元素均用 - 标记(本例中只有一个 class: InlineJavascriptRequirement ),就和描述额外命令行参数的语法一样。
requirements:
- class: InlineJavascriptRequirement
哪里可以使用 JavaScript 表达式?
JavaScript 表达式如同参数引用一样,只能用于某些特定字段,即:
-
argumentsvalueFrom
stdinstdoutstderr-
formatsecondaryFiles来自
inputBindingvalueFrom
-
formatsecondaryFiles-
globoutputEval
来自
Workflow来自 InputParameter 和 WorkflowOutputParameter
formatsecondaryFiles
来自
steps-
valueFrom
-
-
expression来自 InputParameter 和 ExpressionToolOutputParameter
formatsecondaryFiles
-
coresMincoresMaxramMinramMaxtmpdirMintmpdirMaxoutdirMinoutdirMax
-
listing来自 Dirent
entryentryname
来自
EnvVarRequirement-
envValue
-
2.3.1. 通过 expressionLib 使用外部库和内联 JavaScript 代码#
InlineJavascriptRequirement 这一需求支持 expressionLib 属性,可以让用户加载外部 JavaScript 文件或提供内联 JavaScript 代码。
添加到 expressionLib 属性的条目将由 CWL 运行程序的 JavaScript 引擎进行语法分析。这可以用来纳入外部文件中的代码,或创建可在 CWL 文件其他部分调用的 JavaScript 函数。
备注
版本 1.0 到 1.2 的 CWL 标准中写道,在 CWL 表达式中唯一有效的 JavaScript 版本是 ECMAScript 5.1 . 也就是说,在 CWL 文件中纳入或写入的所有 JS 代码都必须符合该版本。
例如,我们可以使用 InlineJavascriptRequirements, 在 expressionLib 下编写内联 JavaScript 函数,让它能在 CWL 文件其他部分调用:
hello-world-expressionlib-inline.cwl#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- |
/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}
baseCommand: echo
inputs:
message:
type: string
arguments: [$( capitalizeWords(inputs.message) )]
outputs: []
运行此 CWL 工作流将调用这个 JavaScript 函数,使得 echo 命令以大写首字母排印输入的“报文”:
hello-world-expressionlib-inline.cwl.#$ cwltool hello-world-expressionlib-inline.cwl --message "hello world"
INFO /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/envs/latest/bin/cwltool 3.2.20260411152607
INFO Resolved 'hello-world-expressionlib-inline.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib-inline.cwl'
INFO [job hello-world-expressionlib-inline.cwl] /tmp/epe47qgu$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib-inline.cwl] completed success
{}INFO Final process status is success
我们来将 capitalizeWords 函数移动到外部文件 custom-functions.js, 并将它导入我们的 CWL 文件:
custom-functions.js#/**
* Capitalize each word passed. Will split the text by spaces.
* For instance, given "hello world", it returns "Hello World".
*
* @param {String} message - The input message.
* @return {String} the message with each word with its initial letter capitalized.
*/
function capitalizeWords (message) {
if (message === undefined || message === null || typeof message !== 'string' || message.trim().length === 0) {
return '';
}
return message
.split(' ')
.map(function (token) {
return token.charAt(0).toUpperCase() + token.slice(1);
})
.join(' ');
}
hello-world-expressionlib-external.cwl#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }
baseCommand: echo
inputs:
message:
type: string
arguments: [$( capitalizeWords(inputs.message) )]
outputs: []
文件 custom-functions.js 通过 $include: custom-functions.js 语句纳入到这个 CWL 文件里,使这个 JavaScript 文件中定义的函数和变量可在 CWL 文件其他部分使用。
hello-world-expressionlib-external.cwl.#$ cwltool hello-world-expressionlib-external.cwl --message "hello world"
INFO /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/envs/latest/bin/cwltool 3.2.20260411152607
INFO Resolved 'hello-world-expressionlib-external.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib-external.cwl'
INFO [job hello-world-expressionlib-external.cwl] /tmp/go_fwpz0$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib-external.cwl] completed success
{}INFO Final process status is success
最后请注意,CWL 文件中可以同时运用内联和外部 JavaScript 代码。下面是最后一个例子,这里我们向 expressionLib 属性中加入新函数 createHelloWorldMessage, 调用外部文件 custom-functions.js 中的 capitalizeWords 函数。
hello-world-expressionlib.cwl#cwlVersion: v1.2
class: CommandLineTool
requirements:
- class: InlineJavascriptRequirement
expressionLib:
- { $include: custom-functions.js }
- |
/**
* A merely illustrative example function that uses a function
* from the included custom-functions.js file to create a
* Hello World message.
*
* @param {Object} message - CWL document input message
*/
var createHelloWorldMessage = function (message) {
return capitalizeWords(message);
};
baseCommand: echo
inputs:
message:
type: string
arguments: [$( createHelloWorldMessage(inputs.message) )]
outputs: []
hello-world-expressionlib.cwl.#$ cwltool hello-world-expressionlib.cwl --message "hello world"
INFO /home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/envs/latest/bin/cwltool 3.2.20260411152607
INFO Resolved 'hello-world-expressionlib.cwl' to 'file:///home/docs/checkouts/readthedocs.org/user_builds/common-workflow-languageuser-guide-zh-hans/checkouts/latest/src/_includes/cwl/expressions/hello-world-expressionlib.cwl'
INFO [job hello-world-expressionlib.cwl] /tmp/66198n5k$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib.cwl] completed success
{}INFO Final process status is success
备注
$include 语句可以用来添加位于本地或远程的文件,并且接受相对路径和绝对路径。详情请参见 CWL 规约中关于 $include 的内容。