问题

问题

当状态代码无用时,是否有办法根据 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 等效的包搜索会导致错误(就像curlor一样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 '^',上面的内容将保证适用于不以换行符结尾的输入,但会添加缺少的换行符。

为了避免这种情况并移植到awkNUL 阻塞的系统,您可以使用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()工作)。

相关内容