当状态代码无用时,是否有办法根据 stdout 的输出构建管道?
我希望答案不是解决用例,而是解决 shell 脚本范围内的问题。我想做的是通过根据国家和语言代码猜测名称来找到存储库中可用的最具体的包。
就拿这个来说吧,
$PACKAGE1=hunspell-en-zz
$PACKAGE2=hunspell-en
第一个猜测更合适,但可能不存在。在这种情况下,我想返回hunspell-en
( $PACKAGE2
) 因为第一个选项hunspell-en-zz
( $PACKAGE1
) 确实不是存在。
apt-cache 的管道
只要命令能够运行(来自 的文档) ,该命令apt-cache
就会返回成功(由 shell 定义为退出代码零)apt-cache
apt-cache 在正常操作时返回零,在错误时返回十进制 100。
这使得在管道中使用该命令变得更加困难。通常,我预计与 404 等效的包搜索会导致错误(就像curl
or一样wget
)。我想搜索一个包是否存在,如果不存在回退到另一个包(如果存在)。
这不会返回任何内容,因为第一个命令返回成功(因此永远不会运行中的 rhs ||
)
apt-cache search hunspell-en-zz || apt-cache search hunspell-en
apt-cache search
有两个参数
这不会返回任何内容,因为apt-cache
它的参数与,
apt-cache search hunspell-en-zz hunspell-en
从文档apt-cache
单独的参数可用于指定通过 and 组合在一起的多个搜索模式。
因此,由于这些参数之一显然不存在,因此不会返回任何内容。
问题
apt-cache
用于处理返回码对任务无用的约定的 shell 惯用法是什么?成功仅取决于 STDOUT 上是否存在输出?
如同
-
他们都源于同一个问题。遗憾的是,此处选择的答案提到了
find -z
哪个解决方案不适用,并且是特定于用例的。没有提到习惯用法或在不使用空终止的情况下构造管道(不是 上的选项apt-cache
)
答案1
创建一个函数,该函数接受命令并在有输出时返回 true。
r() { local x=$("$@"); [ -n "$x" ] && echo "$x"; }
( ( r echo -n ) || echo 'nada' ) | cat # Prints 'nada'
( ( r echo -n foo ) || echo 'nada' ) | cat # Prints 'foo'
所以对于这个用例它会像这样工作,
r apt-cache search hunspell-en-zz || r apt-cache search hunspell-en
答案2
据我所知,没有标准方法来处理命令的成功取决于输出的存在的情况。不过,您可以编写解决方法。
例如,您可以将命令的输出保存在变量中,然后检查该变量是否为空:
output="$(command)"
if [[ -n "${output}" ]]; then
# Code to execute if command succeded
else
# Code to execute if command failed
fi
我认为这从总体上回答了问题,但如果我们谈论apt-cache search
一些解决方案,我就会想到。
我有一个脚本可以使包管理更容易。它的一些功能如下:
search() {
local 'package' 'packages'
packages="$( apt-cache search '.*' | cut -d ' ' -f '1' | sort )"
for package; do
grep -F -i -e "${package}" <<< "${packages}"
done
}
search_all() {
local 'package'
for package; do
apt-cache search "${package}" | sort
done
}
search_description() {
local 'package' 'packages'
packages="$( apt-cache search '.*' | sort )"
for package; do
grep -F -i -e "${package}" <<< "${packages}"
done
}
search_names_only() {
local 'package'
for package; do
apt-cache search --names-only "${package}" | sort
done
}
这些使您可以在单个命令中执行多个搜索。例如:
$ search hunspell-en-zz hunspell-en
hunspell-en-au
hunspell-en-ca
hunspell-en-gb
hunspell-en-med
hunspell-en-us
hunspell-en-za
每个函数都以不同的方式搜索数据库,因此结果可能会根据您使用的函数而有所不同:
$ search gnome | wc -l
538
$ search_all gnome | wc -l
1322
$ search_description gnome | wc -l
822
$ search_names_only gnome | wc -l
550
答案3
我不会称其为优雅,但我认为它可以完成这项工作:
search_packages () {
local packages=($@)
local results=()
for package in "${packages[@]}"; do
results=($(apt-cache -n search "$package"))
if [[ "${#results[@]}" -eq 0 ]]; then
echo "$package not found."
elif [[ "${#results[@]}" -eq 1 ]]; then
do stuff with "$package"
else
echo "Warning! Found multiple packages for ${package}:"
printf '\t-> %s\n' "${results[@]}"
fi
done
}
不幸的是,我没有可以测试的 Debian 机器。我添加了-n
“仅名称”选项来apt-cache
尝试限制搜索结果,因为看起来您几乎确定要搜索的内容。
可以这样运行:
$ search_packages hunspell-en-zz hunspell-en
$ my_packages=('hunspell-en-zz' 'hunspell-en')
$ search_packages "${my_packages[@]}"
答案4
你可以定义一个:
has_output() {
LC_ALL=C awk '1;END{exit!NR}'
}
进而:
if cmd | has_output; then
echo cmd did produce some output
fi
某些awk
实现可能会因输入中的 NUL 字符而阻塞。
与 相反grep '^'
,上面的内容将保证适用于不以换行符结尾的输入,但会添加缺少的换行符。
为了避免这种情况并移植到awk
NUL 阻塞的系统,您可以使用perl
:
has_output() {
perl -pe '}{exit!$.'
}
使用perl
,您还可以定义一个更优雅地处理任意文件的变体:
has_output() {
PERLIO=:unix perl -pe 'BEGIN{$/=\65536} END{exit!$.}'
}
这限制了内存使用(例如对于没有换行符的文件,如大型稀疏文件)。
您还可以创建变体,例如:
has_at_least_one_non_empty_line() {
LC_ALL=C awk '$0 != "" {n++};1; END{exit!n}'
}
或者:
has_at_least_one_non_blank_line() {
awk 'NF {n++};1; END{exit!n}'
}
(注意定义空白的不同的实现有所不同awk
,有些仅限于空格和制表符,有些还包括 ASCII 垂直间距字符,如 CR 或 FF,有些考虑区域设置的空格)
理想情况下,在 Linux 上,您希望使用splice()
系统调用来最大限度地提高性能。我不知道会公开它的命令,但您始终可以使用python
's ctypes
:
has_output() {
python -c 'if 1:
from ctypes import *
import sys
l = CDLL("libc.so.6")
ret = 1
while l.splice(0,0,1,0,65536,0) > 0:
ret = 0
sys.exit(ret)'
}
(请注意,has_output
的 stdin 或 stdout (或两者)必须是管道才能splice()
工作)。