#!/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
14 回答
如果使用文件名调用脚本作为第一个参数
$1
,则从文件读取以下解决方案,否则从标准输入调用 .如果定义了替换
${1:-...}
,则取代$1
否则使用自己进程的标准输入的文件名 .也许最简单的解决方案是使用合并重定向运算符重定向stdin:
Stdin是文件描述符零 . 上面将输入管道传输给你的bash脚本到less的stdin .
Read more about file descriptor redirection .
这是最简单的方法:
用法:
要将stdin分配给变量,您可以使用:
STDIN=$(cat -)
或只是STDIN=$(cat)
因为不需要运算符(根据@mklement0 comment) .要从标准输入中解析每一行,请尝试以下脚本:
要从文件或stdin中读取(如果参数不存在),可以将其扩展为:
请参阅:stackoverflow SE上的How to read stdin when no arguments are passed?
只要
IFS
中断输入流,echo
解决方案就会添加新行 . @fgm's answer可以修改一下:我认为这是直截了当的方式:
问题中的Perl循环从命令行上的所有文件名参数读取,如果没有指定文件,则从标准输入读取 . 如果没有指定文件,我看到的答案似乎都处理单个文件或标准输入 .
虽然经常被嘲笑为UUOC(无用的
cat
),但有时cat
是这项工作的最佳工具,可以说这是其中之一:唯一的缺点是它创建了一个在子shell中运行的管道,因此在管道外部无法访问
while
循环中的变量赋值 .bash
方式是Process Substitution:这使得
while
循环在主shell中运行,因此循环中设置的变量可以在循环外部访问 .Perl的行为,OP中给出的代码可以不带或多个参数,如果参数是单个连字符
-
,这被理解为stdin . 此外,始终可以使用$ARGV
的文件名 . 迄今为止给出的答案都没有真正模仿Perl _1276132的纯粹Bash可能性 . 诀窍是适当使用exec
.文件名可在
$1
中找到 .如果没有给出参数,我们人为地将
-
设置为第一个位置参数 . 然后我们循环参数 . 如果参数不是-
,我们将文件名的标准输入重定向到exec
. 如果此重定向成功,我们循环while
循环 . 我正在使用标准REPLY
变量,在这种情况下,您不需要重置IFS
. 如果你想要另一个名字,你必须像这样重置IFS
(当然,除非你不这样做):更精确地...
请尝试以下代码:
代码
${1:-/dev/stdin}
只会理解第一个参数,那么,这个怎么样 .以下使用标准
sh
(在Debian上使用_1276150测试)并且非常易读,但这是一个品味问题:详细信息:如果第一个参数非空,则
cat
表示该文件,否则为cat
标准输入 . 然后整个if
语句的输出由commands_and_transformations
处理 .我发现这些答案都不可接受 . 特别是,接受的答案仅处理第一个命令行参数并忽略其余部分 . 它试图模拟的Perl程序处理所有命令行参数 . 所以接受的答案甚至没有回答这个问题 . 其他答案使用bash扩展,添加不必要的'cat'命令,仅适用于回输输入到输出的简单情况,或者只是不必要的复杂 .
但是,我必须给他们一些信任,因为他们给了我一些想法 . 这是完整的答案:
我结合了上述所有答案并创建了一个适合我需求的shell函数 . 这是我2台Windows10机器的cygwin终端,我在它们之间有一个共享文件夹 . 我需要能够处理以下事项:
cat file.cpp | tx
tx < file.cpp
tx file.cpp
如果指定了特定的文件名,我需要在复制期间使用相同的文件名 . 在输入数据流通过管道传输的情况下,我需要生成具有小时分和秒的临时文件名 . 共享主文件夹具有一周中的几天的子文件夹 . 这是出于组织目的 .
看哪,这是我需要的终极剧本:
如果您有任何方式可以看到进一步优化这一点,我想知道 .
怎么样