mirror of
https://github.com/shelljs/shelljs.git
synced 2026-01-18 16:03:37 +00:00
feat(command): new command: tail()
This commit is contained in:
parent
a1d5f6afb6
commit
60d63015cd
32
README.md
32
README.md
@ -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
|
||||
|
||||
4
shell.js
4
shell.js
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
65
src/tail.js
Normal 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;
|
||||
@ -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
105
test/tail.js
Normal 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);
|
||||
Loading…
x
Reference in New Issue
Block a user