【问题标题】:bash spaces in whiptail/dialog menu items鞭尾/对话框菜单项中的 bash 空格
【发布时间】:2013-05-29 15:51:03
【问题描述】:

我想用 bash-dialog 创建一个简单的对话框。我使用 (X)DSL 和 bash-3.2。最新的 (X)DSL 基于 Linux 2.4.31 并附带 bash-2.05,但是 bash-3.2 可从 MyDSL/Testing 下载。所以,我的脚本在 '#!/bin/bash-3.2/bin/bash' 下运行。

用户可以选择的菜单项来自数据库。

示例数据库文件“骨架”:

室内照明|室内照明

户外照明|户外照明

我从“骨架”文件中将数据检索到数组“选项”中:

options=($(awk -F"|" '{ print $1,$2 }' 骨架)

在终端“回显”数组:

回显 ${options[@]}

其中显示:

“室内电枢”“室内照明”“户外电枢”“户外照明”

这看起来可以用作带有“whiptail”的选择菜单,但事实并非如此。命令行:

whiptail --clear --title "骨架" --menu "选择骨架" 50 80 10 ${options[@]}

显示:

column1-column2

室内电枢

照明用

室内使用

户外电枢

照明用

户外使用

代替:

column1-column2

室内电枢-室内照明

户外电枢-户外照明

似乎带有双引号的数组元素被“whiptail”忽略或看不到。我也试过“${options[@]}”,但总是在第一个词“室内”上得到结果。

除了 'whiptail' 我尝试了 'dialog' 但它们是相同的:版本信息在两种情况下都显示 'cdialog (ComeOn Dialog!) version 1.1-20080316'。

我的资源非常有限,我不想冒险(还)进入“xdialog”、“zenity”、“dzen”之类的东西,即使这样可以解决这个问题。由于 XDSL(用于 XBOX),我也仅限于 Linux 2.4.31。

我一直在浏览互联网,但无济于事。 'whiptail/dialog' 的解决方案是什么?

【问题讨论】:

    标签: bash dialog dsl xbox whiptail


    【解决方案1】:

    您遇到的基本问题来自 shell 解析命令行的顺序:它在用变量值替换变量之前解析(并删除)引号和转义,并且它永远不会返回并重新解析任何引号或在替换值内转义。考虑这个例子:

    var='"foo bar"'   # Note that the single-quotes will be removed, and the
                      # double-quotes will be treated as part of the variable value.
    somecmd $var   # This runs cmd with 2 arguments: '"foo' and 'bar"'
    

    在您的情况下,我不确定双引号的来源;它们不在您提供的文件列表中,并且 awk 命令不会添加它们。但无论如何,您不希望它们存储为值的一部分,而是希望它们围绕变量引用:

    var='foo bar'   # Again, the single-quotes are removed.
    somecmd "$var"   # This runs cmd with a single argument: 'foo bar'
    

    您的情况有点复杂,因为您使用的是数组,但同样的原则也适用。请注意,echoing 变量具有高度误导性;如果它显示看起来像正确的引用,那实际上意味着有一些可怕的错误,因为它应该在 引用删除之后显示参数。

    那么,你是怎么解决的呢?试试这个:

    options=()
    while IFS="|" read col1 col2 || [ -n "$col1" ]; do
        options+=("$col1" "$col2")  # Note the double-quotes around the variable references
    done <armatures
    echo "options:"
    printf "  '$s'\n" "${options[@]}"  # This prints each array element on a separate line
    whiptail --clear --title "Armatures" --menu "Choose an armature" 50 80 10 "${options[@]}"  # Again, double-quotes around the reference
    

    更新:我添加了一个测试 ([ -n "$col1" ]) 来为数据库文件中未终止的最后一行执行循环。

    如果双引号实际上在数据库文件中,则必须删除它们;处理此问题的最简单方法可能是在将字符串添加到数组时去除引号,使用 bash 在构建数组时进行字符串替换(将 '"' 替换为空白)的能力:

    options=()
    while IFS="|" read col1 col2 || [ -n "$col1" ]; do
        options+=("${col1//'"'/}" "${col2//'"'/}")
    done <armatures
    

    【讨论】:

    • 谢谢! Shell 解析不容易掌握,但您的解释很有帮助。但是,您提出的代码存在一个问题:最后一条记录未显示在“whiptail”中,即在上面的示例中仅显示第一条记录。通过仅使用“|”添加额外的记录,两条记录都显示在“whiptail”中。我怎样才能避免添加额外的“|”记录?
    • @linuph:数据库中的最后一行可能没有终止(即末尾没有换行符);我添加了一个测试来为任何非空行运行循环,即使它没有终止。顺便说一句,这表明数据库文件不是标准的 unix 文本文件格式,而是可能是 DOS 格式,在这种情况下,它会在每行的末尾有一个非打印回车符(它会缠绕在每个 col2 条目的末尾)。如果是这样,有办法过滤它...
    • 我的数据库中没有双引号,所以没关系。我会检查最后一行的终止。再次感谢!
    • 确实,为了避免丢失最后一条记录,这条最后一条记录必须用下一行代码终止,即'\n'。就是这样。
    【解决方案2】:

    主要问题在于 bash(或任何其他 shell)将命令行拆分为“单词”的方式,如第一个答案中所述。行拆分是在 IFS(内部字段分隔符)的基础上完成的,默认情况下设置为 。开发早期 shell 的聪明人(Stephen Bourne 等)显然认为用户能够更改它是一个好主意。可以设置为多字符值也很好。

    所以您真正需要做的就是将字段分隔符设置为换行符,运行 awk 脚本,然后将其重新设置。使用时还应该引用数组。

    这应该适用于 bash:

    IFS=$'\n'
    options=($(awk -F"|" '{ print $1,$2 }' armatures)
    IFS=$' \t\n'
    

    数组成员现在已正确定义。那么:

    whiptail --clear --title "Armatures" --menu "Choose an armature" 50 80 10 "${options[@]}"
    

    对于更原始的 shell,如 Bourne Shell,您可能需要通过输入“IFS=”(不带引号)、打开单引号、按 ,然后用单引号关闭来设置 IFS。要再次重置它,输入 "IFS="(不带引号),打开单引号,输入 ,引用(例如 Ctrl-V)一个 ,点击 ,然后用单引号关闭。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多