【问题标题】:Perl's diamond operator: can it be done in bash?Perl 的菱形运算符:可以在 bash 中完成吗?
【发布时间】:2010-10-18 14:53:11
【问题描述】:

有没有一种惯用的方法可以在 bash 中模拟 Perl 的菱形运算符?使用菱形算子,

script.sh | ...

读取标准输入的输入并

script.sh file1 file2 | ...

读取 file1 和 file2 的输入。

另一个限制是我想将 script.sh 中的标准输入用于我自己脚本的输入之外的其他内容。下面的代码对上面的 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

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

【问题讨论】:

  • 使用 bash 而不是仅仅使用 Perl 有什么令人信服的理由吗?

标签: perl bash unix shell diamond-operator


【解决方案1】:

我(似乎和其他所有人一样)对这里的目标到底是什么有点困惑,所以我将给出三个可能的答案,这些答案可能涵盖了你真正想要的。首先,相对简单的目标是让脚本从文件列表(在命令行中提供)或常规标准输入中读取:

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

注意:$@ 的双引号使用是避免文件名中有趣字符(例如空格)问题的最佳方法 - $* 和不带引号的 $@ 都会把这搞砸。我在这里使用的

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

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

这使用

现在,我认为这就是您想要做的,但这并不是您所要求的,即 both 从提供的文件 重定向脚本的标准输入em> 将标准输入传递给脚本内的命令。这可以通过结合上述技术来完成:

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

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

...虽然我想不出你为什么要这么做。

【讨论】:

  • 感谢您的回复。我认为#2是我想要的。原因是该命令将始终为第一个文件名参数提供相同的数据,但其他文件名参数将是用户数据。我不想分发两个文件(脚本和第一个文件名参数的数据)。
  • 而且我还希望他们能够根据需要将其用作标准的 shell 管道组件,从而导致 stdin 的困境。
【解决方案2】:

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

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

【讨论】:

  • 这与其他建议存在同样的问题——“whatever_you_want”已经有东西通过 here-doc 进入其 STDIN。我想得越多,我就越需要命名管道或其他方式来提供额外的文件描述符。
  • 那么,如果不将文件/标准输入发送到程序,您实际上想对文件/标准输入做什么?用你想做的任何事情替换“for file in”循环的内容 - 用 var=$(head -1 $file) 读取第一行,将它们与 cat $file >> /tmp/file; 结合起来等等
【解决方案3】:

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

【讨论】:

  • 我相信 OP 已经有执行这部分的代码。如果没有给出命令行参数,Perl 的菱形运算符也将逐行读取标准输入,这(我认为)是 OP 所要求的。
  • "$*" 是错误的,即使有多个参数也是一个字符串。你的意思是“$@”。
  • 你是对的。我永远记不起 $* 和 $@ 之间的区别。
  • 请注意,管道读取在 pdksh 中无法正常工作,无论关于 bash 的问题有什么价值...... :)
【解决方案4】:

当然,这是完全可行的:

#!/bin/bash
cat $@ | some_command_goes_here

然后用户可以调用不带参数(或“-”)的脚本从标准输入或多个文件中读取,所有这些都将被读取。

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

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

编辑:将 $* 更改为 $@ 以处理文件名中的空格,感谢有用的评论。

【讨论】:

  • 出色地使用 cat 连接多个文件或标准输入。我总是忘记我们精彩的 cat-as-a-basic-text-editor 技巧
  • @Chris Lutz:是的,cat 非常强大,而且它还有一个有趣的表亲 (tac),它也会以相反的顺序读取行。 :)
  • 这不能正确处理名称中带有空格的文件。
  • 使用“$@”,它将所有参数引用为单独的字符串。
  • for line in $(cat $@) 有一些限制。首先,文件的组合长度必须小于最大命令行长度。其次,您应该引用 $@(如 "$@"),以便包含 $IFS 的任何元素(例如空格)的文件受到尊重。
【解决方案5】:

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

x=`cat $*`
echo $x

【讨论】:

    【解决方案6】:

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

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

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

    【讨论】:

      【解决方案7】:

      有点俗气,但是怎么样

      cat file1 file2 | script.sh
      

      【讨论】:

      • 这玩意儿怎么样?这正是 cat 的用途。
      • 感谢您的回答。问题是我不希望 script.sh 的用户需要这样做。 :)
      • 另外,我认为它在我的情况下不起作用,因为我需要标准输入用于 here-doc 中的这个常量代码。
      猜你喜欢
      • 1970-01-01
      • 2020-07-16
      • 1970-01-01
      • 2012-01-24
      • 2015-07-14
      • 2011-05-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多