简体   繁体   中英

linux shell script read from stdin if no file

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

InArtful Solution

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.


Artful Solution

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM