-
Notifications
You must be signed in to change notification settings - Fork 733
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #379 from shelljs/feat-head-sort-commands
New commands: sort(), head(), and tail()
- Loading branch information
Showing
18 changed files
with
763 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
var common = require('./common'); | ||
var fs = require('fs'); | ||
|
||
// This reads n or more lines, or the entire file, whichever is less. | ||
function readSomeLines(file, numLines) { | ||
var BUF_LENGTH = 64*1024, | ||
buf = new Buffer(BUF_LENGTH), | ||
bytesRead = BUF_LENGTH, | ||
pos = 0, | ||
fdr = null; | ||
|
||
try { | ||
fdr = fs.openSync(file, 'r'); | ||
} catch(e) { | ||
common.error('cannot read file: ' + file); | ||
} | ||
|
||
var numLinesRead = 0; | ||
var ret = ''; | ||
while (bytesRead === BUF_LENGTH && numLinesRead < numLines) { | ||
bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); | ||
var bufStr = buf.toString('utf8', 0, bytesRead); | ||
numLinesRead += bufStr.split('\n').length - 1; | ||
ret += bufStr; | ||
pos += bytesRead; | ||
} | ||
|
||
fs.closeSync(fdr); | ||
return ret; | ||
} | ||
//@ | ||
//@ ### 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) | ||
function _head(options, files) { | ||
options = common.parseOptions(options, { | ||
'n': 'numLines' | ||
}); | ||
var head = []; | ||
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; | ||
} | ||
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; | ||
if (file === '-') | ||
contents = pipe; | ||
else if (options.numLines < 0) { | ||
contents = fs.readFileSync(file, 'utf8'); | ||
} else { | ||
contents = readSomeLines(file, options.numLines); | ||
} | ||
|
||
var lines = contents.split('\n'); | ||
var hasTrailingNewline = (lines[lines.length-1] === ''); | ||
if (hasTrailingNewline) | ||
lines.pop(); | ||
shouldAppendNewline = (hasTrailingNewline || options.numLines < lines.length); | ||
|
||
head = head.concat(lines.slice(0, options.numLines)); | ||
}); | ||
|
||
if (shouldAppendNewline) | ||
head.push(''); // to add a trailing newline once we join | ||
return new common.ShellString(head.join('\n'), common.state.error, common.state.errorCode); | ||
} | ||
module.exports = _head; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
var common = require('./common'); | ||
var fs = require('fs'); | ||
|
||
// parse out the number prefix of a line | ||
function parseNumber (str) { | ||
var match = str.match(/^\s*(\d*)\s*(.*)$/); | ||
return {num: Number(match[1]), value: match[2]}; | ||
} | ||
|
||
// compare two strings case-insensitively, but examine case for strings that are | ||
// case-insensitive equivalent | ||
function unixCmp(a, b) { | ||
var aLower = a.toLowerCase(); | ||
var bLower = b.toLowerCase(); | ||
return (aLower === bLower ? | ||
-1 * a.localeCompare(b) : // unix sort treats case opposite how javascript does | ||
aLower.localeCompare(bLower)); | ||
} | ||
|
||
// compare two strings in the fashion that unix sort's -n option works | ||
function numericalCmp(a, b) { | ||
var objA = parseNumber(a); | ||
var objB = parseNumber(b); | ||
if (objA.hasOwnProperty('num') && objB.hasOwnProperty('num')) { | ||
return ((objA.num !== objB.num) ? | ||
(objA.num - objB.num) : | ||
unixCmp(objA.value, objB.value)); | ||
} else { | ||
return unixCmp(objA.value, objB.value); | ||
} | ||
} | ||
|
||
//@ | ||
//@ ### sort([options,] file [, file ...]) | ||
//@ ### sort([options,] file_array) | ||
//@ Available options: | ||
//@ | ||
//@ + `-r`: Reverse the result of comparisons | ||
//@ + `-n`: Compare according to numerical value | ||
//@ | ||
//@ Examples: | ||
//@ | ||
//@ ```javascript | ||
//@ sort('foo.txt', 'bar.txt'); | ||
//@ sort('-r', 'foo.txt'); | ||
//@ ``` | ||
//@ | ||
//@ Return the contents of the files, sorted line-by-line. Sorting multiple | ||
//@ files mixes their content, just like unix sort does. | ||
function _sort(options, files) { | ||
options = common.parseOptions(options, { | ||
'r': 'reverse', | ||
'n': 'numerical' | ||
}); | ||
|
||
// Check if this is coming from a pipe | ||
var pipe = common.readFromPipe(this); | ||
|
||
if (!files && !pipe) | ||
common.error('no files given'); | ||
|
||
files = [].slice.call(arguments, 1); | ||
|
||
if (pipe) | ||
files.unshift('-'); | ||
|
||
var lines = []; | ||
files.forEach(function(file) { | ||
if (!fs.existsSync(file) && file !== '-') { | ||
// exit upon any sort of error | ||
common.error('no such file or directory: ' + file); | ||
} | ||
|
||
var contents = file === '-' ? pipe : fs.readFileSync(file, 'utf8'); | ||
lines = lines.concat(contents.trimRight().split(/\r*\n/)); | ||
}); | ||
|
||
var sorted; | ||
sorted = lines.sort(options.numerical ? numericalCmp : unixCmp); | ||
|
||
if (options.reverse) | ||
sorted = sorted.reverse(); | ||
|
||
return new common.ShellString(sorted.join('\n')+'\n', common.state.error, common.state.errorCode); | ||
} | ||
|
||
module.exports = _sort; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.