Nushell
安装 Nu !
快速开始
  • Nushell 之书
  • 命令参考列表
  • 实战指南
  • 语言参考指南
  • 贡献指南
博客
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
GitHub
安装 Nu !
快速开始
  • Nushell 之书
  • 命令参考列表
  • 实战指南
  • 语言参考指南
  • 贡献指南
博客
  • English
  • 中文
  • Deutsch
  • Français
  • Español
  • 日本語
  • Português do Brasil
  • Русский язык
GitHub
  • 简介
  • 安装
    • 默认 Shell
  • 快速入门
    • 快速入门
    • 在系统中四处移动
    • 用 Nu 的方式思考
    • Nushell 速查表
  • Nu 基础篇
    • 数据类型
    • 加载数据
    • 管道
    • 处理字符串
    • 处理列表
    • 处理记录(Records)
    • 处理表格
    • 导航和访问结构化数据
    • 特殊变量
  • Nushell 编程
    • 自定义命令
    • 别名
    • 运算符
    • 变量
    • 控制流
    • 脚本
    • 模块
      • 使用模块
      • 创建模块
    • 覆层
    • 排序
    • 测试你的 Nushell 代码
    • 最佳实践
  • Nu 作为 Shell 使用
    • 配置
    • 环境
    • 标准输入、输出和退出码
    • 运行系统(外部)命令
    • 如何配置第三方提示
    • 目录栈
    • Reedline,Nu 的行编辑器
    • 自定义补全
    • 外部命令
    • Nu 的配色和主题
    • 钩子
    • 后台任务
  • 迁移到 Nu
    • 从 Bash 到 Nu
    • 从 CMD.EXE 到 Nu
    • 从其他 Shell 或 DSL 到 Nu
    • 从命令式语言到 Nu
    • 从函数式语言到 Nu
    • Nushell 运算符
  • 设计说明
    • Nushell代码执行原理
  • (不怎么)高级篇
    • 标准库 (预览版)
    • Dataframes
    • 元数据
    • 创建你自己的错误
    • 并行
    • 插件
    • explore

控制流

Nushell 提供了几个命令来帮助确定不同代码组的执行方式。在编程语言中,这种功能通常被称为控制流。

提示

需要注意的一点是,本页讨论的所有命令都使用代码块。这意味着你可以在其中改变环境变量和其他可变变量。

已涵盖内容

下面我们介绍一些与控制流相关的命令,但在开始之前,值得注意的是,在其他章节中已经介绍了一些与控制流相关或可以在相同情况下使用的功能和概念。这些包括:

  • 管道页面上的管道。
  • 数据类型页面上的闭包。
  • 使用列表页面上的迭代命令。例如:
    • each
    • where
    • reduce

选择(条件)

以下命令根据给定的某些条件执行代码。

提示

选择/条件命令是表达式,因此它们返回值,与本页上的其他命令不同。这意味着以下代码是可行的。

'foo' | if $in == 'foo' { 1 } else { 0 } | $in + 2
# => 3

if

if 根据一个或多个条件的结果来评估分支代码块,类似于其他编程语言中的 "if" 功能。例如:

if $x > 0 { 'positive' }

当条件为 true($x 大于零)时返回 'positive',当条件为 false($x 小于或等于零)时返回 null。

我们可以在第一个代码块之后为 if 添加一个 else 分支,当条件为 false 时,它会执行并返回 else 代码块的结果值。例如:

if $x > 0 { 'positive' } else { 'non-positive' }

这次,当条件为 true($x 大于零)时返回 'positive',当条件为 false($x 小于或等于零)时返回 'non-positive'。

我们还可以像下面这样将多个 if 链接在一起:

if $x > 0 { 'positive' } else if $x == 0 { 'zero' } else { "negative" }

当第一个条件为 true($x 大于零)时,它将返回 'positive';当第一个条件为 false 且下一个条件为 true($x 等于零)时,它将返回 'zero';否则,它将返回 'negative'(当 $x 小于零时)。

match

match 根据给定的值执行多个条件分支之一。你还可以进行一些模式匹配来解包列表和记录等复合类型中的值。

match 的基本用法可以像其他语言中常见的 "switch" 语句一样有条件地运行不同的代码。match 检查 match 关键字后面的值是否等于每个分支开头 => 前面的值,如果相等,则执行该分支 => 后面的代码。

