从 shell 脚本启动交互式函数定义

从 shell 脚本启动交互式函数定义

使用 zsh 或 bash,我想运行一个脚本,该脚本可能会提示用户输入多个命令并将它们存储为函数,但是我发现eval "function $FNCNAME() {"echo "function $FNCNAME() {" | zsh -is除非直接键入否则无效,因为脚本不会在执行之前等待更多输入继续下一行或退出。

如何在接受用户输入的同时阻止脚本进程,并仅在用户使用final(不带引号)结束函数定义时才继续}

要求是:

  • shell 自己的上下文感知自动完成功能处于活动状态。
  • 脚本不应立即执行延迟的命令,但应能够调用或打印新函数的定义

这个问题仅限于一般的管道和交互问题。我不是要求:

  • 如何以任何方式向脚本提供预先编写的命令。
  • 如何提供“最佳的用户体验”或者如何改进它。
  • 为什么我不应该解决这个问题而做别的事情。

答案1

在 中zsh,它可能是这样的:

make-function() {
  local psvar=$1
  local line= body=
  print -ru2 Please enter the definition of the $psvar function, one line at a time, terminate with an empty line.
  while
    line=
    vared -p '%v> ' line
    [[ -n $line ]]
  do
    body+=$line$'\n'
  done
  functions[$psvar]=$body
}

那么例如:

$ make-function f
Please enter the definition of the f function, one line at a time, terminate with an empty line.
f> echo test
f> return 42
f>
$ f
test
$ echo $?
42
$ functions f
f () {
        echo test
        return 42
}

你也可以这样做:

vared 'functions[f]'

f在行编辑器中编辑函数体。请注意,Enter离开编辑器后,您需要Alt+EnterEsc后跟Enter(或Ctrl+ v, Ctrl+j就像 bash 的 readline 中一样)以像往常一样在函数体中输入文字换行符。

vared(变量编辑器)是一个内置函数,它调用 zsh 行编辑器(zle)来编辑变量的内容,$functions是一个特殊的关联数组,它将函数名称映射到其主体中代码的表示形式。

在 中bash,您可以通过使用IFS= read -rep "$psvar> " linewhere -eusing readline to edit来执行一些接近的操作$line,并且您可以使用eval "$psvar() { $body; }"名称和主体来创建函数。

答案2

这就是你想做的吗?

$ cat ./tst.sh
#!/usr/bin/env bash

tmp=$(mktemp) || exit 1
trap 'rm -f "$tmp"; exit' EXIT

fncname='foo'

printf 'List commands, terminated with Control-D\n' >&2

{
    printf '%s () {\n' "$fncname"
    while IFS= read -r line; do
        printf '\t%s\n' "$line"
    done
    printf '}\n'
} > "$tmp" &&
. "$tmp"

declare -f "$fncname"

"$fncname"

$ ./tst.sh
List commands, terminated with Control-D
echo 'hello'
date
echo 'world'
        <- I hit Control-D here
foo ()
{
    echo 'hello';
    date;
    echo 'world'
}
hello
Wed Jan  3 15:48:38 CST 2024
world

相关内容