The libBIDS.sh Dependency
libBIDS.sh is a battle-tested bash library that traverses a BIDS directory tree and emits a structured TSV.
This page explains how the library enters the project, how Gradle bundles it into the plugin JAR, and how LibBidsShWrapper locates and executes it at runtime.
What libBIDS.sh Does
libBIDS.sh (version v3.0, hosted at CoBrALab/libBIDS.sh) is a pure-bash BIDS parsing library developed by the CoBrALab.
Its central function, libBIDSsh_parse_bids_to_table, walks a BIDS dataset, reads file names and sidecars, and writes a TSV with columns:
derivatives\tdatatype\tsubject\ttemplate\tsession\tcohort\tsample\ttask\ttracksys\tacquisition\tnucleus\tvolume\tceagent\ttracer\tstain\treconstruction\tdirection\trun\tmodality\techo\tflip\tinversion\tmtransfer\tpart\tprocessing\themisphere\tspace\tsplit\trecording\tchunk\tatlas\tsegmentation\tscale\tresolution\tdensity\tlabel\tdescription\tsuffix\textension\tpath
Each row represents one file. The nf-bids plugin uses this TSV as its only source of BIDS file inventory — no Java-side directory walking occurs.
Vendoring as a Git Submodule
libBIDS.sh is included as a git submodule under the path libBIDS.sh/ at the repository root:
# .gitmodules
[submodule "libBIDS.sh"]
path = libBIDS.sh
url = https://github.com/CoBrALab/libBIDS.sh.git
The submodule pins a specific commit of the upstream library, giving reproducible builds and controlled upgrades. After a fresh clone, the submodule must be initialised before the plugin can function:
git submodule update --init
If the submodule is not initialised, the plugin will still start, but any call to Channel.fromBIDS() will fail at the script-discovery step with a FileNotFoundException that includes the above instruction.
See Installation Guide for the full setup sequence.
Bundling by Gradle
libBIDS.sh is included in the compiled plugin artifact so that users never need to install it separately.
The relevant declaration in build.gradle is:
nextflowPlugin {
dependencies {
implementation fileTree('libBIDS.sh') { include '*.sh' }
}
}
The Gradle nextflow-plugin build plugin (version 1.0.0-beta.10) copies all *.sh files from the libBIDS.sh/ submodule directory into the lib/ directory of the assembled plugin ZIP.
When the plugin is installed under ~/.nextflow/plugins/nf-bids-{version}/, the shell script is available at:
~/.nextflow/plugins/nf-bids-{version}/lib/libBIDS.sh
This embedded copy is the highest-priority discovery target at runtime.
Runtime Script Discovery
LibBidsShWrapper.parseBidsToTable() resolves the script path before executing the parser.
If the caller passes options.libbids_sh, that explicit path is used directly (after path validation).
Otherwise, findLibBidsScript() probes a priority-ordered list of locations and returns the first match.
Discovery Priority Order
| Priority | Location probed |
|---|---|
1 (highest) |
Plugin installation — |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 (lowest) |
|
The first path for which File.exists() && File.canRead() returns true is used.
Override via options.libbids_sh
Any location in the list can be bypassed by setting the libbids_sh option:
Channel.fromBIDS(
'/data/my_bids',
'bids2nf.yaml',
[libbids_sh: '/opt/custom/libBIDS.sh']
)
This is useful in containerised environments where the embedded copy is not accessible, or when testing against a development branch of libBIDS.sh.
Path Validation and Injection Prevention
Before any command is built, both the script path and the BIDS directory path are validated by validateShellPath():
private void validateShellPath(String path, String description) {
def dangerousChars = /[;&|$`()<>"'\\]/
if (path =~ dangerousChars) {
throw new IllegalArgumentException(
"${description} contains dangerous characters: ${path}"
)
}
if (path =~ /[\n\r\t]/) {
throw new IllegalArgumentException(
"${description} contains control characters"
)
}
}
Characters permitted: alphanumeric, /, ., -, _, ~, and space.
Any other character (shell metacharacters) causes an immediate IllegalArgumentException.
Command Construction and Execution
The parser is invoked using the array form of Process.execute() to avoid string-interpolation shell expansion:
['bash', '-c',
'set -euo pipefail && source "$1" && libBIDSsh_parse_bids_to_table "$2" > "$3"',
'bash', // $0 — interpreter name
scriptPath, // $1 — sourced library
bidsDir, // $2 — BIDS root
outputFile.absolutePath // $3 — output TSV
]
set -euo pipefail ensures the bash subshell exits on any error, unset variable, or pipe failure.
The plugin waits for the process to complete via process.waitFor(), captures stdout and stderr separately, and throws a RuntimeException if the exit code is non-zero or if the output TSV is empty.
Script Integrity Verification
LibBidsShWrapper.validateLibBidsScript() checks three conditions before trusting a script:
-
The file exists.
-
The file is readable.
-
The file content contains the string
libBIDSsh_parse_bids_to_table(the expected entry point).
This guards against accidentally pointing the plugin at an unrelated shell script.
Cross-Reference
-
Runtime Entry Path — how the wrapper fits in the full call chain
-
Installation Guide — submodule initialisation steps
-
Dependency Matrix — libBIDS.sh version alongside all other dependencies