我有一个与此类似的脚本:
#!/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