match 3 {
    1 => 'one',
    2 => {
        let w = 'w'
        't' + $w + 'o'
    },
    3 => 'three',
    4 => 'four'
}
# => three

分支可以返回单个值,或者如第二个分支所示,可以返回代码块的结果。

捕获所有分支

你还可以有一个捕获所有条件的分支,用于当给定值不匹配任何其他条件时,方法是让一个分支的匹配值为 _。

let foo = match 7 {
    1 => 'one',
    2 => 'two',
    3 => 'three',
    _ => 'other number'
}
$foo
# => other number

(提醒一下,match 是一个表达式,这就是为什么我们可以在这里将结果赋给 $foo)。

模式匹配

你可以使用模式匹配从列表和记录等类型中“解包”值。然后,你可以将变量赋给你想要解包的部分,并在匹配的表达式中使用它们。

let foo = { name: 'bar', count: 7 }
match $foo {
    { name: 'bar', count: $it } => ($it + 3),
    { name: _, count: $it } => ($it + 7),
    _ => 1
}
# => 10

第二个分支中的 _ 意味着它匹配任何具有 name 和 count 字段的记录,而不仅仅是 name 为 'bar' 的记录。

守卫

你还可以为每个分支添加一个称为“守卫”的附加条件,以确定是否应匹配该分支。为此,在匹配模式之后,在 => 之前放置 if 和条件。

let foo = { name: 'bar', count: 7 }
match $foo {
    { name: 'bar', count: $it } if $it < 5 => ($it + 3),
    { name: 'bar', count: $it } if $it >= 5 => ($it + 7),
    _ => 1
}
# => 14

你可以在模式匹配手册页面中找到有关 match 的更多详细信息。

循环

循环命令允许你多次重复一个代码块。

循环和其他迭代命令

循环命令的功能类似于将闭包应用于列表或表中的元素的命令,例如 each 或 where,很多时候你可以用任何一种方法完成同样的事情。例如:

mut result = []
for $it in [1 2 3] { $result = ($result | append ($it + 1)) }
$result
# => ╭───┬───╮
# => │ 0 │ 2 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => ╰───┴───╯


[1 2 3] | each { $in + 1 }
# => ╭───┬───╮
# => │ 0 │ 2 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => ╰───┴───╯

虽然如果你熟悉其他语言中的循环,可能会倾向于使用循环,但在 Nushell 中,当你能用任何一种方式解决问题时,使用应用闭包的命令被认为是更符合 Nushell 风格(惯用)的。原因在于使用循环有一个相当大的缺点。

循环的缺点

循环最大的缺点是它们是语句,而 each 是表达式。像 each 这样的表达式总会产生某个输出值,而语句则不会。

这意味着它们不能很好地与不可变变量一起工作,而使用不可变变量被认为是更符合 Nushell 风格的。在上一节的示例中,如果没有预先声明的可变变量,就不可能使用 for 来获取递增数字的列表,或任何值。

语句在需要某些输出的 Nushell 管道中也无法工作。事实上,如果你尝试这样做,Nushell 会报错:

[1 2 3] | for x in $in { $x + 1 } | $in ++ [5 6 7]
# => Error: nu::parser::unexpected_keyword
# =>
# =>   × Statement used in pipeline.
# =>    ╭─[entry #5:1:1]
# =>  1 │ [1 2 3] | for x in $in { $x + 1 } | $in ++ [5 6 7]
# =>    ·           ─┬─
# =>    ·            ╰── not allowed in pipeline
# =>    ╰────
# =>   help: 'for' keyword is not allowed in pipeline. Use 'for' by itself, outside of a pipeline.

因为 Nushell 非常面向管道,这意味着使用像 each 这样的表达式命令通常比循环语句更自然。

循环的优点

如果循环有这么大的缺点,为什么它们还存在呢?一个原因是,像 each 使用的闭包不能修改周围环境中的可变变量。如果你尝试在闭包中修改可变变量,你会得到一个错误:

mut foo = []
[1 2 3] | each { $foo = ($foo | append ($in + 1)) }
# => Error: nu::parser::expected_keyword
# =>
# =>   × Capture of mutable variable.
# =>    ╭─[entry #8:1:1]
# =>  1 │ [1 2 3] | each { $foo = ($foo | append ($in + 1)) }
# =>    ·                  ──┬─
# =>    ·                    ╰── capture of mutable variable
# =>    ╰────

