下面的 Perl 脚本(my.pl
)可以从命令行参数指定的文件或者标准输入(STDIN)读取:
while (<>) {
print($_);
}
perl my.pl
会从标准输入读取,而 perl my.pl a.txt
会从文件 a.txt
中读取。这非常方便。
Bash 中有类似的功能吗?
两种主要的方式:
稍微修改之前的答案:
Use cat
, not less
. It's faster and you don't need pagination.
Use $1
to read from first argument file (if present) or $*
to read from all files (if present). If these variables are empty, read from stdin (like cat
does)
#!/bin/bash
cat $* | ...
向命名管道中写入数据略微复杂,但这样可以将标准输入(或文件)视为单个文件:
Create pipe with mkfifo
.
Parallelize the writing process. If the named pipe is not read from, it may block otherwise.
For redirecting stdin into a subprocess (as necessary in this case), use <&0
(unlike what others have been commenting, this is not optional here).
#!/bin/bash
mkfifo /tmp/myStream
cat $* <&0 > /tmp/myStream & # separate subprocess (!)
AddYourCommandHere /tmp/myStream # process input like a file,
rm /tmp/myStream # cleaning up
仅在没有给定参数时创建命名管道。这可能对从文件中读取更稳定,因为命名管道偶尔会阻塞。
#!/bin/bash
FILES=$*
if echo $FILES | egrep -v . >&/dev/null; then # if $FILES is empty
mkfifo /tmp/myStream
cat <&0 > /tmp/myStream &
FILES=/tmp/myStream
fi
AddYourCommandHere $FILES # do something ;)
if [ -e /tmp/myStream ]; then
rm /tmp/myStream
fi
for file in $FILES; do
AddYourCommandHere $file
done
sh
(在Debian上测试过Dash),并且非常易读,但这是一种口味的问题:if [ -n "$1" ]; then
cat "$1"
else
cat
fi | commands_and_transformations
cat
的输入,否则将标准输入作为cat
的输入。然后整个if
语句的输出由commands_and_transformations
处理。cat "${1:--}" | any_command
。读取 shell 变量并将其回显可能适用于小文件,但不太适用于大规模操作。 - Andreas Spindler[ -n "$1" ]
可以简化为 [ "$1" ]
。 - agccat file.cpp | tx
tx < file.cpp
tx file.cpp
tx ()
{
if [ $# -eq 0 ]; then
local TMP=/tmp/tx.$(date +'%H%M%S')
while IFS= read -r line; do
echo "$line"
done < /dev/stdin > $TMP
cp $TMP //$OTHER/stargate/$(date +'%a')/
rm -f $TMP
else
[ -r $1 ] && cp $1 //$OTHER/stargate/$(date +'%a')/ || echo "cannot read file"
fi
}
代码${1:-/dev/stdin}
只会理解第一个参数,所以你可以这样使用:
ARGS='$*'
if [ -z "$*" ]; then
ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done
现有答案中的大多数示例使用循环,立即将每行内容从标准输入中读取并输出。这可能不是你真正想做的事情。
在许多情况下,您需要编写一个调用仅接受文件参数的命令的脚本。但在您的脚本中,您可能还想支持标准输入。在这种情况下,您需要首先完全读取标准输入,然后将其作为文件提供。
让我们看一个例子。下面的脚本打印以PEM格式传递的证书(作为文件或通过标准输入)的详细信息。
# print-cert script
content=""
while read line
do
content="$content$line\n"
done < "${1:-/dev/stdin}"
# Remove the last newline appended in the above loop
content=${content%\\n}
# Keytool accepts certificate only via a file, but in our script we fix this.
keytool -printcert -v -file <(echo -e $content)
# Read from file
cert-print mycert.crt
# Owner: CN=....
# Issuer: ....
# ....
# Or read from stdin (by pasting)
cert-print
#..paste the cert here and press enter
# Ctl-D
# Owner: CN=....
# Issuer: ....
# ....
# Or read from stdin by piping to another command (which just prints the cert(s) ). In this case we use openssl to fetch directly from a site and then print its info.
echo "" | openssl s_client -connect www.google.com:443 -prexit 2>/dev/null \
| sed -n -e '/BEGIN\ CERTIFICATE/,/END\ CERTIFICATE/ p' \
| cert-print
# Owner: CN=....
# Issuer: ....
# ....
我不认为这些答案中有任何一个是可接受的。特别是,被接受的答案只处理第一个命令行参数并忽略其余部分。它试图模拟的 Perl 程序处理所有的命令行参数。因此,被接受的答案甚至没有回答问题。
其他答案使用 Bash 扩展,添加不必要的“cat”命令,只适用于将输入输出回显的简单情况,或者只是不必要地复杂。
然而,我必须承认,他们给了我一些想法。以下是完整的答案:
#!/bin/sh
if [ $# = 0 ]
then
DEFAULT_INPUT_FILE=/dev/stdin
else
DEFAULT_INPUT_FILE=
fi
# Iterates over all parameters or /dev/stdin
for FILE in "$@" $DEFAULT_INPUT_FILE
do
while IFS= read -r LINE
do
# Do whatever you want with LINE here.
echo $LINE
done < "$FILE"
done
作为解决方法,您可以在/dev目录中使用stdin
设备:
....| for item in `cat /dev/stdin` ; do echo $item ;done
随着...
while read line
do
echo "$line"
done < "${1:-/dev/stdin}"
然后用for决定:忽略了来自标准输入的1265个字符。使用“-stdin”或“-”来告诉如何处理管道输入。
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
gniourf_gniourf的答案是正确的,但使用了很多bashisms。由于这个问题是谷歌的顶级结果,这里提供一个符合POSIX标准的版本:
#!/bin/sh
if [ $# -eq 0 ]; then
set -- -
fi
for f in "$@"; do
if [ "$f" = - ] || exec < "$f"; then
while IFS= read -r line; do
printf '%s\n' "$line"
done
done
#!/bin/sh
[ $# -eq 0 ] || set -- -
for f; do
{ [ "$f" = - ] || exec < "$f"; } &&
while IFS= read -r line; do
printf '%s\n' "$line"
done
done
这个在终端上使用起来很容易:
$ echo '1\n2\n3\n' | while read -r; do echo $REPLY; done
1
2
3
find
命令的输出进行排序的东西,这个方法完美地解决了我的问题。 - undefined