下面的Perl脚本(my.pl)既可以从命令行参数中的文件读取,也可以从标准输入(STDIN)读取:

while (<>) {
   print($_);
}

Perl my.pl将从标准输入中读取,而Perl my.pl .txt将从a.txt中读取。这很方便。

Bash中也有类似的功能吗?


当前回答

我将上述所有答案结合起来,创建了一个适合我需要的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
}

如果有任何方法,你可以看到进一步优化这一点,我想知道。

其他回答

这个很容易在终端上使用:

$ echo '1\n2\n3\n' | while read -r; do echo $REPLY; done
1
2
3

下面的解决方案从文件读取(如果脚本调用时将文件名作为第一个参数$1),否则从标准输入读取。

while read line
do
  echo "$line"
done < "${1:-/dev/stdin}"

替换${1:-…}如果定义了,则接受$1。否则,使用自己进程的标准输入的文件名。

Use:

for line in `cat`; do
    something($line);
done

我觉得这些答案都不能接受。特别是,接受的答案只处理第一个命令行参数,而忽略其余的。它试图模拟的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

从stdin读入变量或从文件读入变量。

现有答案中的大多数示例使用循环,当它从stdin读取时立即回显每一行。这可能不是你真正想做的。

在许多情况下,您需要编写一个脚本来调用只接受file参数的命令。但是在你的脚本中,你可能也想要支持stdin。在这种情况下,您需要首先读取完整的stdin,然后将其作为文件提供。

让我们看一个例子。下面的脚本打印一个证书的证书详细信息(以PEM格式),该证书可以作为文件传递,也可以通过stdin传递。

# 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: ....
# ....