diff options
Diffstat (limited to 'modules/core/core-bundle.js')
| -rw-r--r-- | modules/core/core-bundle.js | 62 |
1 files changed, 51 insertions, 11 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index 8d6cbda..6f00658 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -6575,7 +6575,12 @@ class FileSystemModule { const showLong = flags.some(f => f.includes('l')); const showHidden = flags.some(f => f.includes('a')); const files = this.terminal.fs.ls(target); - if (!files || files.length === 0) { this.terminal.print('(empty)', 'info'); return; } + if (!files || files.length === 0) { + // `ls somefile` used to print "(empty)": a file operand should name the file, + // which is also what makes `ls *.txt` (glob-expanded to several paths) readable. + if (target && this.terminal.fs.readFile(target) !== null) { this.terminal.print(target, 'info'); return; } + this.terminal.print('(empty)', 'info'); return; + } const filtered = showHidden ? files : files.filter(f => !f.name.startsWith('.')); if (filtered.length === 0) { this.terminal.print('(no visible files, use ls -a)', 'info'); return; } const maxName = Math.max(...filtered.map(f => f.name.length), 8); @@ -6615,12 +6620,19 @@ class FileSystemModule { docs.forEach(f => this.terminal.print(` ${f.name} ${f.size || 0} B`, 'info')); }; this.commands['cat'] = (args) => { - if(!args[0]) return this.terminal.print('Usage: cat <filename>', 'warning'); - const content = this.terminal.fs.readFile(args[0]); - if(content !== null && content !== undefined) { - if (content.length === 0) this.terminal.print('(empty file)', 'info'); - else this.terminal.print(content); - } else this.terminal.print('cat: ' + args[0] + ': No such file', 'error'); + if(!args[0]) return this.terminal.print('Usage: cat <filename> [...]', 'warning'); + // Multiple operands: `cat *.txt` is useless if only the first path is read. + const files = args.filter(a => !String(a).startsWith('-')); + let printed = 0; + files.forEach((f) => { + const content = this.terminal.fs.readFile(f); + if (content !== null && content !== undefined) { + if (content.length === 0) this.terminal.print('(empty file)', 'info'); + else this.terminal.print(content); + printed++; + } else this.terminal.print('cat: ' + f + ': No such file', 'error'); + }); + if (!printed && !files.length) this.terminal.print('Usage: cat <filename> [...]', 'warning'); }; this.commands['echo'] = (args) => { if (!args || args.length === 0) { this.terminal.print(''); return; } @@ -6848,7 +6860,12 @@ class FileSystemModule { else if (headFiles.length && /^\d+$/.test(a)) n = parseInt(a, 10); // head f 40 else headFiles.push(a); } - if (!headFiles.length) return this.terminal.print('Usage: head [-n] <filename> [...] [lines]', 'warning'); + // Piped input: `cat x | head -n 2` — no file operand means stdin, not usage. + const piped = this.terminal ? this.terminal._pipeInput : null; + if (!headFiles.length) { + if (piped === null || piped === undefined) return this.terminal.print('Usage: head [-n] <filename> [...] [lines]', 'warning'); + return this.terminal.print(String(piped).split('\n').slice(0, Math.max(1, n)).join('\n')); + } const headMulti = headFiles.length > 1; headFiles.forEach(file => { const c = this.terminal.fs.readFile(file); @@ -6877,7 +6894,15 @@ class FileSystemModule { // v4.6.66: every path argument (packet §1). A numeric second positional is still // the legacy `tail <file> <count>`, not a filename. const tailFiles = positional.filter((p, i) => !(i > 0 && /^\d+$/.test(p))); - if (!tailFiles.length) return this.terminal.print('Usage: tail [-f] [-n N] <file> [...] — last N lines; -f follows changes (Ctrl+C or Esc to stop)', 'warning'); + // Piped input: `cat x | tail -n 1` — no file operand means stdin, not usage. + const piped = this.terminal ? this.terminal._pipeInput : null; + if (!tailFiles.length) { + if (piped === null || piped === undefined) return this.terminal.print('Usage: tail [-f] [-n N] <file> [...] — last N lines; -f follows changes (Ctrl+C or Esc to stop)', 'warning'); + const _in = String(piped).split('\n'); + // same trailing-newline rule as the file path below + if (_in.length > 1 && _in[_in.length - 1] === '') _in.pop(); + return this.terminal.print(_in.slice(-n).join('\n')); + } if (positional[1] && /^\d+$/.test(positional[1])) n = parseInt(positional[1], 10); // legacy back-compat const tailMulti = tailFiles.length > 1; let shownFiles = tailFiles; @@ -7047,10 +7072,25 @@ class FileSystemModule { else if (/^-[a-zA-Z]+$/.test(a)) { /* ignore other flags */ } else files.push(a); }); - if (!files.length) return this.terminal.print('Usage: wc [-l] [-w] [-c] <filename> [...]', 'warning'); + // Piped input: `cat x | wc -l`. The pipe wrapper sets terminal._pipeInput for every + // stage (modules/ext/shell.js), so "no file operand" means "count stdin", not usage. + const piped = this.terminal ? this.terminal._pipeInput : null; + const stdinMode = !files.length && piped !== null && piped !== undefined; + if (!files.length && !stdinMode) return this.terminal.print('Usage: wc [-l] [-w] [-c] <filename> [...]', 'warning'); const anyFlag = want.l || want.w || want.c; const rows = []; let total = { l: 0, w: 0, c: 0 }; + if (stdinMode) { + const content = String(piped); + const r = { + file: null, // stdin has no name — GNU wc prints the bare counts + l: content.split('\n').length, + w: content.split(/\s+/).filter(w => w).length, + c: content.length + }; + total.l += r.l; total.w += r.w; total.c += r.c; + rows.push(r); + } files.forEach(file => { const content = this.terminal.fs.readFile(file); if (content === null || content === undefined) { @@ -7073,7 +7113,7 @@ class FileSystemModule { if (want.l || !anyFlag) out.push(r.l); if (want.w || !anyFlag) out.push(r.w); if (want.c || !anyFlag) out.push(r.c); - if (multi || (anyFlag && !(want.l && want.w && want.c))) out.push(name); + if ((multi || (anyFlag && !(want.l && want.w && want.c))) && name !== null) out.push(name); return out.join(' '); }; rows.forEach(r => this.terminal.print(fmt(r, r.file))); |