如果你在闭包中修改环境变量,你可以,但它只会在闭包的作用域内修改它,在其他地方保持不变。然而,循环使用代码块,这意味着它们可以在更大的作用域内修改常规的可变变量或环境变量。

mut result = []
for $it in [1 2 3] { $result = ($result | append ($it + 1)) }
$result
# => ╭───┬───╮
# => │ 0 │ 2 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => ╰───┴───╯

for

for 循环遍历一个范围或集合,如列表或表。

for x in [1 2 3] { $x * $x | print }
# => 1
# => 4
# => 9

表达式命令替代方案

  • each
  • par-each
  • where/filter
  • reduce

while

while 循环执行同一个代码块,直到给定的条件为 false。

mut x = 0; while $x < 10 { $x = $x + 1 }; $x
# => 10

表达式命令替代方案

"until" 和其他 "while" 命令

  • take until
  • take while
  • skip until
  • skip while

loop

loop 无限循环一个代码块。你可以使用 break(如下一节所述)来限制它循环的次数。它对于连续运行的脚本也很有用,比如交互式提示符。

mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x
# => 11

break

break 将停止执行循环中的代码,并恢复在循环之后执行。有效地“跳出”循环。

for x in 1..10 { if $x > 3 { break }; print $x }
# => 1
# => 2
# => 3

continue

continue 将停止执行当前循环,跳过循环中剩余的代码,并进入下一个循环。如果循环通常会结束,比如 for 已经遍历了所有给定的元素,或者 while 的条件现在为 false,它将不会再次循环,执行将在循环块之后继续。

mut x = -1; while $x <= 6 { $x = $x + 1; if $x mod 3 == 0 { continue }; print $x }
# => 1
# => 2
# => 4
# => 5
# => 7

错误

error make

error make 创建一个错误,停止代码和任何调用它的代码的执行,直到它被 try 块处理,或者它结束脚本并输出错误消息。此功能与其他语言中的“异常”相同。

print 'printed'; error make { msg: 'Some error info' }; print 'unprinted'
# => printed
# => Error:   × Some error info
# =>    ╭─[entry #9:1:1]
# =>  1 │ print 'printed'; error make { msg: 'Some error info' }; print 'unprinted'
# =>    ·                  ─────┬────
# =>    ·                       ╰── originates from here
# =>    ╰────

传递给它的记录为捕获它的代码或最终的错误消息提供了一些信息。

你可以在创建自己的错误页面上找到有关 error make 和错误概念的更多信息。

try

try 将捕获在 try 的代码块中任何地方创建的错误,并在块之后恢复代码的执行。

try { error make { msg: 'Some error info' }}; print 'Resuming'
# => Resuming

这包括捕获内置错误。

try { 1 / 0 }; print 'Resuming'
# => Resuming

如果发生错误,结果值将是 nothing,如果没有发生错误,则为块的返回值。

如果在 try 块之后包含一个 catch 块,如果 try 块中发生错误,它将执行 catch 块中的代码。

try { 1 / 0 } catch { 'An error happened!' } | $in ++ ' And now I am resuming.'
# => An error happened! And now I am resuming.

如果没有发生错误,它将不会执行 catch 块。

try 也适用于外部命令:

try { ^nonexisting }; print 'a'
# => a

^nonexisting; print 'a'
# => Error: nu::shell::external_command
# =>
# =>   × External command failed
# =>    ╭─[entry #3:1:2]
# =>  1 │ ^nonexisting; print 'a'
# =>    ·  ─────┬─────
# =>    ·       ╰── Command `nonexisting` not found
# =>    ╰────
# =>   help: `nonexisting` is neither a Nushell built-in or a known external command

其他

return

return 提前结束一个闭包或命令,而不运行命令/闭包的其余部分,并返回给定的值。通常不是必需的,因为闭包或命令中的最后一个值也会被返回,但有时它可能很方便。

def 'positive-check' [it] {
    if $it > 0 {
        return 'positive'
    };

    'non-positive'
}
positive-check 3
# => positive

positive-check (-3)
# => non-positive

let positive_check = {|elt| if $elt > 0 { return 'positive' }; 'non-positive' }

do $positive_check 3
# => positive

do $positive_check (-3)
# => non-positive
在GitHub上编辑此页面
Contributors: voyage200🍬
Prev
变量
Next
脚本