4

有没有一种惯用的方法可以在 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 中的数据。我希望用户数据进入我的脚本的标准输入,但它不能是我脚本中命令调用的标准输入。

4

7 回答 7

6

当然,这是完全可行的:

#!/bin/bash
cat $@ | some_command_goes_here

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

如果您想处理这些文件的内容(例如,逐行),您可以执行以下操作:

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

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

于 2009-04-09T19:14:07.993 回答
4

有点俗气,但是怎么样

cat file1 file2 | script.sh
于 2009-04-09T17:25:06.680 回答
2

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

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 将脚本的标准输入替换为 cat 的输出。

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

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

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

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

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

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

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

于 2009-04-11T19:32:17.090 回答
1

您想获取第一个参数并对其进行处理,然后从指定的任何文件中读取,如果没有文件则从 stdin 中读取?

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

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 - 否则使用命令行上的文件”部分可能是您正在寻找的,但其余代码至少对上下文有用。

于 2009-04-10T15:07:53.413 回答
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
于 2009-04-09T18:58:09.373 回答
0

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

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

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

于 2009-04-09T19:13:16.873 回答
0

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

x=`cat $*`
echo $x
于 2009-04-09T19:17:37.657 回答