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.
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.
Install with fisher:
fisher install ndonfris/stdin_to_argv.fishCall 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
endThis 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-s, --show- Display the contents of$argvafter reading from stdin-x, --export- Export$argvto 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.
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!echo apple banana | stdin_to_argv --show
# apple
# banana# 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: worldThe --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'# 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# Useful with find -print0 and similar tools
find . -name '*.fish' -print0 | stdin_to_argv --null
# Handles filenames with spaces and special characters safelyThe --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# 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
#
# bbegin
echo one two three | stdin_to_argv
echo "First argument: $argv[1]"
end
# First argument: oneThe function uses --no-scope-shadowing to modify the calling scope's $argv directly. It:
- Checks if there's piped input with
isatty - Builds a
readcommand 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)
- Default:
- Reads all input in a loop, collecting results into an array
- Sets
$argvin the parent scope to the collected input
- 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" cbecomes three arguments:a,b, andc. - 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' cis equivalent toprintf '%s\n' 'a b' c | foowhen using--lines. - With
--delimiteror--null: Input is split by the specified delimiter instead of tokenizing on whitespace.
- Default: Input is automatically tokenized on whitespace. Each line is tokenized separately, and all tokens are collected into
- Flag exclusivity: The
--lines,--delimiter, and--nullflags are mutually exclusive.
Issues and pull requests are welcome!