feat(command): new command: tail()

This commit is contained in:
Nate Fischer 2016-03-20 03:24:33 -07:00
parent a1d5f6afb6
commit 60d63015cd
7 changed files with 209 additions and 4 deletions

View File

@ -302,6 +302,36 @@ containing the files if more than one file is given (a new line character is
introduced between each file).
### head([{'-n', \<num\>},] file [, file ...])
### head([{'-n', \<num\>},] file_array)
Examples:
```javascript
var str = head({'-n', 1}, 'file*.txt');
var str = head('file1', 'file2');
var str = head(['file1', 'file2']); // same as above
```
Output the first 10 lines of a file (or the first `<num>` if `-n` is
specified)
### tail([{'-n', \<num\>},] file [, file ...])
### tail([{'-n', \<num\>},] file_array)
Examples:
```javascript
var str = tail({'-n', 1}, 'file*.txt');
var str = tail('file1', 'file2');
var str = tail(['file1', 'file2']); // same as above
```
Output the last 10 lines of a file (or the last `<num>` if `-n` is
specified)
### ShellString.prototype.to(file)
Examples:
@ -345,7 +375,7 @@ using the given search regex and replacement string or function. Returns the new
### sort([options,] file [, file ...])
### sed([options,] file_array)
### sort([options,] file_array)
Available options:
+ `-r`: Reverse the result of comparisons

View File

@ -62,6 +62,10 @@ exports.cat = common.wrap('cat', _cat, {idx: 1});
var _head = require('./src/head');
exports.head = common.wrap('head', _head, {idx: 1});
//@include ./src/tail
var _tail = require('./src/tail');
exports.tail = common.wrap('tail', _tail, {idx: 1});
// The below commands have been moved to common.ShellString(), and are only here
// for generating the docs
//@include ./src/to

View File

@ -101,7 +101,7 @@ var ShellString = function (stdout, stderr, code) {
that.code = code;
that.to = function() {wrap('to', _to, {idx: 1}).apply(that.stdout, arguments); return that;};
that.toEnd = function() {wrap('toEnd', _toEnd, {idx: 1}).apply(that.stdout, arguments); return that;};
['cat', 'head', 'sed', 'sort', 'grep', 'exec'].forEach(function (cmd) {
['cat', 'head', 'sed', 'sort', 'tail', 'grep', 'exec'].forEach(function (cmd) {
that[cmd] = function() {return shell[cmd].apply(that.stdout, arguments);};
});
return that;

View File

@ -32,7 +32,7 @@ function numericalCmp(a, b) {
//@
//@ ### sort([options,] file [, file ...])
//@ ### sed([options,] file_array)
//@ ### sort([options,] file_array)
//@ Available options:
//@
//@ + `-r`: Reverse the result of comparisons

65
src/tail.js Normal file
View File

@ -0,0 +1,65 @@
var common = require('./common');
var fs = require('fs');
//@
//@ ### tail([{'-n', \<num\>},] file [, file ...])
//@ ### tail([{'-n', \<num\>},] file_array)
//@
//@ Examples:
//@
//@ ```javascript
//@ var str = tail({'-n', 1}, 'file*.txt');
//@ var str = tail('file1', 'file2');
//@ var str = tail(['file1', 'file2']); // same as above
//@ ```
//@
//@ Output the last 10 lines of a file (or the last `<num>` if `-n` is
//@ specified)
function _tail(options, files) {
options = common.parseOptions(options, {
'n': 'numLines'
});
var tail = [];
var pipe = common.readFromPipe(this);
if (!files && !pipe)
common.error('no paths given');
var idx = 1;
if (options.numLines === true) {
idx = 2;
options.numLines = Number(arguments[1]);
} else if (options.numLines === false) {
options.numLines = 10;
}
options.numLines = -1 * Math.abs(options.numLines);
files = [].slice.call(arguments, idx);
if (pipe)
files.unshift('-');
var shouldAppendNewline = false;
files.forEach(function(file) {
if (!fs.existsSync(file) && file !== '-') {
common.error('no such file or directory: ' + file, true);
return;
}
var contents = file === '-' ? pipe : fs.readFileSync(file, 'utf8');
var lines = contents.split('\n');
if (lines[lines.length-1] === '') {
lines.pop();
shouldAppendNewline = true;
} else {
shouldAppendNewline = false;
}
tail = tail.concat(lines.slice(options.numLines));
});
if (shouldAppendNewline)
tail.push(''); // to add a trailing newline once we join
return new common.ShellString(tail.join('\n'), common.state.error, common.state.errorCode);
}
module.exports = _tail;

View File

@ -52,10 +52,11 @@ child.exec(JSON.stringify(process.execPath)+' '+file, function(err, stdout) {
// Expands to directories by default
var result = common.expand(['resources/*a*']);
assert.equal(result.length, 4);
assert.equal(result.length, 5);
assert.ok(result.indexOf('resources/a.txt') > -1);
assert.ok(result.indexOf('resources/badlink') > -1);
assert.ok(result.indexOf('resources/cat') > -1);
assert.ok(result.indexOf('resources/head') > -1);
assert.ok(result.indexOf('resources/external') > -1);
// Check to make sure options get passed through (nodir is an example)

105
test/tail.js Normal file
View File

@ -0,0 +1,105 @@
var shell = require('..');
var assert = require('assert');
var fs = require('fs');
shell.config.silent = true;
shell.rm('-rf', 'tmp');
shell.mkdir('tmp');
var result;
//
// Invalids
//
result = shell.tail();
assert.ok(shell.error());
assert.equal(result.code, 1);
assert.equal(fs.existsSync('/asdfasdf'), false); // sanity check
result = shell.tail('/adsfasdf'); // file does not exist
assert.ok(shell.error());
assert.equal(result.code, 1);
//
// Valids
//
var bottomOfFile1 = ['file1 50', 'file1 49', 'file1 48', 'file1 47', 'file1 46',
'file1 45', 'file1 44', 'file1 43', 'file1 42', 'file1 41',
'file1 40', 'file1 39', 'file1 38', 'file1 37', 'file1 36',
'file1 35', 'file1 34', 'file1 33', 'file1 32', 'file1 31'];
var bottomOfFile2 = ['file2 50', 'file2 49', 'file2 48', 'file2 47', 'file2 46',
'file2 45', 'file2 44', 'file2 43', 'file2 42', 'file2 41',
'file2 40', 'file2 39', 'file2 38', 'file2 37', 'file2 36',
'file2 35', 'file2 34', 'file2 33', 'file2 32', 'file2 31'];
// simple
result = shell.tail('resources/head/file1.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile1.slice(0, 10).reverse().join('\n')+'\n');
// multiple files
result = shell.tail('resources/head/file2.txt', 'resources/head/file1.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile2.slice(0, 10).reverse().concat(
bottomOfFile1.slice(0, 10).reverse()
).join('\n')+'\n');
// multiple files, array syntax
result = shell.tail(['resources/head/file2.txt', 'resources/head/file1.txt']);
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile2.slice(0, 10).reverse().concat(
bottomOfFile1.slice(0, 10).reverse()
).join('\n')+'\n');
// reading more lines than are in the file (no trailing newline)
result = shell.tail('resources/file2', 'resources/file1');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, 'test2\ntest1'); // these files only have one line (no \n)
// reading more lines than are in the file (with trailing newline)
result = shell.tail('resources/head/shortfile2', 'resources/head/shortfile1');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, 'short2\nshort1\n'); // these files only have one line (with \n)
// Globbed file
result = shell.tail('resources/head/file?.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile1.slice(0, 10).reverse().concat(
bottomOfFile2.slice(0, 10).reverse()
).join('\n')+'\n');
// With `'-n' <num>` option
result = shell.tail('-n', 4, 'resources/head/file2.txt', 'resources/head/file1.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile2.slice(0, 4).reverse().concat(
bottomOfFile1.slice(0, 4).reverse()
).join('\n')+'\n');
// With `{'-n': <num>}` option
result = shell.tail({'-n': 4}, 'resources/head/file2.txt', 'resources/head/file1.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile2.slice(0, 4).reverse().concat(
bottomOfFile1.slice(0, 4).reverse()
).join('\n')+'\n');
// negative values are the same as positive values
result = shell.tail('-n', -4, 'resources/head/file2.txt', 'resources/head/file1.txt');
assert.equal(shell.error(), null);
assert.equal(result.code, 0);
assert.equal(result, bottomOfFile2.slice(0, 4).reverse().concat(
bottomOfFile1.slice(0, 4).reverse()
).join('\n')+'\n');
shell.exit(123);