【问题标题】:Why doesn't `cat foo.txt | while read` fail if file not found?为什么不`cat foo.txt |如果找不到文件,则读取失败?
【发布时间】:2015-04-03 05:33:57
【问题描述】:

我使用这个循环来遍历文件,或者,如果没有文件,读取标准输入:

#!/bin/bash
set -e
cat "$@" | while read arr ; do
    echo "Got this line ${arr}"
done

问题是如果文件不存在,它不会出错。

您可以在此处查看完整示例: https://github.com/StackExchange/blackbox/blob/master/tools/mk_rpm_fpmdir

cat 如果未找到任何文件,则返回错误代码 1。但是该错误不会导致程序停止。

如果找不到文件,我如何遍历 $@ 并失败?

【问题讨论】:

  • 三引号对 shell 没有任何意义,所以"""$@""" 只是"$@",每边都有额外的空字符串。这也不是以任何方式“迭代”。 cat 只是将所有文件中的内容转储到连续流中,直到完成。这没有失败的原因是因为set -e 仅适用于“简单命令”。见stackoverflow.com/q/25794905/258523
  • 除非设置了shopt -s pipefail,否则管道的右侧(而不是左侧)决定了整个管道的退出状态。
  • bash 成语 cat """$@""" 的意思是“所有的 args,但如果它们包含空格,它仍然可以使用”。
  • @TomOnTime, cat "$@" 意思相同。多余的引号没有效果。
  • 顺便说一句,read -a arr 然后取消引用 "$arr" 只会给你数组的第一个元素,而不是它的完整内容。您需要使用"${arr[@]}" 来获取完整的内容。

标签: bash


【解决方案1】:
for f in "${@}"; do
    test -f "${f}" || exit # fail if file doesn't exist
    cat "${f}" | awk '{print "Got this line" $0}'
done

【讨论】:

  • 使用set -e 只需test -f "$f" 就足以解决这个问题。为了安全起见,您需要引用"$@"
  • 仍然需要test -f "$f",而不是test -f $f
  • 这里额外的花括号没有正确性值,顺便说一句。它们绝对不能替代引号。
  • 很公平。原则上没关系。我不会使用set -e。花括号通常是一种很好的做法。
  • 我个人不同意——我发现过度使用会损害可读性——但这是肯定的。我当然不能说它们以任何影响正确性的方式有害。
【解决方案2】:

有几个可用的选项。一种是简单地抛弃cat,因此没有管道(这具有避免使用子shell 的有利副作用,因此允许更改shell 状态——变量集等——持续到内部循环之外):

set -e
for f; do # in "$@" is implicit
  while read -r -a arr; do
    printf 'Got line: '
    printf '%q ' "${arr[@]}"
    printf '\n'
  done <"$f"
done

上面还竭尽全力以完全保留读取的数组的方式打印读取的内容,并明确地打印其内容(区分包含两个元素的数组foobar baz与包含三个元素的数组@ 987654325@barbaz)。


另一个是设置pipefail,如果管道的任何组件返回非零退出状态,包括cat,这将导致管道被认为失败:

set -e
set -o pipefail
cat "$@" | while IFS= read -r; do
  printf 'Got line: %q\n' "$REPLY"
done

这通过覆盖默认行为而起作用,在该行为中,仅管道的右侧很重要,以考虑命令的整体退出状态。

【讨论】:

    猜你喜欢
    • 2021-03-17
    • 1970-01-01
    • 2017-04-24
    • 1970-01-01
    • 2021-10-22
    • 1970-01-01
    • 2021-05-03
    • 2022-08-14
    • 2014-05-14
    相关资源
    最近更新 更多