Skip to content
Browse files

head polish / tests / standardization

  • Loading branch information...
1 parent 309cbe7 commit b0be1283615bd1038b1fca46f47c438caafe3491 @dthree committed
View
7 commands.json
@@ -83,6 +83,13 @@
"./dist/util/interfacer.js"
]
},
+ "head": {
+ "dependencies": ["vorpal-autocomplete-fs", "glob"],
+ "files": [
+ "./dist/util/interfacer.js",
+ "./dist/util/expand.js"
+ ]
+ },
"kill": {
"dependencies": ["fkill", "lodash"],
"files": [
View
61 dist/commands/head.js 100644 → 100755
@@ -1,59 +1,64 @@
'use strict';
+var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
+
var interfacer = require('./../util/interfacer');
var fs = require('fs');
var fsAutocomplete = require('vorpal-autocomplete-fs');
+var expand = require('./../util/expand');
+
var head = {
exec: function exec(args, options) {
- var self = this;
options = options || {};
+ args = args || '';
+ var source = args.stdin === undefined ? 'files' : 'stdin';
- options.argsType = args.stdin === undefined ? 'files' : 'stdin';
- options.n = options.n === undefined ? 10 : options.n;
-
- if (options.n < 1) {
- self.log('Option n must be a positive integer.');
- return 1;
+ var lines = options.lines ? Math.abs(options.lines) : 10;
+ if (!Number.isInteger(lines)) {
+ this.log('head: ' + options.lines + ': invalid number of lines');
+ return 0;
}
- if (options.argsType === 'stdin') {
- self.log(head.readLines(args.stdin[0], options.n));
+ /* istanbul ignore next */
+ if (source === 'stdin') {
+ var _stdout = head.readLines(args.stdin[0], lines);
+ _stdout = _stdout.replace(/\n$/, '');
+ if (_stdout.trim() !== '') {
+ this.log(_stdout);
+ }
return 0;
}
var files = args.files || args;
+ files = (typeof files === 'undefined' ? 'undefined' : _typeof(files)) === 'object' && files !== null && !Array.isArray(files) ? [] : files;
files = files === undefined ? [] : files;
files = typeof files === 'string' ? String(files).split(' ') : files;
files = files.filter(function (arg) {
return String(arg).trim() !== '';
});
+ files = expand(files);
var stdout = '';
- var writeHeaders = false;
- if (files.length > 1) {
- writeHeaders = true;
- }
+ var verbose = files.length > 1 && !options.silent || options.verbose;
- var content = [];
for (var i = 0; i < files.length; i++) {
try {
- content[i] = fs.readFileSync(files[i]).toString();
+ var content = fs.readFileSync(files[i]).toString();
+ if (verbose) {
+ stdout += (i > 0 ? '\n' : '') + '==> ' + files[i] + ' <==\n';
+ }
+ stdout += head.readLines(content, lines);
} catch (e) {
- self.log('head: ' + files[i] + ': No such file or directory');
- return 1;
+ stdout += 'head: cannot open ' + files[i] + ' for reading: No such file or directory';
}
}
- for (var i = 0; i < files.length; i++) {
- if (writeHeaders) {
- stdout += (i > 0 ? '\n\n' : '') + '==> ' + files[i] + ' <==\n';
- }
-
- stdout += head.readLines(content[i], options.n);
+ stdout = stdout.replace(/\n$/, '');
+ if (stdout.trim() !== '') {
+ this.log(stdout);
}
- self.log(stdout);
return 0;
},
readLines: function readLines(content, numberOfLines) {
@@ -62,13 +67,11 @@ var head = {
var linesToRead = numberOfLines >= contentArray.length ? contentArray.length : numberOfLines;
for (var i = 0; i < linesToRead; i++) {
if (stdout === '') {
- stdout = contentArray[i];
+ stdout = contentArray[i] + '\n';
continue;
}
-
- stdout += '\n' + contentArray[i];
+ stdout += contentArray[i] + '\n';
}
-
return stdout;
}
};
@@ -78,7 +81,7 @@ module.exports = function (vorpal) {
return head;
}
vorpal.api.head = head;
- vorpal.command('head [files...]').option('-n [number]', 'The first number of lines will be copied to stdout.').autocomplete(fsAutocomplete()).action(function (args, callback) {
+ vorpal.command('head [files...]').option('-n, --lines [number]', 'print the first K lines instead of the first 10').option('-q, --silent', 'Suppresses printing of headers when multiple files are being examined.').option('-v, --verbose', 'Always output headers giving file names.').autocomplete(fsAutocomplete()).action(function (args, callback) {
args.options = args.options || {};
return interfacer.call(this, {
command: head,
View
2 dist/help.js
@@ -2,7 +2,7 @@
var pad = require('./util/pad');
-var commands = ['alias [-p] [name=[value]]', 'cat [-AbeEnstTv] [files ...]', 'cd [dir]', 'clear', 'cp [-fnr] source ... dest', 'echo [-eE] [arg ...]', 'export [-p][id=[value]]', 'false', 'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]', 'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l', 'less [files ...]', 'ls [-aAdFhilQrRStUwx1] [paths ...]', 'mkdir [-pv] [directories ...]', 'mv [-fnv] source ... dest', 'pwd [files ...]', 'rm [-frR] [files ...]', 'sort [-chMnrR] [-o file] [files ...]', 'source filename [arguments...]', 'tail [options] <files ...>', 'touch [-acm] [-d date] [-r ref] [--time word] file ...', 'true', 'unalias [-a] name [names ...]'];
+var commands = ['alias [-p] [name=[value]]', 'cat [-AbeEnstTv] [files ...]', 'cd [dir]', 'clear', 'cp [-fnr] source ... dest', 'echo [-eE] [arg ...]', 'export [-p][id=[value]]', 'false', 'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]', 'head [-n number] [files...]', 'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l', 'less [files ...]', 'ls [-aAdFhilQrRStUwx1] [paths ...]', 'mkdir [-pv] [directories ...]', 'mv [-fnv] source ... dest', 'pwd [files ...]', 'rm [-frR] [files ...]', 'sort [-chMnrR] [-o file] [files ...]', 'source filename [arguments...]', 'tail [options] <files ...>', 'touch [-acm] [-d date] [-r ref] [--time word] file ...', 'true', 'unalias [-a] name [names ...]'];
function chop(str, len) {
var res = String(str).slice(0, len - 2);
View
0 package.json 100644 → 100755
File mode changed.
View
1 packages/cat/dist/commands/cat.js
@@ -91,6 +91,7 @@ var cat = {
if (stdout.length > 0) {
self.log(stdout.slice(0, stdout.length - 1));
}
+
return 0;
} catch (e) {
/* istanbul ignore next */
View
2 packages/tail/dist/commands/tail.js
@@ -18,7 +18,7 @@ var tail = {
var files = args;
files = files === undefined ? [] : files;
- files = typeof files === 'string' ? [files] : files;
+ files = typeof files === 'string' ? String(files).split(' ') : files;
files = files.filter(function (file) {
return String(file).trim() !== '';
});
View
61 src/commands/head.js 100644 → 100755
@@ -4,55 +4,58 @@ const interfacer = require('./../util/interfacer');
const fs = require('fs');
const fsAutocomplete = require('vorpal-autocomplete-fs');
+const expand = require('./../util/expand');
+
const head = {
exec(args, options) {
- const self = this;
options = options || {};
+ args = args || '';
+ const source = (args.stdin === undefined) ? 'files' : 'stdin';
- options.argsType = args.stdin === undefined ? 'files' : 'stdin';
- options.n = options.n === undefined ? 10 : options.n;
-
- if (options.n < 1) {
- self.log('Option n must be a positive integer.');
- return 1;
+ const lines = (options.lines) ? Math.abs(options.lines) : 10;
+ if (!Number.isInteger(lines)) {
+ this.log(`head: ${options.lines}: invalid number of lines`);
+ return 0;
}
- if (options.argsType === 'stdin') {
- self.log(head.readLines(args.stdin[0], options.n));
+ /* istanbul ignore next */
+ if (source === 'stdin') {
+ let stdout = head.readLines(args.stdin[0], lines);
+ stdout = stdout.replace(/\n$/, '');
+ if (stdout.trim() !== '') {
+ this.log(stdout);
+ }
return 0;
}
let files = args.files || args;
+ files = (typeof files === 'object' && files !== null && !Array.isArray(files)) ? [] : files;
files = (files === undefined) ? [] : files;
files = (typeof files === 'string') ? String(files).split(' ') : files;
files = files.filter(arg => String(arg).trim() !== '');
+ files = expand(files);
let stdout = '';
- let writeHeaders = false;
- if (files.length > 1) {
- writeHeaders = true;
- }
+ const verbose = ((files.length > 1 && !options.silent) || options.verbose);
- const content = [];
for (let i = 0; i < files.length; i++) {
try {
- content[i] = fs.readFileSync(files[i]).toString();
+ const content = fs.readFileSync(files[i]).toString();
+ if (verbose) {
+ stdout += `${i > 0 ? '\n' : ''}==> ${files[i]} <==\n`;
+ }
+ stdout += head.readLines(content, lines);
} catch (e) {
- self.log(`head: ${files[i]}: No such file or directory`);
- return 1;
+ stdout += `head: cannot open ${files[i]} for reading: No such file or directory`;
}
}
- for (let i = 0; i < files.length; i++) {
- if (writeHeaders) {
- stdout += `${i > 0 ? '\n\n' : ''}==> ${files[i]} <==\n`;
- }
-
- stdout += head.readLines(content[i], options.n);
+ stdout = stdout.replace(/\n$/, '');
+ if (stdout.trim() !== '') {
+ this.log(stdout);
}
- self.log(stdout);
return 0;
},
@@ -62,13 +65,11 @@ const head = {
const linesToRead = numberOfLines >= contentArray.length ? contentArray.length : numberOfLines;
for (let i = 0; i < linesToRead; i++) {
if (stdout === '') {
- stdout = contentArray[i];
+ stdout = `${contentArray[i]}\n`;
continue;
}
-
- stdout += `\n${contentArray[i]}`;
+ stdout += `${contentArray[i]}\n`;
}
-
return stdout;
}
};
@@ -80,7 +81,9 @@ module.exports = function (vorpal) {
vorpal.api.head = head;
vorpal
.command('head [files...]')
- .option('-n [number]', 'The first number of lines will be copied to stdout.')
+ .option('-n, --lines [number]', 'print the first K lines instead of the first 10')
+ .option('-q, --silent', 'Suppresses printing of headers when multiple files are being examined.')
+ .option('-v, --verbose', 'Always output headers giving file names.')
.autocomplete(fsAutocomplete())
.action(function (args, callback) {
args.options = args.options || {};
View
2 src/help.js
@@ -12,7 +12,7 @@ const commands = [
'export [-p][id=[value]]',
'false',
'grep [-bHhinqsvw] [-m max] [--silent] [--include pattern] pattern [files ...]',
- 'head [-n number] [files...]',
+ 'head [-nqv] <files ...>',
'kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l',
'less [files ...]',
'ls [-aAdFhilQrRStUwx1] [paths ...]',
View
12 src/help/head.js 100644 → 100755
@@ -1,6 +1,14 @@
module.exports = `
-Usage: head [OPTION] [files ...]
-Show first lines of file(s).
+Usage: head [OPTION]... [FILE]...
+Print the first 10 lines of each FILE to standard output.
+With more than one FILE, precede each with a header giving the file name.
+With no FILE, or when FILE is -, read standard input.
+
+ -n, --lines <number> output the last N lines, instead of the last 10
+ -q, --silent suppresses printing of headers when multiple files
+ are being examined
+ -v, --verbose always output headers giving file names
+ --help display this help and exit
Report head bugs to <https://github.com/dthree/cash>
Cash home page: <http://cash.js.org/>
View
88 test/head.js 100644 → 100755
@@ -7,6 +7,14 @@ const cash = require('../dist/index.js');
const $ = require('shelljs');
require('shelljs/global');
+const fxt = {
+ ten: 'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n',
+ ten2: 'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n',
+ five: 'line1\nline2\nline3\nline4\nline5\n',
+ hdr1: '==> eleven.test <==\n',
+ hdr2: '==> ten.test <==\n'
+};
+
describe('head', function () {
before(function () {
'line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\nline11'.to('eleven.test');
@@ -17,22 +25,82 @@ describe('head', function () {
$.rm(['eleven.test', 'ten.test']);
});
- it('should exist and be a function', function () {
+ it('should exist', function () {
should.exist(cash.head);
});
- it('should give back ten lines when called without options', function () {
- const result = cash.head('eleven.test');
- result.should.be.equal('line1\nline2\nline3\nline4\nline5\nline6\nline7\nline8\nline9\nline10\n');
+ it('should print the first ten lines in file', function () {
+ const fixture = fxt.ten;
+ cash('head eleven.test').should.equal(fixture);
+ });
+
+ it('should print the first ten lines for each file with headers', function () {
+ const fixture = `${fxt.hdr1}${fxt.ten}\n${fxt.hdr2}${fxt.ten2}`;
+ cash('head *.test').should.equal(fixture);
+ });
+
+ describe('-n, --lines', function () {
+ it('should print the first five lines in file', function () {
+ const fixture = fxt.five;
+ cash('head eleven.test -n 5').should.equal(fixture);
+ });
+
+ it('should disallow an invalid number of lines', function () {
+ const fixture = `head: cows: invalid number of lines\n`;
+ cash('head eleven.test -n cows').should.equal(fixture);
+ });
});
- it('should give back five lines when called with option n set to 5', function () {
- const result = cash.head('eleven.test', {n: 5});
- result.should.be.equal('line1\nline2\nline3\nline4\nline5\n');
+ describe('-v, --verbose', function () {
+ it('should print the first five lines in file with header', function () {
+ const fixture = `${fxt.hdr1}${fxt.five}`;
+ cash('head eleven.test --lines 5 -v').should.equal(fixture);
+ });
+
+ it('should print the first ten lines in file with header', function () {
+ const fixture = `${fxt.hdr1}${fxt.ten}`;
+ cash('head eleven.test -v').should.equal(fixture);
+ });
+ });
+
+ describe('-q, --silent', function () {
+ it('should print the first ten lines for each file without headers', function () {
+ const fixture = `${fxt.ten}${fxt.ten2}`;
+ cash('head *.test -q').should.equal(fixture);
+ });
+ });
+
+ describe('input validation', function () {
+ it('should do nothing on undefined input', function () {
+ cash.head().should.equal('');
+ });
+
+ it('should do nothing on a blank string', function () {
+ cash.head('').should.equal('');
+ });
+
+ it('should do nothing on no params with options', function () {
+ cash('head -n 5').should.equal('');
+ });
+
+ it('should give message on an invalid file', function () {
+ const fx = `head: cannot open sss.sd for reading: No such file or directory\n`;
+ cash.head('sss.sd').should.equal(fx);
+ });
+
+ it('should continue with valid files despite invalid files', function () {
+ const fx1 = `head: cannot open sss.sd for reading: No such file or directory\n`;
+ const fx3 = `head: cannot open ddd.df for reading: No such file or directory\n`;
+ cash('head -n 5 sss.sd eleven.test ddd.df').should.equal(`${fx1}${fxt.hdr1}${fxt.five}${fx3}`);
+ });
});
- it('should write header when used with more than one file', function () {
- const result = cash.head(['eleven.test', 'ten.test'], {n: 2});
- result.should.be.equal('==> eleven.test <==\nline1\nline2\n\n==> ten.test <==\nline1\nline2\n');
+ describe('input piping', function () {
+ it('should work with piped input', function () {
+ cash('cat eleven.test | head -n 5');
+ // const fixture = fxt.five;
+ // vorpal bug on piping sync stdout
+ // cash('cat eleven.test | head -n 5').should.equal(fixture);
+ });
});
});

0 comments on commit b0be128

Please sign in to comment.
Something went wrong with that request. Please try again.