Reputation: 162
I am trying to set my Linux shell script to read from a file (which I have working) but if there isn't any file then I need to read from stdin.
The command for reading a file looks like this:
./stats -row test_file
How would I be able to read what the user enters with something like this:
./stats -row 4 2 3 5 3 4 5 3 6 5 6 3 4
When I enter a command like this I get 'no such file or directory'
I broke my script down to the problem I need help with.
#!/bin/sh
INPUT_FILE=$2 #Argument 2 from command line is the input file
exec 5< $INPUT_FILE #assign input file to file descriptor #5
while read -u 5 line #read from file descriptor 5 (input file)
do
echo "$line"
done
exec 5<&- #close file descriptor #5
This also won't work for the input I need.
while read line
do
echo "$line"
done <$2
Upvotes: 3
Views: 5522
Reputation: 2346
In bash scripts, I usually put code that reads from a file (or a pipe) in a function, where the redirection can be separated from the logic.
Also, when reading from a file or from STDIN, it's a good idea for the logic to not care which is which. So, it's best to capture STDIN into a temp file and then the rest of the file reading code is the same.
Here's an example script that reads from ARG 1 or from STDIN, and just counts the lines in the file. It also invokes wc -l
on the same input and shows the results from both methods.
#!/bin/bash
# default input is this script
input=$0
# If arg given, read from it
if (( $# > 0 )); then
input=$1
echo 1>&2 "Reading from $input"
else
# otherwise, read from STDIN
# since we're reading twice, need to capture it into
# a temp file
input=/tmp/$$.tmp
cat >$input
trap "rm -f $input" EXIT ERR HUP INT QUIT
echo 1>&2 "Reading from STDIN (saved to $input)"
fi
count_lines() {
local count=0
while read line ; do
let count+=1
done
echo $count
}
lines1=`count_lines <$input`
lines2=`wc -l <$input`
fmt="%15s: %d\n"
printf "$fmt" 'count_lines' $lines1
printf "$fmt" 'wc -l' $lines2
exit
Here are two invocations: one with a file on arg 1, and one with no argument, reading from STDIN:
$ ./t2.sh t2.sh
Reading from t2.sh
count_lines: 35
wc -l: 35
$ ./t2.sh <t2.sh
Reading from STDIN (saved to /tmp/8757.tmp)
count_lines: 35
wc -l: 35
Upvotes: 1
Reputation: 84642
A very in-artful if
statement will do the trick:
INPUT_FILE=$2 #Argument 2 from command line is the input file
if [ -f "$INPUT_FILE" ]; then
while read -r line
do
echo "$line"
done <"$INPUT_FILE"
else
while read -r line
do
echo "$line"
done
fi
Note: this presumes you are still looking for the filename as the 2nd argument.
I cannot take credit, but the artful
solution was already answered here: How to read from file or stdin in bash?
INPUT_FILE=${2:-/dev/stdin} #Argument 2 from command line is the input file
while read -r line
do
echo "$line"
done <"$INPUT_FILE"
exit 0
I was picking around with a solution like this but missed the stdin
device /dev/stdin
as the default for INPUT_FILES
. note this solution is limited to OS's with a proc-filesystem.
Upvotes: 2