10 cli snippets
gracefully closing node.js applications via signal handling
To make your node.js application gracefully respond to shutdown signals, use process.on(SIGNAL,HANDLER)
.
For example, to respond to SIGINT
(typically Ctrl-c), you can use:
process.on( "SIGINT", function() {
console.log('CLOSING [SIGINT]');
process.exit();
} );
Note that without the process.exit()
, the program will not be shutdown. (This is you chance to override or "trap" the signal.)
Some common examples (in CoffeeScript):
process.on 'SIGHUP', ()->console.log('CLOSING [SIGHUP]'); process.exit()
process.on 'SIGINT', ()->console.log('CLOSING [SIGINT]'); process.exit()
process.on 'SIGQUIT', ()->console.log('CLOSING [SIGQUIT]'); process.exit()
process.on 'SIGABRT', ()->console.log('CLOSING [SIGABRT]'); process.exit()
process.on 'SIGTERM', ()->console.log('CLOSING [SIGTERM]'); process.exit()
PS: On Linux (and similar) you can enter kill -l
on the command line to see a list of possible signals, and kill -N PID
to send signal N to the process with process ID PID.
Check require.main
to test if a Node.js file is run directly
In Node, when a file is run directly from the command line, require.main
is set to its module
. Hence require.main === module
tells you whether or not your script was invoked directly or required by another file.
A JavaScript "main" idiom:
//#!/usr/bin/env node
// file: example.js
function main() {
// ...
}
if(require.main === module) {
main();
}
The main
method will run if example.js
is invoked via node example.js
or ./example.js
but not when required within another script (via require('./example')
, for example).
A CoffeeScript "main" idiom (using classes, although it doesn't have to):
#!/usr/bin/env coffee
# file: example.coffee
class Example
main:()->
# ...
if require.main is module
(new Example()).main()
The main
method will run if example.coffee
is invoked via coffee example.coffee
or ./example.coffee
but not when required within another script (via require('./example')
, for example).
Also see the nodejs.org docs.
In node-optimist, argv._
is an array of the "extra" parameters
In substack's node-optimist, you can use argv._
to fetch any parameters remaining after optimist has done its parsing.
For example (in CoffeeScript):
# file: example.coffee
optimist = require 'optimist'
options = {
'help' : { description:'Show this message and exit.', boolean:true, alias:'h' }
}
argv = optimist.usage('Usage: $0 [--help]', options).argv
# Now argv._ contains an array "extra" parameters, if any
console.log argv._
For example
coffee example.coffee --help
yields
[ ]
but either of
coffee example.coffee --help foo.txt bar.png
or
coffee example.coffee foo.txt bar.png
yield
[ "foo.txt", "bar.png" ]
Ruby-like ARGF for Node.js
tokuhirom's node-argf module offers a Ruby-like ARGF for Node.js.
Install via:
npm install argf
or by adding
{
"dependencies" : {
"argf" : "latest"
}
}
to your package.json
file.
Use ARGF like this:
ARGF = require('argf');
argf = new ARGF(); // create argf based on current
// command line parameters or
// input streams.
// register a callback for when all input data has been read
argf.on('finished', function() {
console.log("Done processing all inputs.");
});
// process the input(s)
argf.forEach( function(line) {
console.log("Read:",line);
console.log("From source:",argv.stream.path);
}
Like Ruby's ARGF
, the module assumes any elements in process.argv
represent files to process (and uses the input stream if no files are provided.
You can also pass an array to new ARGF()
to provide the list of files, which is handy if you're using something like node-optimist. (Note that in node-optimist you can use argv._
to get the remaining parameters after parsing.) For example:
optimist = require('optimist');
ARGF = require('argf');
options = {
# ...
}
argv = optimist.usage('Usage: $0 ...', options).argv;
argf = new ARGF(argv._);
argf.on('finished', function() {
console.log("Done processing all inputs.");
});
// process the input(s)
argf.forEach( function(line) {
console.log("Read:",line);
console.log("From source:",argv.stream.path);
}
Use 'less -S' for horizontal scrolling
The flag -S
(or --chop-long-lines
) will cause less
to truncate lines at the screen (terminal) boundary, rather than wrapping as it does by default. You can then scroll horizontally (with the arrow keys, for example) to view the full lines when needed.
cat some_file_with_very_long_lines | less -S
How to right-align text in your bash prompt
Right aligning text by padding with spaces
To have text in your bash prompt ($PS1
) hug the right side of the terminal:
PS1="`printf "%${COLUMNS}s\n" "${TEXT}"`$PS1"
(This assumes you want the right-aligned text to appear before the rest of your prompt, if any. Move the $PS1
bit to the left side of the string to have the right-aligned text appear after the rest of your prompt.)
The ${COLUMNS}
variable contains the number of columns in the current terminal (it should change if you resize the terminal). The ${TEXT}
variable is a placeholder for the text you want to right-align.
The trick here is to use printf
to left-pad the string to given width. printf "%ns" "text"
will left-pad the given string (here, text
) with spaces until the entire string is n characters wide.
Right aligning text by padding with something other than space.
Say you want to pad with -
instead of space. Try:
PS1="`printf -vch "%${COLUMNS}s" "${TEXT}"; printf "%s" "${ch// /-}"`$PS1"
This will left-pad the ${TEXT}
with spaces, as above, and then replace any spaces with -
.
If you have any spaces in ${TEXT}
you want to preserve, one hacky work-around is to mark spaces in $TEXT
with some other character, say _
, and then replace _
with a space () after the other substitution:
$ PS1="$PS1`printf -vch "%${COLUMNS}s" "${TEXT}"; printf -vch "%s" "${ch// /-}"; printf "%s\n" "${ch//_/ }"`"
Drawing a line to the end of the line
I recently added a line containing the date and time to my bash prompt (so I can tell when a given command completed) and wanted to draw a line across the rest of the screen to make it visually easier to tell where a new prompt is displayed. Something like this:
-- Tue 02-Oct-2012 05:19 PM --------------------------------
(Assuming the terminal is 60 characters wide.)
Here's how I did it.
Within my $PROMPT_COMMAND
I execute the following:
line="`printf -vch "%${COLUMNS}s" ""; printf "%s" "${ch// /-}"`"
dts="`date +"-- %a %d-%b-%Y %I:%M %p "`"
PS1="$PS1\e[1m\e[32m${dts}${line:${#dts}}"
The first line creates a variable ($line
) with ${COLUMNS}
dashes (-
). This line would span the length of the terminal.
The second line creates a variable ($dts
) with my date and time format of choice (prefixed with --
just for kicks).
The ${dts}${line:${#dts}}
bit in the third line displays my date and time string ($dts
) and then a substring of $line
, starting at the length of my date and time string (${#dts}
). (In this particular case ${dts}
is always exactly 28 characters long, so that value could be hard-coded but this way it works in the general case too.)
If you are curious, the \e[1m\e[32m
bit makes the text bold (\e[1m
) and green (\e[32m
).
Python one-liner for reading a CSV file into a JSON array of arrays
Reading a CSV file into 2-d Python array (an array of arrays):
import csv
array = list(csv.reader(open( MYFILE.csv )))
Dumping that as JSON (via the command-line):
$ python -c "import json,csv;print json.dumps(list(csv.reader(open( CSV-FILENAME ))))"
Launch an HTTP server serving the current directory using Python
The Python SimpleHTTPServer
module makes it easy to launch a simple web server using a current working directory as the "docroot".
With Python 2:
python -m SimpleHTTPServer
or with Python 3:
python3 -m http.server
By default, each will bind to port 8080, hence http://localhost:8080/
will serve the top level of the working directory tree. Hit Ctrl-c
to stop.
Both accept an optional port number:
python -m SimpleHTTPServer 3001
or
python3 -m http.server 3001
if you want to bind to something other than port 8080.
Reading from input files or STDIN in Ruby using ARGF.
ARGF
makes it easy for a Ruby script to read from STDIN, a file specified on the command-line argument or multiple files specified on the command line, all through the same interface.
Recall that ARGV
array contains the arguments passed to your Ruby script on the command line.
ARGF
assumes that any elements that remain in ARGV
represent files. Methods like ARGF.each
(accepting a block) and ARGF.readlines
(returning an array) operate on the concatenation of all files found in ARGV
. If ARGV
is empty, then ARGF
operates on STDIN instead.
For example, a cat
-like program could be implemented in Ruby as:
ARGF.each_line { |line| puts line }
When working with optparse
, use the parse!
method to strip recognized "flag" parameters from ARGV
, leaving only the files you want to operate so that ARGF
works just like you want it to. For example:
require 'optparse'
options = { }
opt_parser = OptionParser.new do |opt|
opt.banner = "Usage: #{$0} [OPTIONS]"
opt.separator ""
opt.separator "OPTIONS"
opt.on("-h","--heading HEADING","a heading to display.") do |heading|
options[:heading] = heading
end
opt.on("-v","--verbose","be more chatty") do
options[:verbose] = true
end
end
opt_parser.parse!
puts options[:heading] unless options[:heading].nil?
ARGF.each_line { |line| puts line }