使bash函数在为空时失败



我想写一个函数,总是有一个非空输出或失败,但我缺少一个命令,读取标准输入并管道到标准输出,如果非空或失败,如:

example() {
do_something_interesting_here $1 | cat_or_fails
}

这个想法是,如果命令cat_or_fails的输入是空的,它就会失败(所以函数会失败),或者输入没有任何改变就输出(像cat)。

但是我找不到任何标准的实用程序能够做到这一点,或者可能是我不确定如何使用这些工具。

遵循评论中@William Pursell的想法,grep似乎可以使用以下内容:

example() {
do_something_interesting_here $1 | grep "[[:alnum:]]"
}

正如在注释中提到的,它将使用任何空行(或任何不匹配给定正则表达式的行)。

确保至少找到文本文件的一行的常用技巧如下。这都是标准的壳的东西…Bash, ksh, zsh.

example() {
set -o pipefail     #assure failure on do_something is not suppressed
do_something_interesting_here $1 | 
grep .           #assure no output returns error
}

如果您需要在函数返回时取消管道失败:

example() {
(   #isolate pipefail in subshell
set -o pipefail     #assure failure on do_something is not suppressed
do_something_interesting_here $1 | 
grep .           #assure no output returns error
)
}

实现此目的的简单算法很简单,很明显:尝试读取一个字节。如果它工作,写字节回来,然后运行cat。如果失败,则以非零状态退出。

下面两种变体都可以用问题(... | cat_or_fails)中描述的方式使用。


一个非常简单的实现(不尝试处理以NUL字符开头的二进制文件)看起来像:

cat_or_fails() {
local firstbyte
IFS= read -r -n 1 firstbyte || return
printf '%s' "${firstbyte:-$'n'}"
cat
}

尝试正确处理二进制文件的稍微不那么简单的实现可能看起来像:

cat_or_fails() {
local firstbyte
IFS= read -r -d '' -n 1 firstbyte || return
if [[ $firstbyte ]]; then
printf '%s' "$firstbyte"
else
printf ''
fi
cat
}

有多种方法可以做到这一点,但最简单的是使用awk:

function cat_or_fail () { awk '1;END{exit !NR}' "$@"; }

这里,如果没有读取任何一行,awk返回非零状态。

或者,moreutils包中提供了一个工具。命令ifne允许执行命令,具体取决于/dev/stdin是否为空:

  • ifne command:如果标准输入不为空,运行command
  • ifne -n command:如果标准输入为空,则执行command。如果标准输入不为空,则将标准输入发送到标准输出。

后者本质上是OP所期望的。函数cat_or_fail看起来像

function cat_or_fail () { ifne -n false; }

如果OP想要具有与cat相似的行为,您可以将其写为:

function cat_or_fail () { cat -- "${@}" | ifne -n false; }

后者可以接受文件作为参数,也可以从类似于cat的管道中读取输入。

如果您想将其扩展到command_or_fail,其中command使用标准输入执行,或者失败。你的工作方式要有一点不同。

ifne的返回状态不受标准输入内容(空或不空)的影响。所以你不能在and-or序列列表(foo && bar || baz)中使用它。

为了创建请求的行为,需要使用pipefail选项

$ set -o pipefail
$ function command_or_fail() { ifne -n false | ifne "$@"; }
$ echo foo | command_or_fail cat -n
1 foo
$ echo $?
0
$ </dev/null | command_or_fail cat -n
$ echo $?
1

相关内容

  • 没有找到相关文章

最新更新