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.

libBIDS.sh integration

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 installationgetPluginLibPath() inspects the class-loader URL for the LibBidsShWrapper.class resource, extracts the plugin directory from the path (matching nf-bids-{version}), and returns {pluginDir}/lib/libBIDS.sh. If classloader introspection fails, it falls back to scanning ~/.nextflow/plugins/ for nf-bids-* directories and picking the lexicographically latest version.

2

lib/libBIDS.sh — relative to the current working directory.

3

libBIDS.sh/libBIDS.sh — the git submodule in the current directory.

4

../libBIDS.sh/libBIDS.sh — submodule one level up.

5

../../libBIDS.sh/libBIDS.sh — submodule two levels up.

6

../../../libBIDS.sh/libBIDS.sh — submodule three levels up (covers the validation/ subdirectory used in integration tests).

7

/usr/local/bin/libBIDS.sh — system-wide install.

8

~/.local/bin/libBIDS.sh — user-local install (tilde is expanded at runtime).

9 (lowest)

libBIDS.sh/libBIDS.sh relative to System.getProperty("user.dir").

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:

  1. The file exists.

  2. The file is readable.

  3. 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