Rock26 Rock26 - 2 months ago 11x
Bash Question

Read command line arguments with input redirection operator in bash

I need to read command line arguments. First arg is script name. second one is redirection operator i.e. "<" and third one is input filename. When I tried to use "$#", I got 0. When I used "$*", it gave me nothing. I have to use "<" this operator. My input file consists of all user input data. If I don't use the operator, It asks user for the input. Can someone please help me? Thank you !

Command Line :

./script_name < input_file


echo "$*" # gave nothing
echo "$#" # gave me 0

I need to read input filename and store it to some variable. Then I have to change the extension of it. Any help/suggestions should be appreciated.


When a user runs:

./script_name <input_file

...that's exactly equivalent to if they did the following:

(exec <input_file; exec ./script_name)

...first redirecting stdin from input_file, then invoking the script named ./script_name without any arguments.

There are operating-system-specific interfaces you can use to get the filename associated with a handle (when it has one), but to use one of these would make your script only able to run on an operating system providing that interface; it's not worth it.

# very, very linux-specific, won't work for "cat foo | ./yourscript", generally evil
if filename=$(readlink /proc/self/fd/0) && [[ -e $filename ]]; then
  set -- "$@" "$filename" # append filename to the end of the argument list

If you want to avoid prompting for input when an argument is given, and to have the filename of that argument, then don't take it on stdin but as an argument, and do the redirection yourself within the script:


if [[ $1 ]]; then
  exec <"$1" # this redirects your stdin to come from the file

# ...put other logic here...

...and have users invoke your script as:

./script_name input_file

Just as ./yourscript <filename runs yourscript with the contents of filename on its standard input, a script invoked with ./yourscript filename which invokes exec <"$1" will have the contents of filename on its stdin after executing that command.