下面的Perl脚本(my.pl)既可以从命令行参数中的文件读取,也可以从标准输入(STDIN)读取:
while (<>) {
print($_);
}
Perl my.pl将从标准输入中读取,而Perl my.pl .txt将从a.txt中读取。这很方便。
Bash中也有类似的功能吗?
下面的Perl脚本(my.pl)既可以从命令行参数中的文件读取,也可以从标准输入(STDIN)读取:
while (<>) {
print($_);
}
Perl my.pl将从标准输入中读取,而Perl my.pl .txt将从a.txt中读取。这很方便。
Bash中也有类似的功能吗?
当前回答
我觉得这些答案都不能接受。特别是,接受的答案只处理第一个命令行参数,而忽略其余的。它试图模拟的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
其他回答
问题中的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中运行,因此在循环中设置的变量可以在循环外访问。
代码${1:-/dev/stdin}只理解第一个参数,所以你可以这样使用:
ARGS='$*'
if [ -z "$*" ]; then
ARGS='-'
fi
eval "cat -- $ARGS" | while read line
do
echo "$line"
done
与…
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
两种主要方法:
要么将参数文件和stdin管道到单个流和处理,就像stdin(流方法) 或者重定向stdin(和参数文件)到一个命名管道和进程,就像一个文件(文件方法)
流的方法
对之前答案的小修改:
使用cat,而不是更少。这样更快,而且不需要分页。 使用$1从第一个参数文件读取(如果存在)或$*从所有文件读取(如果存在)。如果这些变量为空,则从stdin读取(像cat一样) # !/bin/bash 猫$* |…
文件的方法
写入一个命名管道有点复杂,但这允许你把stdin(或文件)当作一个单独的文件:
使用mkfifo创建管道。 并行化写作过程。如果未读取命名管道,则可能阻塞。 要将stdin重定向到子进程(在这种情况下是必要的),使用<&0(与其他注释不同,这在这里不是可选的)。 # !/bin/bash mkfifo / tmp / myStream /tmp/myStream & #分离子进程(!) AddYourCommandHere /tmp/myStream #进程输入像一个文件, rm /tmp/myStream #正在清理
文件方法:变化
仅在没有给出参数的情况下创建命名管道。这对于从文件中读取可能更稳定,因为命名管道偶尔会阻塞。
#!/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
此外,它允许你迭代文件和stdin,而不是连接到一个单一的流:
for file in $FILES; do
AddYourCommandHere $file
done
作为一种变通方法,你可以使用/dev目录下的stdin设备:
....| for item in `cat /dev/stdin` ; do echo $item ;done