如何在 Bash 中读取文件或 STDIN?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6980090/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read from a file or STDIN in Bash?
提问by Dagang
The following Perl script (my.pl
) can read from either the file on the command line args or from STDIN:
以下 Perl 脚本 ( my.pl
) 可以从命令行 args 上的文件或从 STDIN 中读取:
while (<>) {
print($_);
}
perl my.pl
will read from STDIN, while perl my.pl a.txt
will read from a.txt
. This is very convenient.
perl my.pl
将从 STDIN 读取,而perl my.pl a.txt
将从a.txt
. 这是非常方便的。
Wondering is there an equivalent in Bash?
想知道 Bash 中是否有等价物?
回答by Fritz G. Mehner
The following solution reads from a file if the script is called
with a file name as the first parameter $1
otherwise from standard input.
如果使用文件名作为第一个参数调用脚本,则以下解决方案从文件中读取,$1
否则来自标准输入。
while read line
do
echo "$line"
done < "${1:-/dev/stdin}"
The substitution ${1:-...}
takes $1
if defined otherwise
the file name of the standard input of the own process is used.
如果已定义,则替换${1:-...}
将$1
使用自身进程的标准输入的文件名。
回答by Ryan Ballantyne
Perhaps the simplest solution is to redirect stdin with a merging redirect operator:
也许最简单的解决方案是使用合并重定向运算符重定向 stdin:
#!/bin/bash
less <&0
Stdin is file descriptor zero. The above sends the input piped to your bash script into less's stdin.
Stdin 是文件描述符零。以上将通过管道传输到 bash 脚本的输入发送到 less 的标准输入中。
回答by kenorb
Here is the simplest way:
这是最简单的方法:
#!/bin/sh
cat -
Usage:
用法:
$ echo test | sh my_script.sh
test
To assign stdinto the variable, you may use: STDIN=$(cat -)
or just simply STDIN=$(cat)
as operator is not necessary (as per @mklement0 comment).
要将stdin分配给变量,您可以使用:STDIN=$(cat -)
或者只是STDIN=$(cat)
因为不需要运算符(根据@mklement0 注释)。
To parse each line from the standard input, try the following script:
要解析标准输入中的每一行,请尝试以下脚本:
#!/bin/bash
while IFS= read -r line; do
printf '%s\n' "$line"
done
To read from the file or stdin(if argument is not present), you can extend it to:
要从文件或标准输入中读取(如果参数不存在),您可以将其扩展为:
#!/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 Spaceand Tabat the beginning and end of the lines are ignored (trimmed).- Use
printf
instead ofecho
to avoid printing empty lines when the line consists of a single-e
,-n
or-E
. However there is a workaround by usingenv POSIXLY_CORRECT=1 echo "$line"
which executes your externalGNUecho
which supports it. See: How do I echo "-e"?
笔记:
-
read -r
- 不要以任何特殊方式对待反斜杠字符。将每个反斜杠视为输入行的一部分。-如果不设置
IFS
,默认情况下的序列Space和Tab在线条的开始和结束都被忽略(修剪)。-当行由单个,或组成时,使用
printf
代替echo
来避免打印空行。但是,有一种解决方法是使用它来执行支持它的外部GNU 。请参阅:如何回显“-e”?-e
-n
-E
env POSIXLY_CORRECT=1 echo "$line"
echo
See: How to read stdin when no arguments are passed?at stackoverflow SE
请参阅:如何在不传递参数时读取标准输入?在 stackoverflow SE
回答by Amir Mehler
I think this is the straight-forward way:
我认为这是直接的方法:
$ 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
回答by David Souther
The echo
solution adds new lines whenever IFS
breaks the input stream. @fgm's answercan be modified a bit:
该echo
解决方案在IFS
中断输入流时添加新行。@fgm 的答案可以稍微修改一下:
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
回答by Jonathan Leffler
The Perl loop in the question reads from allthe file name arguments on the command line, or from standard input if no files are specified. The answers I see all seem to process a single file or standard input if there is no file specified.
问题中的 Perl 循环从命令行上的所有文件名参数中读取,如果未指定文件,则从标准输入中读取。如果没有指定文件,我看到的所有答案似乎都在处理单个文件或标准输入。
Although often derided accurately as UUOC(Useless Use of cat
), there are times when cat
is the best tool for the job, and it is arguable that this is one of them:
尽管经常被准确地嘲笑为UUOC(Useless Use of cat
),但有时cat
它是完成这项工作的最佳工具,并且可以说这是其中之一:
cat "$@" |
while read -r line
do
echo "$line"
done
The only downside to this is that it creates a pipeline running in a sub-shell, so things like variable assignments in the while
loop are not accessible outside the pipeline. The bash
way around that is Process Substitution:
唯一的缺点是它创建了一个在子 shell 中运行的管道,因此while
循环中的变量赋值等内容在管道外无法访问。在bash
周围的办法是进程替换:
while read -r line
do
echo "$line"
done < <(cat "$@")
This leaves the while
loop running in the main shell, so variables set in the loop are accessible outside the loop.
这使得while
循环在主 shell 中运行,因此循环中设置的变量可以在循环外访问。
回答by gniourf_gniourf
Perl's behavior, with the code given in the OP can take none or several arguments, and if an argument is a single hyphen -
this is understood as stdin. Moreover, it's always possible to have the filename with $ARGV
.
None of the answers given so far really mimic Perl's behavior in these respects. Here's a pure Bash possibility. The trick is to use exec
appropriately.
Perl 的行为,OP 中给出的代码可以不带参数,也可以带多个参数,如果参数是单个连字符,则将其-
理解为标准输入。此外,文件名总是可以带有$ARGV
. 迄今为止给出的答案都没有真正模仿 Perl 在这些方面的行为。这是一个纯粹的 Bash 可能性。诀窍是exec
适当地使用。
#!/bin/bash
(($#)) || set -- -
while (($#)); do
{ [[ = - ]] || exec < ""; } &&
while read -r; do
printf '%s\n' "$REPLY"
done
shift
done
Filename's available in $1
.
文件名在$1
.
If no arguments are given, we artificially set -
as the first positional parameter. We then loop on the parameters. If a parameter is not -
, we redirect standard input from filename with exec
. If this redirection succeeds we loop with a while
loop. I'm using the standard REPLY
variable, and in this case you don't need to reset IFS
. If you want another name, you must reset IFS
like so (unless, of course, you don't want that and know what you're doing):
如果没有给出参数,我们人为地设置-
为第一个位置参数。然后我们循环参数。如果参数不是-
,我们使用 重定向来自文件名的标准输入exec
。如果此重定向成功,我们将循环while
循环。我正在使用标准REPLY
变量,在这种情况下,您不需要 reset IFS
。如果你想要另一个名字,你必须IFS
像这样重置(当然,除非你不想要那个并且知道你在做什么):
while IFS= read -r line; do
printf '%s\n' "$line"
done
回答by Webthusiast
Please try the following code:
请尝试以下代码:
while IFS= read -r line; do
echo "$line"
done < file
回答by sorpigal
More accurately...
更精确地...
while IFS= read -r line ; do
printf "%s\n" "$line"
done < file
回答by Takahiro Onodera
Code ${1:-/dev/stdin}
will just understand first argument, so, how about this.
代码${1:-/dev/stdin}
只会理解第一个参数,所以,这个怎么样。
ARGS='$*'
if [ -z "$*" ]; then
ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done