Skip to content

ndonfris/stdin_to_argv.fish

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 

Repository files navigation

stdin_to_argv.fish

A fish shell utility that seamlessly converts piped stdin into $argv, allowing functions to handle both piped input and command-line arguments uniformly. Supports multiline input, custom delimiters, and null-delimited data.

Why?

When writing fish shell functions, you often need to handle two input methods:

  • Arguments passed directly: my_function arg1 arg2
  • Data piped in: echo arg1 arg2 | my_function

Without stdin_to_argv, you'd need to write boilerplate code in every function to detect and handle both cases. This plugin eliminates that duplication.

Installation

Install with fisher:

fisher install ndonfris/stdin_to_argv.fish

Usage

Call stdin_to_argv at the beginning of any function where you want to accept piped input:

function my_function
    stdin_to_argv
    # Now $argv contains either the arguments or piped input
    printf '%s\n' $argv
end

This works seamlessly with both input methods:

# Using arguments
my_function foo bar
# Output:
# foo
# bar

# Using pipes
echo foo bar | my_function
# Output:
# foo
# bar

Options

  • -s, --show - Display the contents of $argv after reading from stdin
  • -x, --export - Export $argv to the global scope
  • -L, --lines - Read each line as a single argument, preserving whitespace within lines
  • -d, --delimiter=DEL - Split input by custom delimiter instead of tokenizing on whitespace
  • -z, --null - Split input on null characters (\0) instead of tokenizing
  • -m, --max=NUM - Limit the number of arguments read from stdin to NUM
  • -n, --no-empty - Do not include empty arguments in $argv
  • -h, --help - Show help message and exit

Note: The --lines, --delimiter, and --null flags are mutually exclusive.

Examples

Basic usage in a function

function greet
    stdin_to_argv
    for name in $argv
        echo "Hello, $name!"
    end
end

echo Alice Bob | greet
# Hello, Alice!
# Hello, Bob!

greet Alice Bob
# Hello, Alice!
# Hello, Bob!

Using the --show flag

echo apple banana | stdin_to_argv --show
# apple
# banana

Handling multiline input

# Default behavior: tokenizes on whitespace
string split ' ' -- 'a b c d' | stdin_to_argv --show
# a
# b
# c
# d

# Array indexing works as expected
function test_func
    stdin_to_argv
    echo "First: $argv[1]"
    echo "Second: $argv[2]"
end

string split ' ' -- 'hello world' | test_func
# First: hello
# Second: world

Preserving whitespace with --lines

The --lines flag reads each line as a single argument, preserving whitespace within the line. This matches how arguments work when passed directly to a function:

# With --lines, whitespace within each line is preserved
printf '%s\n' 'a b' c | stdin_to_argv --lines --show
# a b
# c

# Compare: default tokenizing behavior splits on all whitespace
printf '%s\n' 'a b' c | stdin_to_argv --show
# a
# b
# c

# Practical example: mimicking function arguments
function my_func
    stdin_to_argv --lines
    echo "arg[1]: '$argv[1]'"
    echo "arg[2]: '$argv[2]'"
end

# These produce the same result:
my_func 'hello world' 'foo'
# argv[1]: 'hello world'
# argv[2]: 'foo'

printf '%s\n' 'hello world' 'foo' | my_func
# argv[1]: 'hello world'
# argv[2]: 'foo'

Using custom delimiters

# Split on colons
echo 'foo:bar:baz' | stdin_to_argv --delimiter ':' --show
# foo
# bar
# baz

# Split on commas
echo 'apple,banana,cherry' | stdin_to_argv -d ',' --show
# apple
# banana
# cherry

Working with null-delimited input

# Useful with find -print0 and similar tools
find . -name '*.fish' -print0 | stdin_to_argv --null
# Handles filenames with spaces and special characters safely

Streaming and buffering with --max

The --max flag enables streaming mode, where you can process input in controlled chunks rather than loading everything into memory at once. This is useful for processing large datasets, infinite streams, or when you want to handle input incrementally.

# Stream processing: handle input 2 items at a time
seq 6 | while stdin_to_argv --lines --max 2
    echo "Processing batch: $argv"
    # Do work with this batch...
end
# Processing batch: 1 2
# Processing batch: 3 4
# Processing batch: 5 6

# Practical example: batch file processing
find . -name '*.txt' -print0 | while stdin_to_argv --null --max 10
    # Process up to 10 files at a time
    echo "Processing batch of (count $argv) files"
    do_something $argv
end

# Control infinite streams
yes | stdin_to_argv --lines --max 5
# y
# y
# y
# y
# y

Filtering empty values with --no-empty

# Combine --max and --no-empty to skip empty lines
printf %s\n a '' b '' c | stdin_to_argv --lines --max 3 --show --no-empty
# a
# b
# c

# Without --no-empty, empty lines are included
printf %s\n a '' b | stdin_to_argv --lines --show
# a
#
# b

Using in a begin block

begin
    echo one two three | stdin_to_argv
    echo "First argument: $argv[1]"
end
# First argument: one

How It Works

The function uses --no-scope-shadowing to modify the calling scope's $argv directly. It:

  1. Checks if there's piped input with isatty
  2. Builds a read command based on the specified flags:
    • Default: read --tokenize (splits on whitespace, handles multiple lines)
    • With --lines: read -L (reads each line as a single element, preserving whitespace)
    • With --delimiter: read --delimiter (splits on custom delimiter)
    • With --null: read --null (splits on null characters)
  3. Reads all input in a loop, collecting results into an array
  4. Sets $argv in the parent scope to the collected input

Important Notes

  • No piped input = no-op: If called without piped input, the function returns immediately without modifying $argv.
  • Multiline support: The function reads all lines from stdin, not just the first line. This enables proper handling of piped commands like string split.
  • Tokenization modes:
    • Default: Input is automatically tokenized on whitespace. Each line is tokenized separately, and all tokens are collected into $argv. For example, echo "a b" c becomes three arguments: a, b, and c.
    • With --lines: Each line becomes a single argument, preserving whitespace within the line. This matches how arguments work when calling a function directly: foo 'a b' c is equivalent to printf '%s\n' 'a b' c | foo when using --lines.
    • With --delimiter or --null: Input is split by the specified delimiter instead of tokenizing on whitespace.
  • Flag exclusivity: The --lines, --delimiter, and --null flags are mutually exclusive.

License

MIT

Contributing

Issues and pull requests are welcome!

About

fish utility for tokenizing piped stdin into a functions `$argv`

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages