如何在shell脚本中将文件作为stdin传递

时间:2015-04-17 09:32:02

标签: linux bash shell

我有一个bash脚本,在这样调用时可以工作:./stats.sh -rows test_file

该程序基本上计算行平均值和中位数&amp;列平均值和中位数。现在到程序我想传递文件作为标准输入。但是当我运行此代码时,它会打印"you have 2 provide 2 arguments"。我应该做什么更改,以便代码将stdin作为文件。我的意思是说如果我想运行脚本,我也可以通过这种方式运行它./stats.sh -rows < test_file。我想获得这个功能!!

输入文件是:(由标签分隔的列

93  93  93  93  93  93  93  93  100
73  84  95  83  72  86  80  97  100
85  0   82  75  88  79  80  81  100
85  0   87  73  88  79  80  71  100
80  81  83  63  100 85  63  68  100
53  57  61  53  70  61  73  50  100
55  54  41  63  63  45  33  41  100
53  55  43  44  63  75  35  21  100
100 100 100 100 100 100 100 100 100

我所处理的代码是:

#! /bin/bash
clear
#the arguments below will check for your command line args whether you have provided corrctly or not
flag=0
if [ "$#" -eq 0 ]; then
    echo "Please provide arguments"
elif [ "$#" -lt 2 ]; then
     echo "You have to provide 2 arguments" >&2
     exit 1
elif [ "$#" -gt 2 ]; then
    echo "${#}"
    FILE= "${4}"
    if [ -f "${FILE}" ]; then
     flag=1
    else
      echo "You have provided more number of arguments" >&2
    fi
    exit 1
else
    echo "You have entered correct number of arguments"
fi
# the below code is the case code which checks whether you have -r/-rows or -c/-cols
option="${1}"
l1=0
sorted=()
case ${option} in 
   -rows| -r| -r*)
      if [ $flag -eq 1 ]; then
        FILE="${4}"
      else
        FILE="${2}"
      fi
      clear
      echo "Average  Median"
      lines=$(wc -l < "$FILE")
      while read -r line
      do
      len=0
      tot=0
      name=$line
      #array=(`echo $name | cut -d "    "  --output-delimiter=" " -f 1-`)
      IFS=' ' read -a array <<< "$name"  #if any error comes that might be with this line just check the spaces in the speech marks they should be 4 spaces as it is checking for tabs
      for element in "${array[@]}"
      do
          tot=$(expr $tot + $element)
          #let tot+=$element #you can use this as well to get the totals
          let len+=1
      done
      avg=($(printf "%.0f" $(echo "scale=2;$tot/$len" | bc)))
      readarray -t sorted < <(for a in "${array[@]}"; do echo "$a"; done | sort)
      no=`expr $len % 2`
      if [ $no -eq 0 ]; then
      mid=`expr $len / 2`
      echo "$avg   ${sorted[$mid]}"
      else
      if [ $lines -lt 2 ]; then
        mid=`expr $len / 2`
            echo "$avg   ${sorted[$mid]}"
      else
        l1=`expr $len / 2`
        mid=`expr $l1 + 1`
        echo "$avg   ${sorted[$mid]}"
      fi

      fi
      unset "array[@]"
      unset "sorted[@]"
      done < "$FILE"
      ;;

   -cols| -c| -c*)
      if [ $flag -eq 1 ]; then
        FILE="${4}"
      else
        FILE="${2}"
      fi
      #echo "cols"
      #echo "File name is $FILE"
      cols=$(head -1 "$FILE" | tr "\t" '\n' | wc -l)
      lines=$(wc -l < "$FILE")
      IFS=$'\t\n' read -d '' -r -a lins < "$FILE"
      while read line;do
      x=1
      read -a array <<< "$line" ##Split the line by spaces
      for element in "${!array[@]}"
      do
      row[${element}]=$((${row[${element}]}+${array[$element]})) ##For each column increment array variable by number in the column.
      ((x++))
      done
      done < "$FILE"
      echo "Averages: "
      for element in ${row[@]}
      do
      mean= printf "%.0f" $(echo "scale=2;$element/$lines" | bc) ##bc prints floating point numbers and then we round of using scale and .0f
      echo -n "$mean    "
      done
      printf "\n"
      echo "Medians: "
      for ((i=0;i<$cols;i++))
      do 
      carr=()
      for ((j=i;j<$lines * $cols;j=j+$cols)) 
      do
          carr+=(${lins[$j]})
      done
    IFS=$' \n' csort=($(sort <<<"${carr[*]}"))
    no=`expr $lines % 2`
    if [ $no -eq 0 ]; then
           mid=`expr $lines / 2`
           echo -n "${csort[$mid]}    "
    else
           if [ $lines -lt 2 ]; then
                  mid=`expr $lines / 2`
              echo -n "${csort[$mid]}    "
           else
              l1=`expr $lines / 2`
              mid=`expr $l1 + 1`
              echo -n "${csort[$mid]}    "
           fi
    fi
      done <<<"$lins"
      printf "\n"

      ;; 
   *)  
      echo "`basename ${0}`:usage: [-r|-rows rows] | [-c|-cols columns]" 
      exit 1 # Command to come out of the program with status 1
      ;; 
esac 
trap "echo ;exit" 1 2

3 个答案:

答案 0 :(得分:0)

使用您的bash脚本的read a洞察来读取/处理标准输入的内容。

示例:

skript.sh:

read a
echo "content of std in"
echo a

在这种情况下,cat test_file.txt | stats.sh将起作用。

答案 1 :(得分:0)

您也可以在脚本中将文件重定向到stdin:

# redirect FILE to stdin
exec 0<$FILE
# read from FILE
read VAR

答案 2 :(得分:0)

在许多现代体系结构中,如果脚本绝对要求您传递文件名参数,则可以传递/dev/stdin以使标准输入可用作类文件名实体。许多脚本也接受-作为特殊文件名来表示“不要打开文件;而是读取标准输入”。像./stats.sh -rows - <file这样的东西也可能有效。 (当然,这是一个愚蠢的例子,因为./stats.sh -rows file是等价的;但它对像stuff | ./stats.sh -rows -这样的东西很重要。)

然而,该脚本存在许多设计缺陷,并且至少有一个语法错误(在赋值FILE= "${4}"中不能有空格。第四个参数将被简单地计算为命令。这甚至可能具有安全隐患,具体取决于您运行此脚本的方式)。我会认真考虑用一两个简单的Perl或Awk脚本完全替换它。 shell对于你正在使用的算术类型来说并不是非常理想。