-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
[Feature Request] Add nul delimited output support #1218
Description
Yes, filenames with newlines are cursed and generally should not be used ever, but sometimes you have to be sure operations on paths work reliably. find, grep, perl, xargs, fd, rg, dirname, basename, realpath, readlink and pretty much any established tool that deals with linux paths can consume and/or produce nul delimited output. xz works fantastically as a glue with business logic that hooks into native binaries piggy-backing on their performance, so sometimes using JS glob is just not the preferable way to implement a script. We have .json(), .lines(), .text(), .blob(), .buffer(), [Symbol.asyncIterator] so maybe this list can be extended with something like this:
type ProcessOutput = {
nulLines: (() => Promise<string[]>) & { [Symbol.asyncIterator](): AsyncIterator<string> }
};Currently the only options are either to allocate (potentially) enourmous string and split by nul bytes or reimplement an async iterator from scratch every time:
import { Readable } from 'node:stream';
import 'zx/globals';
const root = import.meta.dirname;
cd(root);
$.verbose = true;
await $`mkdir -p testdir`;
await $`touch ${'testdir/absolutely\nlegitemate\nlinux\nfilename'}`;
await $`touch testdir/justafile`;
const entries = await $`find ./testdir -type f -print0`.lines();
console.log(entries.length); // 4
const split = await $`find ./testdir -type f -print0`
.text()
.then((v) => v.slice(0, -1).split('\0'));
console.log(split.length); // 2
async function* nulIterator(stream: Readable) {
let buf = Buffer.alloc(0);
for await (const chunk of stream) {
buf = Buffer.concat([buf, chunk]);
let idx;
while ((idx = buf.indexOf(0)) !== -1) {
yield buf.subarray(0, idx).toString();
buf = buf.subarray(idx + 1);
}
}
if (buf.length) yield buf.toString();
}
for await (const entry of nulIterator(
$`find ./testdir -type f -print0`.stdout,
)) {
console.log(entry); // 2 invocations
}This may not fit the size constraints and/or the vision of the package, so (obviously) feel free to close as wontfix