代码之家  ›  专栏  ›  技术社区  ›  tifrel

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

  •  0
  • tifrel  · 技术社区  · 7 年前

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

    让我详细说明为什么许多问题都是通过设置退出状态或 echo ING还不够:

    • 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 内部脚本,用helper函数污染全局范围。
    2. 需要 eval ,尤其是因为混淆的多引号。
    3. 冗长。我首先需要源代码,然后调用一个函数,而我更愿意 bash inner.sh result .

    关于内部脚本的进一步信息

    内部脚本应该写入备用屏幕缓冲区。在这个缓冲区中,用户可以从数组中选择一个选项(通过箭头键或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 回复  |  直到 7 年前
        1
  •  3
  •   John Kugelman Michael Hodel    7 年前

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

    例如, dialog 如果你使用 --output-fd 1 告诉它把答案写给标准输出。它使用咒语将对话框绘制到备用屏幕,但都是在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 给标准答案。这样就不需要调用者篡改文件描述符。


    也就是说,提示不是很unix-y。考虑完全避免交互,而使用命令行参数、配置文件或环境变量。这些选项对于那些知道如何使用脚本并希望自己实现脚本自动化的高级用户来说更好。

    我在这里的主要目的是 latest-stable-config 从我最后一次备份的选择中,我认为这需要人工判断何时将备份视为适当的稳定。

    我个人处理这件事的方法是用几种模式写一个脚本。我们称之为 backups . backups --list 将显示备份列表。你挑一个然后打电话 backups --commit <id> 它将提交指定的配置。 备份 如果没有参数,则会为不熟悉的用户显示用法。

    $ 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
    推荐文章