Bash 条件管道
Posted
技术标签:
【中文标题】Bash 条件管道【英文标题】:Bash conditional piping 【发布时间】:2012-05-11 20:58:12 【问题描述】:如何通过管道输出命令以防万一它返回 true?
function open
TEMPFILE=$(mktemp -u)
if ! gpg2 --quiet --decrypt --batch --passphrase "$2" "$1" 2> $TEMPFILE; then
error $"Password errata od errore di lettura dal file\n\nDettagli:\n$(grep -v '^$' $TEMPFILE)"
rm -f $TEMPFILE
return 1
fi
rm -f $TEMPFILE
if ! open "$@" "$PASSWORD"; then
exit 1
fi | <SOMECOMMAND>
这样,它只是通过管道而不检查 open 是返回 true 还是 false,因此永远不会执行“exit 1”。
如何在不使用文件的情况下解决它(出于安全原因)。
【问题讨论】:
完整来源:fpasswd-0.2-alpha on SourceForge 【参考方案1】:如果文件成功打开,以下代码应有条件地通过管道传输结果:
result=open "$@" "$PASSWORD"
if [ $? -gt 0 ]; then
exit 1
fi
echo "$result" | <SOMECOMMAND>
【讨论】:
open 的输出包含换行符和其他不可打印的字符,它不能这样工作。 换行不是问题...你的命令是什么? 您对新行是正确的,但它还包含其他未保留的不可打印字符(如 \0)。 SOMECOMMAND 是一个打印密码的函数(如果是图形则通过 zenity,如果不是则通过 stdout)。出于安全原因,我无法使用文件。 @Uno 你总是要引用子进程的输出。试试result="$(open $@ $PASSWORD)"
(我讨厌蜱虫)
@KurzedMetal: open
需要打印返回码,即 printf "1\n"f
用于由 cmd-substitution 捕获的值(即 result=$(...)
)。 return
只设置调用者的$?
的值。只要分配 result=...whatever
不会导致语法错误,$?
的值现在可能会反映对 result
的成功分配,并且“返回”的值会丢失。没有时间测试它。祝大家好运。【参考方案2】:
使用命名管道并存储返回值
mkfifo piper;
open "$@" "$PASSWORD"; retval=$? > piper &
if [ x"$retval" != x0 ]
then
rm piper
exit 1
fi
<SOMECOMMAND> < piper
rm piper
【讨论】:
行不通。首先,retval 被设置在一个子shell 中(因为&
)。其次,open ...
命令可能会阻塞等待从管道中读取数据,但直到 open ...
完成后才能从管道中读取任何内容。
我刚刚对此进行了测试(尽管显然没有使用 open )并且它按预期工作......无论是发送到管道的命令失败还是成功,都在应该输入 then 语句时。并且打开命令正在写入管道,而不是读取
对我不起作用。我尝试了false; retval=$? >/dev/null &
,等待它完成,然后echo $retval
得到一个空行。至于 open 命令阻塞,如果你尝试向管道写入过多(一般为 64kB,请参阅here),写入过程将阻塞等待读取数据并在管道中腾出空间。
刚刚意识到我已经过度简化了我的测试,以至于删除了 &.. 哎呀,我的错误
如我所说,我既不想使用文件,也不想使用命名管道,内容充满了明文密码。【参考方案3】:
在我提出解决方案之前,让我解释一下这比您意识到的更困难。基本问题是时间问题:open ...
函数在运行时会产生输出; 在它完成运行后(因此在它产生它的输出之后)会产生一个退出状态。由于您想根据退出状态对输出执行不同的操作,因此您必须将输出临时存储在某个地方,直到函数完成,您可以决定如何处理输出。
管道本来就不能解决这个问题,因为管道不存储数据(除了一点缓冲空间)——它们将数据“实时”从一个程序传递到另一个程序,在这种情况下,第二个程序可以'直到第一个完成后才开始。通常,临时文件将是完美的(存储数据是文件的用途),但出于安全原因,您不希望这样做。这几乎就是将数据放在 RAM 中的某个地方(尽管这也不是完全安全的......)。
@Karoly Horvath 的回答建议将输出存储在 bash 变量中(存储在 RAM 中),但这不起作用,因为 bash 无法处理变量值中的空字节。因此,我提出了一种变体,您可以在其中使用数据的“安全”编码,并将其放入 bash 变量中。我用的是uuencode格式,不过你也可以用base64、hex dump等等……
if result=$(open "$@" "$PASSWORD" | uuencode -; exit $PIPESTATUS[0]); then
echo "$result" | uudecode -p | SOMECOMMAND
fi
请注意,PIPESTATUS 是一个 bashism,因此您应该以 #!/bin/bash
开始脚本。此外,如果输出太长,您可能会遇到 bash 想要存储/扩展/等多少数据的限制;如果这被证明是一个问题,事情就会变得更加复杂。
顺便说一句,如果您担心安全性,请不要使用 gpg2 的 --passphrase
选项 - 在命令行上传递密码短语会将其暴露给例如在正确的时间运行ps
的任何人,这是一个非常糟糕的主意。 gpg2 提供了很多选项来提供密码,所以请使用更好的选项。
【讨论】:
谢谢,稍后我尝试使用 uuencode 并让您知道。也感谢您的解释;现在我将使用“echo $PASS | gpg2 -d --batch --passphrase-fd 0 $FILE”,它是安全的,对吧? 但是,当我在寻找替代方案时,我使用了两次“打开”功能,一次是检查 (>/dev/null),另一次是使用数据...但这是一种资源和时间的浪费。 (但是,密码在 ps 中只显示了几秒钟,但您绝对正确,我不应该使用 --passphrase) 是的,使用echo "$PASSWORD" | gpg2 ... --passphrase-fd 0
(注意$PASSWORD
周围的双引号)应该是安全的,因为echo
不是作为常规命令运行,而是作为子shell 中的内置命令运行,所以ps
等看不到它的参数。
在这种情况下,这似乎是最好的方法。
我希望我的软件具有更少的依赖性,所以我使用了 base64... 效果很好!!真的谢谢!还有我不知道的 $PIPESTATUS!以上是关于Bash 条件管道的主要内容,如果未能解决你的问题,请参考以下文章
Azure DevOps Pipelines 中的条件阶段执行