如何从Bash中的文件或标准input读取?

在Perl中,以下代码将从命令行参数或stdin中指定的文件中读取:

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

这很方便。 我只是想知道从bash中的文件或标准input读取最简单的方法是什么。

如果使用文件名作为第一个参数$1调用脚本,则从文件读取以下解决scheme,否则从标准input读取脚本。

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

如果定义${1:-...} ,那么replace${1:-...} $1 ,否则使用自己进程的标准input的文件名。

也许最简单的解决scheme是用合并redirect操作符redirectstdin:

 #!/bin/bash less <&0 

标准input是文件描述符零。 上面发送到您的bash脚本inputinput到less的标准input。

阅读更多关于文件描述符redirect的信息 。

这是最简单的方法:

 #!/bin/sh cat - 

用法:

 $ echo test | sh my_script.sh test 

要将stdin分配给variables,可以使用: STDIN=$(cat -)或者仅仅是STDIN=$(cat)因为操作符是不必要的(根据@ mklement0注释 )。


要从标准inputparsing每一行,请尝试以下脚本:

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

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

 #!/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") 

笔记:

read -r – 不要以任何特殊的方式处理反斜线字符。 考虑每个反斜杠是input行的一部分。

– 在不设置IFS的情况下,默认情况下会忽略线条开始和结束处的空格制表符的序列(修剪)。

– 使用printf而不是echo来避免在行由单个-e-n-E组成时打印空行。 然而,通过使用env POSIXLY_CORRECT=1 echo "$line"来执行你的外部 GNU echo ,这是一个解决方法。 请参阅: 我如何回显“-e”?

请参阅: 如何读取stdin时没有parameter passing? 在stackoverflow SE

我认为这是直截了当的方式:

 $ 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 

每当IFS中断inputstream时, echo解决scheme都会添加新行。 @ fgm的答案可以修改一下:

 cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}" 

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

虽然经常被人们嘲笑为UUOC (无用cat ),但有时候cat是最好的工具,而这也是其中之一:

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

唯一的缺点就是它创build了一个在子shell中运行的pipe道,所以像while循环中的variables赋值之类的东西在stream水线之外是不可访问的。 bash方式是stream程replace :

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

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

Perl的行为,在OP中给出的代码可以不带任何或几个参数,如果一个参数是一个连字符-这被理解为标准input。 而且,总是可以用$ARGV来指定文件名。 到目前为止所给出的答案都没有真正模仿Perl在这些方面的行为。 这是纯粹的Bash可能性。 诀窍是适当地使用exec

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

文件名在$1可用。

如果没有给出论据,我们人为地设置-作为第一个位置参数。 然后我们循环参数。 如果一个参数不是- ,我们使用execredirect来自文件名的标准input。 如果这个redirect成功,我们循环一个while循环。 我正在使用标准的REPLYvariables,在这种情况下,您不需要重置IFS 。 如果你想要另一个名字,你必须像这样重置IFS (当然,除非你不需要这个,并且知道你在做什么):

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

更精确地…

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

请尝试下面的代码:

 while IFS= read -r line; do echo "$line" done < file 

代码${1:-/dev/stdin}只会理解第一个参数,所以呢,这个怎么样。

 ARGS='$*' if [ -z "$*" ]; then ARGS='-' fi eval "cat -- $ARGS" | while read line do echo "$line" done 

下面的内容适用于标准的sh (用Debian上的dashtesting)并且非常易读,但是这是一个有趣的问题:

 if [ -n "$1" ]; then cat "$1" else cat fi | commands_and_transformations 

详情:如果第一个参数是非空的,那么cat那个文件,否则cat标准input。 然后整个if语句的输出由commands_and_transformations处理。

怎么样

 for line in `cat`; do something($line); done