编写bash脚本以接受所有可能方向的参数和输入的最简单方法是什么(类似于sort -k1 -r)?

Jes*_*pin 3 bash stdin command-line-parsing

我想编写一个bash脚本,可以处理类似于许多内置bash命令的参数和输入.例如,像sort一样,它可以处理

sort -k 1 -r input.txt
sort input.txt -k 1 -r
cat input.txt | sort -k 1 -r
sort -k 1 -r < input.txt
sort -k 1 -r <(cat input.txt)
Run Code Online (Sandbox Code Playgroud)

我希望我的脚本能够以类似的方式处理参数和输入

myscript.sh -i 3 -b 4 input.txt
myscript.sh input.txt -i 3 -b 4
cat input.txt | myscript.sh -i 3 -b 4
myscript.sh -i 3 -b 4 < input.txt
myscript.sh -i 3 -b 4 <(cat input.txt)
Run Code Online (Sandbox Code Playgroud)

到目前为止,我只使用了"read"和"getopts"中的一些功能,并认为如果我自己尝试这样做可能会出错.

为了让我更清楚地说明我的问题,请输入input.text的内容

aaa
bbb
ccc
Run Code Online (Sandbox Code Playgroud)

我想使用参数i和b中的值来做一些事情,但我只是在这个例子中打印出来.我想要的样本输出是

i : 3
b : 4
aaa
bbb
ccc
Run Code Online (Sandbox Code Playgroud)

编写代码来处理上面的示例命令以提供此输出的最佳方法是什么?

下面是从@chepner的三明治概念中得到的代码,这是迄今为止最好的代码.

#!/bin/bash -l
die () {
    echo >&2 "[exception] $@"
    exit 1
}

#parse param
while getopts "i:b:" OPTION; do
  case "$OPTION" in
    i)
      i="$OPTARG"
      ;;
    b)
      b="$OPTARG"
      ;;
    *)
      die "unrecognized option"
      ;;
  esac
done

if [ -e tmpfile ] 
then 
    rm tmpfile 
fi

shift $(($OPTIND - 1))
echo "i : "$i
echo "b : "$b
cat $1 > tmpfile

if read -t 0; then
    cat >> tmpfile
fi

cat tmpfile
Run Code Online (Sandbox Code Playgroud)

che*_*ner 6

执行摘要:您可以使用它read -t 0来测试标准输入上是否有任何可用输入.它将以状态0退出,数据立即可用于标准输入(通过管道或重定向文件),如果没有则立即可用(例如,仍然连接到键盘).然后,您可以根据是否需要从标准输入读取来分支脚本.

if read -t 0; then
    # Do one thing
else
    # Do something else
fi
Run Code Online (Sandbox Code Playgroud)

对我来说棘手的部分是编写脚本,这样如果你不管道任何东西,它就不会阻止读取标准输入.

这似乎有效; 改进欢迎.首先,消耗标准输入的所有内容; 然后处理作为参数给出的文件.

# The first call to read only succeeds when there is input
# available on standard input. It does not actually consume
# a line, though.
if read -t 0; then
    # Read standard input normally
    while read line; do
        echo $line
    done
fi

# I'll assume you populate an array called input files
# while processing your arguments
for file in "${inputfiles[@]"; do 
    cat $file
done
Run Code Online (Sandbox Code Playgroud)

这是一个无意义的包装器,sort只是展示了将标准输入与其他输入文件组合的另一种方式:

if read -t 0; then
    cat | sort fileA fileB
else
    sort fileA file B
fi
Run Code Online (Sandbox Code Playgroud)

一个稍微有用的命令可能是sandwich,它在命令行上给出的两个文件之间输出其标准输入(如果有的话).

#!/bin/bash

cat "$1"    # Output the first file
read -t 0 && cat # Pass through the standard input, if there is any
cat "$2"    # Output the second file

# cat "$1" - "$2" is almost the same, but requires standard input.
Run Code Online (Sandbox Code Playgroud)

有些电话sandwich可能会

$ sandwich header.txt footer.txt
$ sandwich header.txt footer.txt < body.txt
$ cat chapter1.txt chapter2.txt | sandwich <(echo "My header") <(echo "My footer") 
Run Code Online (Sandbox Code Playgroud)

这不太有用,所以还有改进的余地......

$ cat - | sandwich header.txt footer.txt
Run Code Online (Sandbox Code Playgroud)