如何从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"
来执行你的外部 GNUecho
,这是一个解决方法。 请参阅: 我如何回显“-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
可用。
如果没有给出论据,我们人为地设置-
作为第一个位置参数。 然后我们循环参数。 如果一个参数不是-
,我们使用exec
redirect来自文件名的标准input。 如果这个redirect成功,我们循环一个while
循环。 我正在使用标准的REPLY
variables,在这种情况下,您不需要重置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上的dash
testing)并且非常易读,但是这是一个有趣的问题:
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