Bash:检查管道中的输入是否为空

8

假设我有一个bash命令mycommand。有时它会输出一些内容,但总是返回退出码0。我需要使用管道来将退出码修复为对于空输出为0,对于非空输出为其他值。

mycommand | ???

我不关心if语句或自定义命令。我只想有一个像grep一样简单的命令,带一些参数来检查其输入是否为空,并返回退出代码。最好能够在输入不为空时打印输出,但这不是必需的。

1
为什么你需要使用管道? - l0b0
不是“需要”,只是“想要”。无论如何,这是我从评论中得到的最佳答案:!(mycommand | grep ^) - enumag
5个回答

7

或许:

yourcommand | grep -qv .

! ( yourcommand |  grep -q ^ )

或相反意思:如果没有返回任何内容,则返回false
yourcommand |  grep -q ^ || echo no output.

没有否定,你必须反转条件的含义。 - F. Hauri - Give Up GitHub
1
如果空行也算作输出(例如来自简单的 echo),则应该测试 ^ 而不是 .。例如:echo | grep -q ^ || echo noecho | grep -q . || echo no - clt60
然后再使用 grep -v ^,尽管这样不会显示任何内容。你对避免使用 ! 的执念似乎有些过度了。 - tripleee
@tripleee 已经尝试过了。不知道为什么,似乎无法与我的命令配合使用(在空结果上退出代码为1)。不确定原因。!可以正常工作。 - enumag
尝试使用 bash -c '! yourcommand | grep ^' 命令行吧? - tripleee
显示剩余8条评论

6

wc 支持换行符和 \0

$ printf '\n' | wc -c
1
$ printf '\0' | wc -c
1

因此,以下是一个简单的符合POSIX标准的方法:

mycommand | [ $(wc -c) -eq 0 ]

示例:

$ printf '' | [ $(wc -c) -eq 0 ]; echo $?
0
$ printf 'yay' | [ $(wc -c) -eq 0 ]; echo $?
1
$ printf '\n' | [ $(wc -c) -eq 0 ]; echo $?
1
$ printf '\0' | [ $(wc -c) -eq 0 ]; echo $?
1

不起作用的替代方案:

  • grep . doesn't match a solitary newline or \0 as a character:

    $ printf '\n' | grep .; echo $?
    1
    $ printf '\0' | grep .; echo $?
    1
    
  • You can't save \0 in a variable:

    $ a=$'foo\0bar\0baz'
    $ printf '%s' "$a" | wc -c
    3
    
  • Command substitutions remove trailing newlines:

    $ test -z "$(printf '\n')"; echo $?
    0
    

1
echo 命令会返回一个空行!请尝试使用 echo -ntrue 命令! - F. Hauri - Give Up GitHub
@F.Hauri 问题已解决。当前的解决方案适用于换行符和\0 - l0b0
使用 ^grep 将会工作... @l0b0 - clt60

1

看起来最清晰的方法是

mycommand | ( ! grep ^ )

否定也可以在开头:! mycommand | grep ^ - Sergii Shymko

1

由于答案众多,因此使用^来使用grep

pp() { printf "================\nfor: %s\n" "$@"; }

pp "no input"
printf '' | grep -q ^ ; echo $?
printf '' | grep -q ^ && echo got input || echo no input

pp 'null character'
printf '\0' | grep -q ^ ; echo $?
printf '\0' | grep -q ^ && echo got input || echo no input

pp '\n'
printf '\n' | grep -q ^ ; echo $?
printf '\n' | grep -q ^ && echo got input || echo no input

pp 'some'
printf 'some' | grep -q ^ ; echo $?
printf 'some' | grep -q ^ && echo got input || echo no input

pp 'some\n'
printf 'some\n' | grep -q ^ ; echo $?
printf 'some' | grep -q ^ && echo got input || echo no input

输出

================
for: no input
1
no input
================
for: null character
0
got input
================
for: \n
0
got input
================
for: some
0
got input
================
for: some\n
0
got input

pp() { local sline;eval "printf -v sline '%.0s-' {1..$COLUMNS}";printf "%s\nFor: <%s>:\n" $sline "$1";printf "$1" | grep ^ ; echo "Returned code: $?"; } - F. Hauri - Give Up GitHub
您可以运行pp而不带任何参数,例如。然后printf... ${1:-no output}...但您已经知道这些了。 - F. Hauri - Give Up GitHub
read -v COLUMNS < <(tput cols),然后对于固定的输入,eval 不是“邪恶”的!使用这种方式使用 eval 没有问题。 - F. Hauri - Give Up GitHub

0
一个常见的,虽然有点不太优雅的解决方法是使用临时文件。
t=$(mktemp -t myscript.XXXXXXXX) || exit
# Use a trap to clean up even if interrupted
trap 'rm -f "$t"' EXIT
trap 'exit' HUP TERM
cat >"$t"
if [ -s "$t" ]; then
    : do stuff with "$t"
fi

这显然太复杂了,无法在命令行上随意使用,但有时你只需要编写一个脚本。

这个方法可以很好地推广到任何需要对所有输入执行多个命令的情况。

我还要顺便提一下xargs -r,当然,这只有在你已经将输出管道传递给xargs时才有意义。


谢谢,但我想要一个优雅的一行代码放入我的.travis.yml文件中。 - enumag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接