下面的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循环从命令行上的所有文件名参数读取,如果没有指定文件,则从标准输入读取。如果没有指定文件,我看到的答案似乎都在处理单个文件或标准输入。
虽然经常被嘲笑为UUOC(无用地使用猫),但有时猫是工作的最佳工具,这是有争议的:
cat "$@" |
while read -r line
do
echo "$line"
done
唯一的缺点是它创建了一个在子shell中运行的管道,因此while循环中的变量赋值之类的东西在管道之外是不可访问的。bash的方法是进程替换:
while read -r line
do
echo "$line"
done < <(cat "$@")
这使得while循环在主shell中运行,因此在循环中设置的变量可以在循环外访问。
其他回答
作为一种变通方法,你可以使用/dev目录下的stdin设备:
....| for item in `cat /dev/stdin` ; do echo $item ;done
我将上述所有答案结合起来,创建了一个适合我需要的shell函数。这是我的两台Windows 10机器的Cygwin终端,我在它们之间有一个共享文件夹。我需要能够处理以下问题:
Cat文件。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
}
如果有任何方法,你可以看到进一步优化这一点,我想知道。
每当IFS中断输入流时,回显解决方案就添加新行。@fgm的回答可以稍微修改一下:
cat "${1:-/dev/stdin}" > "${2:-/dev/stdout}"
我觉得这些答案都不能接受。特别是,接受的答案只处理第一个命令行参数,而忽略其余的。它试图模拟的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的行为,OP中给出的代码可以不带参数,也可以有多个参数,如果一个参数是一个连字符-这被理解为stdin。此外,文件名总是可能带有$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重定向filename中的标准输入。如果重定向成功,则使用while循环进行循环。我使用标准的REPLY变量,在这种情况下,您不需要重置IFS。如果你想要另一个名字,你必须像这样重置IFS(当然,除非你不想这样做,并且知道你在做什么):
while IFS= read -r line; do
printf '%s\n' "$line"
done