UNPKG

parallel

Version:

CLI tool to execute shell commands in parallel, based on GNU parallel command

119 lines (105 loc) 3.06 kB
const util = require('./util'), opts = require('./opts'), jobs = require('./jobs'), stream = require('stream') const NEW_LINE = '\n' exports.open = function() { // Check if command contains {##} placeholder const hasTotal = opts._.some(arg => arg.includes('{##}')) const needsBuffering = opts.shuf || hasTotal // If new line, support with and without carriage return const sep = opts.delimiter === NEW_LINE ? /\r?\n/ : new RegExp(opts.delimiter) let lines = needsBuffering ? [] : null // Buffer when shuffling or when {##} is used let src = opts.argFile, buf = '' src.setEncoding('utf8') src.on('error', util.fatal) // Handle block-based input for pipe mode if (opts.block) { let blockBuffer = '' src.on('data', function (chunk) { blockBuffer += chunk while (blockBuffer.length >= opts.block) { const block = blockBuffer.slice(0, opts.block) blockBuffer = blockBuffer.slice(opts.block) jobs.processLine(block) } }) src.on('end', function () { if (blockBuffer) { jobs.processLine(blockBuffer) } jobs.close() }) return } src.on('data', function(chunk) { if (sep.test(chunk)) { const buffer = (buf + chunk).split(sep) buf = buffer.pop() if (lines) { // Collect lines for shuffling lines.push.apply(lines, buffer) } else { // Process lines immediately buffer.forEach(jobs.processLine) } } else { buf += chunk } }) src.once('end', function() { if (buf) { if (lines) { lines.push(buf) } else { jobs.processLine(buf) } buf = '' } if (lines) { // Calculate total jobs when we have all buffered lines if (lines.length > 0) { const jobs = opts.maxArgs || opts.jobs opts._totalJobs = Math.ceil(lines.length / jobs) } if (opts.shuf) { lines.sort(() => Math.random() - 0.5) } lines.forEach(jobs.processLine) } jobs.close() }) // When nothing is piped, stdin needs end() closed to be released if (src === process.stdin && process.stdin.isTTY) { src.end() } } // Input provided through command-line with ::: is placed first exports.setInlineInput = function(inputs) { const perms = generatePermutations(inputs) const data = perms.join(opts.delimiter) + opts.delimiter // Create a readable stream from the inline input data const inputStream = new stream.Readable() inputStream.push(data) inputStream.push(null) // End the stream // Replace the input source opts.argFile = inputStream } function generatePermutations(inputs) { const perms = [] let max = inputs.reduce((m, input) => m * input.length, 1) while (max--) { let rest = max const cols = [] let i = inputs.length while (i--) { const input = inputs[i] const l = input.length cols.unshift(input[rest % l]) rest = Math.floor(rest / l) } perms.unshift(cols.join(opts.colsep)) } return perms }