English 中文(简体)
Perl的菱形运算符:可以在bash中完成吗?
原标题:
  • 时间:2009-04-09 17:22:05
  •  标签:

有没有一种惯用的方法可以在bash中模拟Perl的菱形运算符?对于钻石操作者,

script.sh | ...

读取stdin作为其输入

script.sh file1 file2 | ...

读取file1和file2作为其输入。

另一个限制是,我想将script.sh中的stdin用于除输入自己的脚本之外的其他内容。下面的代码完成了我想要的file1 file2。。。上面的情况,但不适用于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 for命令是我在here文档中的数据。我希望用户数据进入脚本的stdin,但它不能是脚本内命令调用的stdin。

问题回答

当然,这是完全可行的:

#!/bin/bash
cat $@ | some_command_goes_here

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

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

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

编辑:将$*更改为$$,以处理文件名中的空格,这要归功于一条有用的注释。

有点俗气,但怎么样

cat file1 file2 | script.sh

我(似乎和其他人一样)对这里的目标有点困惑,所以我会给出三个可能的答案,可能涵盖你真正想要的。首先,相对简单的目标是让脚本从文件列表(在命令行上提供)或其常规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

注意:$@的双引号使用是避免文件名中出现有趣字符(如空格)问题的最佳方法——$*和无引号的$@都会把问题搞砸。<;()我在这里使用的技巧是一个仅限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

这使用<;()通过cat将脚本的stdin清洗到一个命名管道,然后将该管道作为参数传递给命令。同时,命令s的stdin取自here文档。

现在,我认为这是您想要做的,但这并不是您所要求的,即从提供的文件重定向脚本的stdin,将stdin传递到脚本内的命令。这可以通过结合上述技术来实现:

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

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

…尽管我想不出你为什么真的想这么做。

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

您想获取第一个参数并对其执行某些操作,然后从指定的任何文件中读取,如果没有文件,则从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。获取后,将参数移一个,然后使用剩余的参数作为文件列表,或者使用bash特殊文件句柄“/dev/stdin”(如果没有其他参数)。

我认为“如果没有指定文件,请使用/dev/stdin,否则请使用命令行上的文件”这一部分可能是您想要的,但其余代码至少对上下文有用。

也有点俗气,但这个怎么样:

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

如果您需要逐行读取和处理(这很可能),并且不想复制/粘贴同一代码两次(这很有可能),请在脚本中定义一个函数,然后将行一行接一行地传递给该函数,并在所述函数中进行处理。

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

x=`cat $*`
echo $x




相关问题