从需要写入终端的脚本填充变量

时间:2018-05-31 11:09:23

标签: bash sh

如何填充传递给bash脚本的变量,该变量是从另一个bash脚本调用的?我想知道选项和最佳实践。

让我详细说明为什么通过设置退出状态或echo来回答的许多问题是不够的:

  • exit n:n被限制为0< = n< = 255
  • echo foo不允许我回显该脚本中的任何相关信息,备用屏幕缓冲区也不会对此有所帮助。

我找到了一个可能的解决方案:

#outer.sh
source inner.sh
populate result
echo "Evaluated: $result"

#inner.sh
function populate {
    local __popvar=$1
    eval $__popvar="'RETURN VALUE'"
}

我不喜欢这个解决方案有三个原因:

  1. 需要source内部脚本,使用辅助函数污染全局范围。
  2. 需要eval,特别是因为混淆的多引号。
  3. 详细程度。我首先需要来源,然后调用一个函数,而我更喜欢bash inner.sh result
  4. 有关内部脚本的更多信息

    内部脚本应该写入备用屏幕缓冲区。在此缓冲区上,用户可以从阵列中选择一个选项(通过箭头键或ijkl样式选择,使用空格确认或输入)。应该以某种方式从脚本返回此选项。返回索引不是一个选项,因为数组中的元素数量可能超过256.代码:

    #!/usr/bin/bash
    
    prompt=$1; shift
    options=( "$@" )
    
    c_opts=${#options[@]}
    selected=0
    
    # switch to alternate screen and trap the kill signal to switch back
    tput smcup
    
    trap ctrl_c INT
    function ctrl_c {
      tput rmcup
      exit 1
    }
    
    
    function print_opts {
      for (( i = 0; i < $c_opts; i++ )); do
        if [[ i -eq $selected ]]; then
          echo -e "\t\e[7m ${options[i]} \e[0m"
        else
          echo -e "\t ${options[i]} "
        fi
      done
    }
    
    function reset_term {
      for (( i = 0; i < $c_opts; i++ )); do
        tput cuu1  # move cursor up 1 line
        tput el    # delete current line
      done
    
    }
    
    function reprint_opts {
      reset_term
      print_opts
    }
    
    echo $prompt
    print_opts
    
    while read -sN1 key; do
    
      read -sN1 -t 0.0001 k1
      read -sN1 -t 0.0001 k2
      read -sN1 -t 0.0001 k3
      key+="${k1}${k2}${k3}"
    
      # colemak layout
      case "$key" in
        n|u|$'\e[A'|$'\e0A'|$'\e[D'|$'\e0D')   # up or left
          ((selected > 0)) && ((selected--));;
    
        e|i|$'\e[B'|$'\e0B'|$'\e[C'|$'\e0C')   # down or right
          ((selected < $c_opts-1)) && ((selected++));;
    
        $'\e[1~'|$'\e0H'|$'\e[H')  # home key
          selected=0;;
    
        $'\e[4~'|$'\e0F'|$'\e[F')  # end key
          ((selected = $c_opts-1));;
    
        ' '|$'\x0a')  # enter or space
          tput rmcup && echo ${options[$selected]} && exit 0;;
    
        q|$'\e')  # q or escape
          tput rmcup && exit 0;;
    
      esac
    
      reprint_opts
    done
    

    基于@ JohnKugelman的评论,该脚本应按以下方式调用:

    prompt="Your options are:"
    
    options=(
      "Option A"
      "Option B"
      "Option C"
      "Option D"
    )
    
    
    result=$( exec 3>&1; bash select-menu.sh "$prompt" "${options[@]}" 2>&1 1>&3; exec 3>&- )
    echo $result
    

    这似乎是一个吸引人的解决方案,但它无法解决问题。不打印要在备用屏幕缓冲区上打印的“选择”菜单。但输入正常,选择存储在result

    要了解所需的行为,您可以替换调用脚本中的最后两行,如下所示:

    bash select-menu.sh "$prompt" "${options[@]}"
    

1 个答案:

答案 0 :(得分:3)

不要排除result=$(inner.sh)。如果要在脚本中显示交互式提示或对话框,请在stderr上执行这些操作,并让它只写入stdout的答案。然后你可以吃蛋糕并吃掉它:交互式提示将结果保存到变量中。

例如,如果您使用dialog告诉它将其答案写入stdout,则--output-fd 1会执行此操作。它使用curses将对话框绘制到备用屏幕,但在stderr上完成所有操作。

$ value=$(dialog --keep-tite --output-fd 1 --inputbox title 10 40)
<dialog box shown>
<type "hello">
hello

(通过Ask Ubuntu: How to get dialog box input directed to a variable?

您发布的脚本可以做同样的事情。它目前写入stdout。将exec 3>&1 1>&2放在顶部,这样它就会写入stderr,并将最终echo ${options[$selected]}更改为echo ${options[$selected]} >&3以将答案写入stdout。这样就不需要调用者来处理文件描述符了。

那说,提示不是很UNIX-y。考虑完全避开交互性以支持命令行参数,配置文件或环境变量。对于知道如何使用脚本并希望自行自动化的高级用户来说,这些选项更好。

  

我的主要目的是从我上次备份的选择中提交latest-stable-config,我认为这需要人们判断何时将备份视为适当稳定。

我个人处理的方式是编写一个带有几种模式的脚本。我们称之为backupsbackups --list将显示备份列表。你选择一个,然后调用backups --commit <id>,它将提交命名配置。没有参数的backups将显示不熟悉用户的使用情况。

$ backups
Usage: backups --list
   or: backups --commit <id>

Manages a selection of backups. Use --list to list available backups
and --commit to commit the latest stable config.
$ backups --list
4ac6  10 minutes ago
18f2  1 day ago
3019  7 days ago
$ backups --commit 4ac6