Perl的钻石操作员:可以用bash完成吗?

时间:2009-04-09 17:22:06

标签: perl bash unix shell diamond-operator

在bash中是否有习惯模拟Perl钻石操作符的方法?有钻石操作员,

script.sh | ...

读取stdin的输入和

script.sh file1 file2 | ...

读取file1和file2的输入。

另一个约束是我想在script.sh中使用stdin来输入我自己的脚本以外的其他东西。下面的代码执行我想要的file1 file2 ... case,但不适用于stdin上提供的数据。

command - $@ <<EOF
some_code_for_first_argument_of_command_here
EOF

我更喜欢Bash解决方案,但任何Unix shell都可以。

编辑:为了澄清,这是script.sh的内容:

#!/bin/bash
command - $@ <<EOF
some_code_for_first_argument_of_command_here
EOF

我希望这个工作方式与钻石运算符在Perl中的工作方式相同,但它现在只处理文件名作为参数。

编辑2:我不能做任何事情

cat XXX | command

因为命令的stdin不是用户的数据。命令的stdin是here-doc中的数据。我希望用户数据进入我脚本的stdin,但它不能是我脚本中命令调用的标准输入。

7 个答案:

答案 0 :(得分:6)

当然,这完全可行:

#!/bin/bash
cat $@ | some_command_goes_here

然后,用户可以不带参数(或“ - ”)调用脚本来读取stdin或多个文件,所有文件都将被读取。

如果你想处理这些文件的内容(比如逐行),你可以这样做:

for line in $(cat $@); do
    echo "I read: $line"
done

修改:将$*更改为$@以处理文件名中的空格,这要归功于有用的评论。

答案 1 :(得分:4)

有点cheezy,但是怎么样

cat file1 file2 | script.sh

答案 2 :(得分:2)

我(就像其他人一样,似乎)对目标究竟是什么感到有些困惑,所以我会给出三个可能的答案,可能涵盖你真正想要的东西。首先,相对简单的目标是让脚本从文件列表(在命令行中提供)或从常规stdin中读取:

if [ $# -gt 0 ]; then
  exec < <(cat "$@")
fi
# From this point on, the script's stdin is redirected from the files
# (if any) supplied on the command line

注意:使用$ @的双引号是避免文件名中有趣字符(例如空格)出现问题的最佳方法 - $ *和未加引号的$ @都会弄乱这个问题。我在这里使用的&lt;()技巧是一个仅限bash的功能;它在后台触发cat以从命令行提供的文件中提供数据,然后我们使用exec将脚本的stdin替换为cat的输出。

......但这似乎并不是你真正想要的。您真正想要的是将提供的文件名或脚本的stdin 作为参数传递给脚本内的命令。这需要相反的过程:将脚本的stdin转换为文件(实际上是命名管道),其名称可以传递给命令。像这样:

if [[ $# -gt 0 ]]; then
  command "$@" <<EOF
here-doc goes here
EOF
else
  command <(cat) <<EOF
here-doc goes here
EOF
fi

这使用&lt;()通过cat将脚本的stdin清洗到命名管道,然后将其作为参数传递给命令。同时,命令的stdin取自here-doc。

现在,我认为这就是你想要做的,但它并不是你所要求的,而是两者从提供的文件中重定向脚本的stdin 将stdin传递给脚本中的命令。这可以通过结合上述技术来完成:

if [ $# -gt 0 ]; then
  exec < <(cat "$@")
fi

command <(cat) <<EOF
here-doc goes here
EOF

......虽然我不明白为什么你真的想这样做。

答案 3 :(得分:1)

Perl菱形运算符实际上遍历所有命令行参数,将每个参数视为文件名。它打开每个文件并逐行读取。这里有一些bash代码大致相同。

for f in "$@"
do
   # Do something with $f, such as...
   cat $f | command1 | command2
   -or-
   command1 < $f
   -or-
   # Read $f line-by-line
   cat $f | while read line_from_f
   do
      # Do stuff with $line_from_f
   done
done

答案 4 :(得分:1)

你想拿第一个参数并用它做一些事情,然后从任何指定的文件中读取,或者如果没有文件则读取stdin?

就个人而言,我建议使用getopt来指示使用“-a value”语法的参数来帮助消除歧义,但那只是我。这是我在bash 中没有 getopts的方式:

firstarg=${1?:usage: $0 arg [file1 .. fileN]}
shift
typeset -a files
if [[ ${#@} -gt 0 ]]
then
  files=( "$@" )
else
  files=( "/dev/stdin" )
fi
for file in "${files[@]}"
do
  whatever_you_want < "$file"
done

如果没有指定args,则?:运算符将会死亡,因为您似乎至少想要一个arg。抓住它之后,将args移动一个,然后使用剩余的args作为文件列表,或者如果没有其他args则使用bash特殊文件句柄“/ dev / stdin”。

我认为“如果没有指定文件,请使用/ dev / stdin - 否则使用命令行上的文件”这段可能就是你要找的,但其余的代码至少对于上下文。

答案 5 :(得分:0)

也有点羞涩,但是怎么样:

if [[ $# -eq 0 ]]
then
  # read from stdin
else
  # read from $* (args)
fi

如果您需要逐行阅读和处理(可能)并且不想复制/粘贴相同的代码两次(很可能),请在脚本中定义一个函数并传递这些行逐个执行此函数,并在所述函数中处理它们。

答案 6 :(得分:0)

为什么不在脚本中使用``cat @ * ?例如:

x=`cat $*`
echo $x