如何通过一个文件作为标准输入的shell脚本(how to pass a file as stdin

2019-10-22 16:36发布

我有一个bash脚本的时候这样调用它的工作原理: ./stats.sh -rows test_file

该方案基本上计算行平均值和中位数及平均数柱和中位数。 我们的节目,我想通过文件作为标准输入。 但是当我运行这段代码将打印"you have 2 provide 2 arguments" 。 我应该有什么样的变化,使以使代码采用标准输入作为一个文件。 我的意思是说如果我要运行脚本我可以通过这种方式运行,以及./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

Answer 1:

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

例:

skript.sh:

read a
echo "content of std in"
echo a

在这种情况下, cat test_file.txt | stats.sh cat test_file.txt | stats.sh会工作。



Answer 2:

您也可以重定向一个文件到您的脚本中的标准输入:

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


Answer 3:

在许多现代建筑,如果脚本确实需要你通过一个文件名参数,你可以传递/dev/stdin有可作为文件名类似实体上的标准输入。 许多脚本也接受-作为一种特殊的文件名来表示“不打开一个文件,读取标准输入,而不是”。 喜欢的东西./stats.sh -rows - <file可能实际工作,太。 (当然,这是一个愚蠢的例子,因为./stats.sh -rows file等同,但它很重要的东西,喜欢stuff | ./stats.sh -rows -

然而,剧本从众多的设计缺陷所害,而且至少有一个语法错误(你不能在分配的空间FILE= "${4}" 。第四个参数将被简单地评价为命令。这甚至有安全隐患,这取决于你如何运行此脚本)。 我会认真考虑完全用一名两个简单的Perl或awk中的脚本替换它。 外壳是不很理想的排序算法的你与任务吧。



文章来源: how to pass a file as stdin in a shell script
标签: linux bash shell