nip (Node Input/output Piper)

repository·master·Indexed 43 days ago

https://github.com/kolodny/nip

A command-line utility and Node.js library for complex file and pipe processing using JavaScript instead of traditional shell tools like awk or sed. Version 1.1.0 allows developers to transform text via JavaScript functions provided as strings or files, featuring customizable line and column splitters, in-place editing, and lifecycle events.

Tokens
3K
Snippets
11
Records
13
Agent score
54%

What's inside nip

  1. Execute logic once per file with --first-line-only

    master

    By default, nip executes the provided function for every line. Use the -1 or --first-line-only flag to execute the function only once per file. This is useful when you want to process the entire file as a single unit using the lines variable.

    nip 'return lines.length' -1 file.txt
  2. Use lifecycle events and context in nip

    master

    When using the closure style or complex logic, you can interact with the processing lifecycle using this.on().

    Available events:

    • start
    • end
    • fileStart
    • fileEnd

    The context (this) can also be used as a global store for state across lines, and it provides access to a filename property.

    nip '
      var biggest = 0;
      this.on("end", function() { print(biggest); });
      return function(_, i, lines) {
        biggest = Math.max(biggest, Math.max.apply(Math, lines.match(/(?:\s|^)[\d]+(?:\.\d*)?(?:\s|$)/g)));
      }
    ' -1 *
  3. Install nip via npm

    master

    Install the nip command-line utility globally to use it from any terminal session:

    npm install nip -g

    If you choose not to install it globally, ensure the local npm module path is added to your system's PATH.

  4. Use nip with JavaScript functions

    master

    The core usage of nip involves passing a JavaScript function as an argument to process input lines. The syntax for the js-function argument supports three styles:

    1. Expression style: A single return statement.
    2. Function style: A full function definition.
    3. Closure style: A block of code that returns a function.

    Arguments available to the function:

    • line: The current line content.
    • index: The current line index.
    • cols: Number of columns (if using a splitter).
    • lines: The array of all lines (useful with the -1 flag).

    Return value behavior:

    • If you return false: Nothing is sent to the output stream.
    • If you return a string or number: That value is sent to the output stream.
    • If you return anything else: The original line is sent to the output stream.
    nip 'return line.substr(0, 10) + index'
    nip 'function(line, index) { return line.toUpperCase(); }'
    nip '/* setup code */ return function(line, i) { return line + i; }'
  5. Provide JavaScript transformation logic to nip

    master

    To use nip, you must provide JavaScript code that defines how input lines are transformed. You can provide this logic in two ways:

    1. As a function-like string: Pass a string directly as the js-function argument.
    2. Via a file: Use the --file flag to point to a .js file containing your transformation logic.

    When using a string, you can use one of three formats:

    • A simple return statement: 'return line.toUpperCase()'
    • A full function definition: 'function(line, lineIndex) { return line.toUpperCase(); }'
    • A setup block that returns a function: 'var setup = 123; return function(line) { return line; }'

    Available Variables: When using the string format, the following variables are available in your scope:

    • line: The current line being processed.
    • index: The current line index.
    • cols: The number of columns.
    • lines: The total number of lines (only available if using the --first-line-only flag).

    Return Values:

    • Return a string to provide the transformed line.
    • Return false to omit the line from the output.
    # Example using a function-like string
    nip 'return line.toUpperCase()' input.txt
    
    # Example using a file
    nip --file transform.js input.txt
  6. Understand the nip event lifecycle

    master

    The nip function utilizes an internal context (an EventEmitter) to signal the lifecycle of the processing job. While the core nip function doesn't explicitly return the context to the user in the provided signature, the internal logic emits the following events:

    • start: Emitted once when the first file begins processing.
    • fileStart: Emitted at the beginning of every file in the files array.
    • fileEnd: Emitted after a file has been fully processed.
    • end: Emitted when all files in the files array have been processed.
  7. Define transformation logic for nip

    master

    The transformation logic (the [js-code] argument or the content of the --file) must be valid JavaScript that evaluates to a function. nip supports three formats for this function:

    1. Return statement: A string starting with return (e.g., return function(line, index, cols, lines) { ... }).
    2. Function declaration: A string starting with function (e.g., function(line) { return line.toUpperCase(); }).
    3. Expression: A raw function expression (e.g., (line) => line.trim()).

    The callback function receives the following arguments:

    • line: The current line being processed.
    • index: The index of the current line.
    • cols: An array of columns (split by the column splitter).
    • lines: The full content/context of the lines (depending on mode).
    # Example using a function expression via CLI
    nip "(line) => line.toUpperCase()" input.txt
    
    # Example using a file containing a function
    nip --file transform.js input.txt
  8. Configure nip with the --file option

    master

    Instead of passing a JavaScript string as a command-line argument, you can use the -f or --file option to point to a .js file containing your processing logic.

    nip -f jsfile.js file.txt
  9. Configure splitters and joiners

    master

    You can customize how nip parses input and joins output using the following options:

    • -c string-or-regex, --col-splitter=string-or-regex: Defines the splitter for columns (default is /\s+/).
    • -s string-or-regex, --line-splitter=string-or-regex: Defines the line separator (default is \n).
    • -n string, --line-joiner=string: The string used to join lines back together.
  10. Process input as a stream with processAsStream()

    master

    The processAsStream function provides a way to read data from a file or stdin, split it into lines based on a regular expression, and process each line through a callback. The results of the callback are then written to an output stream.

    Input Sources

    • If options.filename is set to '-', the function reads from process.stdin.
    • Otherwise, it creates a read stream from the file path provided in options.filename.

    Callback Behavior

    The options.callback is executed for every line. It receives the following arguments:

    1. line: The raw string content of the current line.
    2. lineNumber: The zero-based index of the current line.
    3. columns: An array of strings created by splitting the line using options.colSeparatorRegex.

    Return Values of the Callback:

    • false: The line is skipped, and no newline is written to the output stream.
    • string or number: The value is converted to a string and written to options.outStream, followed by the options.lineSeparator on the next line.
    • Any other type: The original line is written to options.outStream, followed by the options.lineSeparator on the next line.

    Options Object

    KeyDescription
    filenamePath to the input file, or '-' for stdin
    lineSeparatorRegexRegex used by the split module to identify line boundaries
    colSeparatorRegexRegex used to split the line into columns
    outStreamA writable stream where processed results are sent
    lineSeparatorThe string used to separate lines in the output stream
    callbackThe function executed for each line
    context(Optional) The this context for the callback function
    const processAsStream = require('./lib/process-as-stream');
    const fs = require('fs');
    
    const options = {
      filename: 'input.txt',
      lineSeparatorRegex: /\r?\n/,
      colSeparatorRegex: /,/,
      outStream: fs.createWriteStream('output.txt'),
      lineSeparator: '\n',
      callback: function(line, lineNumber, columns) {
        // Process columns
        const transformed = columns[0].toUpperCase();
        return transformed;
      }
    };
    
    processAsStream(options, (err) => {
      if (err) console.error(err);
      else console.log('Processing complete');
    });
  11. Use the nip function for file processing

    master

    The nip function is the primary entrypoint for processing files with custom line and column separators. It accepts an options object and processes a list of files sequentially.

    Required options:

    • files: An array of filenames to process.
    • callback: A function called after each file is processed.

    Optional options:

    • lineSeparator: A string representing the line separator.
    • lineSeparatorRegex: A regular expression for identifying line separators.
    • colSeparatorRegex: A regular expression for identifying column separators.
    • firstLineOnly: If true, only the first line of each file is processed (uses processAsBuffer internally).
    • inPlace: If true, the output is written back to the original filename (provided the filename is not -). Note that for non-stdin files, it temporarily writes to filename + ".nip" and then renames it.
    • outStream: The destination stream for output (defaults to process.stdout).
    • doneCallback: A function called after all files in the files array have been processed. It receives the total number of files processed as an argument.
    const nip = require('nip');
    
    nip({
      files: ['input.txt', 'data.csv'],
      callback: (err) => {
        if (err) console.error(err);
        else console.log('File processed');
      },
      lineSeparator: '\n',
      colSeparatorRegex: /,/,
      inPlace: true,
      doneCallback: (total) => {
        console.log(`Finished processing ${total} files`);
      }
    });
  12. Configure nip CLI options

    master

    Use the following flags to control how nip parses and joins lines, and how it handles output:

    • -f, --file <path>: Path to a JavaScript file containing the transformation function.
    • -1, --first-line-only: Process the entire file as a single unit instead of line-by-line.
    • -s, --line-splitter [splitter]: A regex or string used to split lines. Defaults to /\r?\n/.
    • -n, --line-joiner [joiner]: A string used to join lines back together. Defaults to \n.
    • -c, --col-splitter [splitter]: A regex or string used to split columns within a line. Defaults to \s+.
    • -i, --in-place: Write the transformed output directly back into the input file.
    nip --help