当此处字符串有错误时如何使脚本失败?

当此处字符串有错误时如何使脚本失败?

我有一个与此类似的脚本:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

while read -r l; do
    echo "${l}"
done <<< "$(cat input.txt)"

echo Success

该命令cat input.txt只是这里的一个示例,以简化我的问题。

我预计如果input.txt不存在,脚本将立即退出,这要归功于set -euo pipefail.但事实并非如此,脚本成功结束并显示以下输出:

cat: input.txt: No such file or directory

Success

有没有办法让脚本按照我的预期失败?

答案1

我知道这里cat input.txt是一些任意命令的占位符。具体来说cat input.txt,解决方案很简单:只需使用输入重定向input.txt即可。

退出代码将在此处字符串的下一个扩展中可用。例如:

cat <<< "$(exit 3)$?"

输出 3. 所以你可以这样做:

#! /bin/bash -
set -o nounset -o pipefail -o errexit

unset -v ret
{
  [ "$ret" -eq 0 ] || exit "$ret"
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
} <<< "$(cat input.txt)"${ret[1+(ret=$?)]}"
echo success

这似乎至少适用于 bash 5.0。

或者您可以使用临时变量:

#! /bin/bash -
set -o nounset -o pipefail -o errexit

output_minus_trailing_newlines=$(cat input.txt)
while IFS= read -r line || [ -n "$line" ]; do
  printf '%s\n' "$line" || exit
done <<< "$output_minus_trailing_newlines"

echo success

在任何一种情况下,如果命令失败,则不会处理其任何输出。

lastpipe如果您不希望循环在子 shell 中运行,您还可以使用管道和选项。

#! /bin/bash -
set -o nounset -o pipefail -o errexit
shopt -s lastpipe

cat input.txt |
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done

echo success

然后输出将被处理。只有在管道退出后,如果由于errexit( -e) 和的组合导致命令失败,shell 才会退出pipefail。在这种情况下,尾随空行将被保留。

相同使用过程替换:

#! /bin/bash -
set -o nounset -o pipefail -o errexit

{
  pid="$!"
  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
  wait "$pid"
} < <(cmd input.txt)

echo success

或者您可以使用临时文件(此处的文档和此处的字符串过去是在 bash 中使用临时文件实现的)。

#! /bin/bash -
set -o nounset -o pipefail -o errexit
tmp=$(mktemp)
{
  rm -f -- "$tmp"
  cat input.txt >&3
  exec 3>&-

  while IFS= read -r line || [ -n "$line" ]; do
    printf '%s\n' "$line" || exit
  done
  wait "$pid"
} 3> "$tmp" < "$tmp"

echo success

这样,如果命令失败并且您没有将输出存储在内存中(多次),则不会处理输出,并且保留尾随空行。

无论如何,请参阅为什么使用 shell 循环处理文本被认为是不好的做法?

答案2

像这样:

#!/bin/bash
set -euo pipefail
IFS=$'\n\t'

[[ -s input.txt ]]
while read -r l; do
    echo "${l}"
done <<< "$(cat input.txt)"

echo Success

相关内容