2.3. 表达式#
If you need to manipulate input parameters, include the requirement
InlineJavascriptRequirement
and then anywhere a parameter reference is
legal you can provide a fragment of Javascript that will be evaluated by
the CWL runner.
重要
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 /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'expression.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/expression.cwl'
INFO [job expression.cwl] /tmp/wpdpu0or$ echo \
-A \
2 \
-B \
baz \
-C \
10 \
9 \
8 \
7 \
6 \
5 \
4 \
3 \
2 \
1 > /tmp/wpdpu0or/output.txt
INFO [job expression.cwl] completed success
{
"example_out": {
"location": "file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/output.txt",
"basename": "output.txt",
"class": "File",
"checksum": "sha1$a739a6ff72d660d32111265e508ed2fc91f01a7c",
"size": 36,
"path": "/home/runner/work/user_guide/user_guide/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 表达式如同参数引用一样,只能用于某些特定字段,即:
-
arguments
valueFrom
stdin
stdout
stderr
-
format
secondaryFiles
来自
inputBinding
valueFrom
-
format
secondaryFiles
-
glob
outputEval
From
Workflow
来自 InputParameter 和 WorkflowOutputParameter
format
secondaryFiles
来自
steps
-
valueFrom
-
-
expression
来自 InputParameter 和 ExpressionToolOutputParameter
format
secondaryFiles
-
coresMin
coresMax
ramMin
ramMax
tmpdirMin
tmpdirMax
outdirMin
outdirMax
-
listing
来自 Dirent
entry
entryname
来自
EnvVarRequirement
-
envValue
-
2.3.1. 通过 expressionLib
使用外部库和内联 JavaScript 代码#
The requirement InlineJavascriptRequirement
supports an expressionLib
attribute
that allows users to load external JavaScript files, or to provide inline JavaScript
code.
添加到 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 /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-inline.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib-inline.cwl'
INFO [job hello-world-expressionlib-inline.cwl] /tmp/tz92a84q$ 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 /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib-external.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib-external.cwl'
INFO [job hello-world-expressionlib-external.cwl] /tmp/9ndleaa4$ 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 /opt/hostedtoolcache/Python/3.9.19/x64/bin/cwltool 3.1.20240508115724
INFO Resolved 'hello-world-expressionlib.cwl' to 'file:///home/runner/work/user_guide/user_guide/src/_includes/cwl/expressions/hello-world-expressionlib.cwl'
INFO [job hello-world-expressionlib.cwl] /tmp/ro3728en$ echo \
'Hello World'
Hello World
INFO [job hello-world-expressionlib.cwl] completed success
{}INFO Final process status is success
备注
$include
语句可以用来添加位于本地或远程的文件,并且接受相对路径和绝对路径。详情请参见 CWL 规约中关于 $include
的内容。