下面的Perl脚本(my.pl)既可以从命令行参数中的文件读取,也可以从标准输入(STDIN)读取:

while (<>) {
   print($_);
}

Perl my.pl将从标准输入中读取,而Perl my.pl .txt将从a.txt中读取。这很方便。

Bash中也有类似的功能吗?


当前回答

这是最简单的方法:

#!/bin/sh
cat -

用法:

$ echo test | sh my_script.sh
test

要将stdin分配给变量,您可以使用:stdin =$(cat -)或只是简单的stdin =$(cat)作为操作符是不必要的(根据@mklement0注释)。


要解析标准输入中的每一行,请尝试以下脚本:

#!/bin/bash
while IFS= read -r line; do
  printf '%s\n' "$line"
done

要从文件或stdin中读取(如果参数不存在),您可以将其扩展为:

#!/bin/bash
file=${1--} # POSIX-compliant; ${1:--} can be used either.
while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")

Notes: - read -r - Do not treat a backslash character in any special way. Consider each backslash to be part of the input line. - Without setting IFS, by default the sequences of Space and Tab at the beginning and end of the lines are ignored (trimmed). - Use printf instead of echo to avoid printing empty lines when the line consists of a single -e, -n or -E. However there is a workaround by using env POSIXLY_CORRECT=1 echo "$line" which executes your external GNU echo which supports it. See: How do I echo "-e"?

参见:当没有参数传递时如何读取stdin ?在stackoverflow SE

其他回答

作为一种变通方法,你可以使用/dev目录下的stdin设备:

....| for item in `cat /dev/stdin` ; do echo $item ;done

问题中的Perl循环从命令行上的所有文件名参数读取,如果没有指定文件,则从标准输入读取。如果没有指定文件,我看到的答案似乎都在处理单个文件或标准输入。

虽然经常被嘲笑为UUOC(无用地使用猫),但有时猫是工作的最佳工具,这是有争议的:

cat "$@" |
while read -r line
do
    echo "$line"
done

唯一的缺点是它创建了一个在子shell中运行的管道,因此while循环中的变量赋值之类的东西在管道之外是不可访问的。bash的方法是进程替换:

while read -r line
do
    echo "$line"
done < <(cat "$@")

这使得while循环在主shell中运行,因此在循环中设置的变量可以在循环外访问。

我认为这是最直接的方法:

$ cat reader.sh
#!/bin/bash
while read line; do
  echo "reading: ${line}"
done < /dev/stdin

--

$ cat writer.sh
#!/bin/bash
for i in {0..5}; do
  echo "line ${i}"
done

--

$ ./writer.sh | ./reader.sh
reading: line 0
reading: line 1
reading: line 2
reading: line 3
reading: line 4
reading: line 5

与…

while read line
do
    echo "$line"
done < "${1:-/dev/stdin}"

我得到以下输出:

忽略标准输入中的1265个字符。使用“-stdin”或“-”来说明如何处理管道输入。

然后决定用for:

Lnl=$(cat file.txt | wc -l)
echo "Last line: $Lnl"
nl=1

for num in `seq $nl +1 $Lnl`;
do
    echo "Number line: $nl"
    line=$(cat file.txt | head -n $nl | tail -n 1)
    echo "Read line: $line"
    nl=$[$nl+1]
done

Perl的行为,OP中给出的代码可以不带参数,也可以有多个参数,如果一个参数是一个连字符-这被理解为stdin。此外,文件名总是可能带有$ARGV。 到目前为止给出的答案都没有真正模仿Perl在这些方面的行为。这里有一个纯Bash的可能性。诀窍在于适当地使用exec。

#!/bin/bash

(($#)) || set -- -
while (($#)); do
   { [[ $1 = - ]] || exec < "$1"; } &&
   while read -r; do
      printf '%s\n' "$REPLY"
   done
   shift
done

文件名可在$1。

如果没有给出参数,则人为地将-设置为第一个位置参数。然后循环参数。如果参数不是-,则使用exec重定向filename中的标准输入。如果重定向成功,则使用while循环进行循环。我使用标准的REPLY变量,在这种情况下,您不需要重置IFS。如果你想要另一个名字,你必须像这样重置IFS(当然,除非你不想这样做,并且知道你在做什么):

while IFS= read -r line; do
    printf '%s\n' "$line"
done