如何从低级函数中终止fish脚本?

6

假设有一个名为 foo.fish 的脚本,该脚本只会输出“foo”、“help”或错误信息

function foo
  __parse_args $argv[1]
  echo foo
end

function __parse_args --argument option
  if test -z $option
    return  # No option to parse, return early
  end

  switch $option
    case -h --help
      echo "Shows this help and exits"
      return 0  # How can we exit 0 instead of return?
    case -\*
      echo "Error: '$option' not a valid option"
      return 1  # How can we exit 1 instead of return?
  end
end

实际表现:

↪ foo -h
Shows this help and exits
foo

期望的行为:


↪ foo -h
Shows this help and exits
< p > return 命令会停止当前内部函数并设置该函数的退出状态。

当在嵌套函数调用中时,我们如何提前退出脚本,并使用适当的退出代码?

请注意,我们不能使用 exit 命令,因为它会退出 shell 而不仅仅是脚本。


1
你说的“退出 shell 而不仅是脚本”是什么意思?除非你使用 source 或者 . 来运行这个脚本,否则它会在自己独立的 shell 中运行。 - Mark Reed
3个回答

5
除非你通过 source. 运行脚本,否则它将在自己的新 shell 进程中运行。exit 命令将终止该进程并返回调用该脚本的父 shell;exit 的参数将是退出后立即在父进程内部的 $status 值。
如果您实际上是在交互式 shell 中定义 foo 函数(通过 source. 或在 shell 提示符下键入/粘贴或在您的 .fishrc 或 ~/.config 中的启动文件中定义),那么 __parse_args 就没有办法从 foo 返回。 foo 必须显式地检查 __parse_args 的返回值(即在调用 __parse_args 后检查 $status),然后根据需要立即返回。这也意味着在处理 --help 时,__parse_args 必须返回与其他成功操作不同的值。
但是,除非 foo 的实际操作涉及对您的 shell 环境进行一些修改,否则我建议将其作为可执行的脚本文件而不是函数,例如将以下内容放入名为 foo 的文件中,该文件位于您的命令搜索 $PATH 的某个位置:
#!/usr/bin/env fish
function foo
  __parse_args $argv[1]
  echo foo
end

function __parse_args --argument option
  if test -z $option
    return  # No option to parse, return early
  end

  switch $option
    case -h --help
      echo "Shows this help and exits"
      exit 0  # How can we exit 0 instead of return?
    case -\*
      echo "Error: '$option' not a valid option"
      exit 1  # How can we exit 1 instead of return?
  end
end

foo $argv

这会得到期望的结果:

> foo
foo
> foo -x
Error: '-x' not a valid option
[1]> foo -h
Shows this help and exits
>

3
在样本脚本中,将“return 0”更改为“exit 0”,然后复制并粘贴函数,执行“foo-h”。您运行它的 shell 将关闭。根据“exit”的手册,它将退出(fish)shell。仅当在源文件中调用 exit 时,才会跳过文件的其余部分而不是退出 shell 本身。 - Dennis
是的,但在提示符处复制和粘贴与“source”或“.”相同 - 您正在交互式 shell 中定义函数,然后只需运行该函数。在fish中没有退出多级函数调用的方法。我建议将其制作为脚本。 - Mark Reed
~/.config/fish/functions/foo.fish 创建脚本仍然保留了 shell。 - Dennis
1
请注意,短路建议仅处理故障情况(从错误返回1),而不是优雅的情况(从帮助返回0)。 - Dennis
True。调用者需要在 $status 上再加一个 switch/case 来做正确的事情。 - Mark Reed

3
很晚才进入这个话题,但Mark提供的脚本仍然会导致Dennis退出Shell的原因似乎是它被放在了~/.config/fish/functions/foo.fish中,这是函数路径,而不是可执行路径。这意味着foo函数被自动加载和执行,而不是foo脚本本身。在这种情况下,“exit”会中止函数(从而中止Shell),而不是脚本的进程。
将脚本放在PATH而不是函数路径中应该可以使其正常工作。
或者,如果你真的想运行它作为自动加载的函数,在~/.config/fish/functions中,以下代码有点复杂,但可以作为脚本/函数工作。当然,脚本的名称必须是foo.fish。实质上,它是一个调用其包含文件作为脚本的函数。
#!/usr/bin/fish

function foo
  chmod 740 (status --current-filename)
  fish -c (status --current-filename)" __exec_script $argv"
end

if test "$_" = "source"
  or test -z "$argv"
  or ! test "$argv[1]" = "__exec_script"
  foo $argv
  exit
end

function __parse_args --argument option
  if test -z $option
    return  # No option to parse, return early
  end

  switch $option
    case -h --help
      echo "Shows this help and exits"
      exit 0  # How can we exit 0 instead of return?
    case -\*
      echo "Error: '$option' not a valid option"
      exit 1  # How can we exit 1 instead of return?
  end
end

set argv $argv[2..-1] # Remove first argument, the __exec_script magic token
__parse_args $argv[1]
echo foo

当你安装脚本的时候,如果你自己进行了操作,那么chmod命令就不是必须的。

生成的文件可以作为脚本进行调用:

~/.config/fish/functions/foo.fish      # Results in "foo"
~/.config/fish/functions/foo.fish -h   # Results in "Shows this help and exits"
~/.config/fish/functions/foo.fish -i   # Results in "Error: '-i' not a valid option"

as a function ...

foo              # Results in "foo"
foo -h           # Results in "Shows this help and exits"
foo -i           # Results in "Error: '-i' not a valid option"

或从其他来源获取它

source ~/.config/fish/functions/foo.fish         # Results in "foo"
source ~/.config/fish/functions/foo.fish -h      # Results in "Shows this help and exits"
source ~/.config/fish/functions/foo.fish -i      # Results in "Error: '-i' not a valid option"

...所有结果都是一样的:


2
__parse_args函数中,你可以为-h返回1
function foo
    __parse_args $argv[1]
        or return $status
    echo foo
end

这提供了一个非零的返回状态给-h,但这可能不是什么大问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接