diff --git a/.vuepress/configs/navbar/zh-CN.ts b/.vuepress/configs/navbar/zh-CN.ts index a46745f3e13..a3563e2bfae 100644 --- a/.vuepress/configs/navbar/zh-CN.ts +++ b/.vuepress/configs/navbar/zh-CN.ts @@ -1,8 +1,17 @@ import type { NavbarConfig } from '@vuepress/theme-default'; export const navbarZhCN: NavbarConfig = [ - { text: '书', link: '/zh-CN/book/' }, - // { text: "Contributor Book", link: "/contributor-book/" }, - { text: 'Cookbook', link: '/cookbook/' }, + { text: '安装 Nu !', link: '/zh-CN/book/installation' }, + { text: '快速开始', link: '/zh-CN/book/getting_started' }, + { + text: '文档', + children: [ + { text: 'Nushell 之书', link: '/zh-CN/book/' }, + { text: '命令参考列表', link: '/commands/' }, + { text: '实战指南', link: '/zh-CN/cookbook/' }, + { text: '语言参考指南', link: '/lang-guide/' }, + { text: '贡献指南', link: '/zh-CN/contributor-book/' }, + ], + }, { text: '博客', link: '/blog/' }, ]; diff --git a/.vuepress/configs/sidebar/zh-CN.ts b/.vuepress/configs/sidebar/zh-CN.ts index 7f181339827..df21cee1ddc 100644 --- a/.vuepress/configs/sidebar/zh-CN.ts +++ b/.vuepress/configs/sidebar/zh-CN.ts @@ -1,80 +1,172 @@ import type { SidebarConfig } from '@vuepress/theme-default'; +import { commandCategories } from './command_categories'; export const sidebarZhCN: SidebarConfig = { '/zh-CN/book/': [ { - text: '入门篇', + text: '简介', + link: '/zh-CN/book/README.md', + collapsible: false, + }, + { + text: '安装', + link: '/zh-CN/book/installation.md', + collapsible: false, + children: ['/zh-CN/book/default_shell.md'], + }, + { + text: '快速入门', + link: '/zh-CN/book/getting_started.md', collapsible: false, children: [ - '/zh-CN/book/README.md', - '/zh-CN/book/installation', - '/zh-CN/book/thinking_in_nu', - '/zh-CN/book/moving_around', + '/zh-CN/book/quick_tour.md', + '/zh-CN/book/moving_around.md', + '/zh-CN/book/thinking_in_nu.md', + '/zh-CN/book/cheat_sheet.md', ], }, { text: 'Nu 基础篇', + link: '/zh-CN/book/nu_fundamentals.md', collapsible: false, children: [ - '/zh-CN/book/types_of_data', - '/zh-CN/book/loading_data', - '/zh-CN/book/working_with_strings', - '/zh-CN/book/working_with_lists', - '/zh-CN/book/working_with_tables', - '/zh-CN/book/pipelines', - '/zh-CN/book/command_reference', + '/zh-CN/book/types_of_data.md', + '/zh-CN/book/loading_data.md', + '/zh-CN/book/pipelines.md', + '/zh-CN/book/working_with_strings.md', + '/zh-CN/book/working_with_lists.md', + '/zh-CN/book/working_with_records.md', + '/zh-CN/book/working_with_tables.md', + '/zh-CN/book/navigating_structured_data.md', + '/zh-CN/book/special_variables.md', ], }, { text: 'Nushell 编程', + link: '/zh-CN/book/programming_in_nu.md', collapsible: false, children: [ - '/zh-CN/book/custom_commands', - '/zh-CN/book/aliases', - '/zh-CN/book/operators', - '/zh-CN/book/variables_and_subexpressions', - '/zh-CN/book/scripts', - '/zh-CN/book/modules', - '/zh-CN/book/overlays', + '/zh-CN/book/custom_commands.md', + '/zh-CN/book/aliases.md', + '/zh-CN/book/operators.md', + '/zh-CN/book/variables.md', + '/zh-CN/book/control_flow.md', + '/zh-CN/book/scripts.md', + { + text: '模块', + link: '/zh-CN/book/modules.md', + collapsible: false, + children: [ + '/zh-CN/book/modules/using_modules.md', + '/zh-CN/book/modules/creating_modules.md', + ], + }, + '/zh-CN/book/overlays.md', + '/zh-CN/book/sorting.md', + '/zh-CN/book/testing.md', + '/zh-CN/book/style_guide.md', ], }, { text: 'Nu 作为 Shell 使用', + link: '/zh-CN/book/nu_as_a_shell.md', collapsible: false, children: [ - '/zh-CN/book/configuration', - '/zh-CN/book/environment', - '/zh-CN/book/stdout_stderr_exit_codes', - '/zh-CN/book/escaping', - '/zh-CN/book/3rdpartyprompts', - '/zh-CN/book/shells_in_shells', - '/zh-CN/book/line_editor', - '/zh-CN/book/externs', - '/zh-CN/book/custom_completions', - '/zh-CN/book/coloring_and_theming', + '/zh-CN/book/configuration.md', + '/zh-CN/book/environment.md', + '/zh-CN/book/stdout_stderr_exit_codes.md', + '/zh-CN/book/running_externals.md', + '/zh-CN/book/3rdpartyprompts.md', + '/zh-CN/book/directory_stack.md', + '/zh-CN/book/line_editor.md', + '/zh-CN/book/custom_completions.md', + '/zh-CN/book/externs.md', + '/zh-CN/book/coloring_and_theming.md', '/zh-CN/book/hooks.md', + '/zh-CN/book/background_jobs.md', ], }, { text: '迁移到 Nu', + link: '/zh-CN/book/coming_to_nu.md', collapsible: false, children: [ - '/zh-CN/book/coming_from_bash', - '/zh-CN/book/nushell_map', - '/zh-CN/book/nushell_map_imperative', - '/zh-CN/book/nushell_map_functional', - '/zh-CN/book/nushell_operator_map', + '/zh-CN/book/coming_from_bash.md', + '/zh-CN/book/coming_from_cmd.md', + '/zh-CN/book/nushell_map.md', + '/zh-CN/book/nushell_map_imperative.md', + '/zh-CN/book/nushell_map_functional.md', + '/zh-CN/book/nushell_operator_map.md', ], }, { - text: '高级篇', + text: '设计说明', + link: '/zh-CN/book/design_notes.md', + collapsible: false, + children: ['/zh-CN/book/how_nushell_code_gets_run.md'], + }, + { + text: '(不怎么)高级篇', + link: '/zh-CN/book/advanced.md', + collapsible: false, + children: [ + '/zh-CN/book/standard_library.md', + '/zh-CN/book/dataframes.md', + '/zh-CN/book/metadata.md', + '/zh-CN/book/creating_errors.md', + '/zh-CN/book/parallelism.md', + '/zh-CN/book/plugins.md', + '/zh-CN/book/explore.md', + ], + }, + ], + '/commands/': [ + { + text: '命令分类', + collapsible: false, + children: commandCategories, + }, + ], + '/zh-CN/contributor-book/': [ + { + text: '贡献者指南', + link: '/zh-CN/contributor-book/README.md', + collapsible: false, + children: [ + '/zh-CN/contributor-book/README.md', + '/zh-CN/contributor-book/philosophy', + '/zh-CN/contributor-book/philosophy_0_80', + '/zh-CN/contributor-book/commands', + '/zh-CN/contributor-book/plugins', + '/zh-CN/contributor-book/plugin_protocol_reference', + ], + }, + ], + '/zh-CN/cookbook/': [ + { + text: '实战指南', collapsible: false, children: [ - '/zh-CN/book/dataframes', - '/zh-CN/book/metadata', - '/zh-CN/book/creating_errors', - '/zh-CN/book/parallelism', - '/zh-CN/book/plugins', + '/zh-CN/cookbook/README.md', + '/zh-CN/cookbook/setup', + '/zh-CN/cookbook/help', + '/zh-CN/cookbook/system', + '/zh-CN/cookbook/parsing', + '/zh-CN/cookbook/foreign_shell_scripts', + '/zh-CN/cookbook/pattern_matching', + '/zh-CN/cookbook/custom_completers', + '/zh-CN/cookbook/external_completers', + '/zh-CN/cookbook/modules', + '/zh-CN/cookbook/files', + '/zh-CN/cookbook/git', + '/zh-CN/cookbook/parsing_git_log', + '/zh-CN/cookbook/input_listen_keys', + '/zh-CN/cookbook/http', + '/zh-CN/cookbook/direnv', + '/zh-CN/cookbook/ssh_agent', + '/zh-CN/cookbook/tables', + '/zh-CN/cookbook/polars_v_pandas_v_nushell', + '/zh-CN/cookbook/jq_v_nushell', ], }, ], diff --git a/cookbook/setup.md b/cookbook/setup.md index e619ce3e404..5fd78698397 100644 --- a/cookbook/setup.md +++ b/cookbook/setup.md @@ -32,7 +32,7 @@ Because you can append a list of paths, you can append multiple at once. You can $env.PATH ++= [ '~/.local/bin', ($env.CARGO_HOME | path join "bin") ] ``` -Because PATH order makes a difference, you may want to *prepend* your paths instead, so that they take precedence over other executables with the same name: +Because PATH order makes a difference, you may want to _prepend_ your paths instead, so that they take precedence over other executables with the same name: ``` use std/util "path add" @@ -84,6 +84,7 @@ You should now be able to run `config nu` or `config env` and edit those files e ```nu $env.APPDATA ``` + --- ### Use hooks to export state via environment variables @@ -95,11 +96,11 @@ To be most compatible, the `starship` binary will run every prompt render and is absolute stateless. Nushell, however, is very stateful in a single instance. -[Hooks](https://www.nushell.sh/book/hooks.html#hooks) allow registration of +[Hooks](../book/hooks.md) allow registration of custom callback functions. In this case, the `pre_prompt` hook is very useful. With it, we can export state information as an environment variable, for -example, what [overlays](https://www.nushell.sh/book/overlays.html) are +example, what [overlays](../book/overlays.md) are currently activated. ```nu diff --git a/zh-CN/README.md b/zh-CN/README.md index 3228cf60289..2dee9b63302 100755 --- a/zh-CN/README.md +++ b/zh-CN/README.md @@ -7,7 +7,7 @@ actionText: 点此了解更多 → actionLink: /zh-CN/book/ features: - title: 利用管道控制任意系统 - details: Nu 可以在 Linux、macOS 和 Windows 上运行。一次学习,处处可用。 + details: Nu 可以在 Linux、macOS、BSD 和 Windows 上运行。一次学习,处处可用。 - title: 一切皆数据 details: Nu 管道使用结构化数据,你可以用同样的方式安全地选择,过滤和排序。停止解析字符串,开始解决问题。 - title: 强大的插件系统 @@ -34,20 +34,39 @@ Nushell 可以通过 [你喜欢的软件包管理器](https://repology.org/proje #### macOS / Linux: +##### Homebrew + ```shell $ brew install nushell ``` +##### Nix profile + +```shell +$ nix profile install nixpkgs#nushell +``` + #### Windows: ```powershell +# 安装到用户范围(默认)。 winget install nushell +# 系统范围安装(以管理员身份运行)。 +winget install nushell --scope machine ``` 完成安装后,输入 `nu` 来启动 Nu。 +## 文档 + +- [入门](/zh-CN/book/getting_started.html) 指引你熟悉 Nushell +- [Nu 之异同](/zh-CN/book/coming_to_nu.html) 描述了 Nu 与其他语言和 Shell 的异同之处 +- [Nu 基础](/zh-CN/book/nu_fundamentals.html) 是对 Nu 基础知识更详尽、更有条理的描述 +- [Nu 编程](/zh-CN/book/programming_in_nu.html) 将 Nu 作为一门编程语言进行描述 +- [作为 Shell 的 Nu](/zh-CN/book/nu_as_a_shell.html) 让你深入了解 Shell 环境中的交互功能和可配置性 + ## 社区 如果你有任何问题可以在 [Discord](https://discord.gg/NtAbbGn) 上找到我们。 -您可以通过 [意见反馈](https://github.com/nushell/nushell.github.io/issues) 或者 [贡献 PR](https://github.com/nushell/nushell.github.io/pulls) 来帮助我们完善此网站。 +你可以通过 [意见反馈](https://github.com/nushell/nushell.github.io/issues) 或者 [贡献 PR](https://github.com/nushell/nushell.github.io/pulls) 来帮助我们完善此网站。 diff --git a/zh-CN/book/3rdpartyprompts.md b/zh-CN/book/3rdpartyprompts.md index 31f975afd46..7596856ad1d 100644 --- a/zh-CN/book/3rdpartyprompts.md +++ b/zh-CN/book/3rdpartyprompts.md @@ -1,12 +1,14 @@ -# 配置第三方提示 +# 如何配置第三方提示 -## Nerdfonts +## Nerd Fonts -Nerdfonts 并不是必需的,但它们能使呈现效果更好。 +Nerd Fonts 并非必需,但它们可以通过额外的字形和图标来改善提示符的显示效果。 -[网站](https://www.nerdfonts.com) +> Nerd Fonts 为面向开发人员的字体提供了大量的字形(图标)。 +> 特别是,它从流行的“图标字体”(如 Font Awesome、Devicons、Octicons 等)中添加了大量额外的字形。 -[仓库](https://github.com/ryanoasis/nerd-fonts) +- [Nerd Fonts 网站](https://www.nerdfonts.com) +- [源仓库](https://github.com/ryanoasis/nerd-fonts) ## oh-my-posh @@ -16,18 +18,23 @@ Nerdfonts 并不是必需的,但它们能使呈现效果更好。 如果你喜欢 [oh-my-posh](https://ohmyposh.dev/),可以通过以下几个步骤在 Nushell 里使用 oh-my-posh,它与 Nushell 一起配合得很好。在 Nushell 里设置 oh-my-posh 的步骤: -1. 安装 Oh My Posh 并按照 [指南](https://ohmyposh.dev/docs/linux#installation)下载 oh-my-posh 的主题。 -2. 下载并安装一个 [Nerdfonts 字体](https://github.com/ryanoasis/nerd-fonts)。 -3. 在`~/.config/nushell/config.nu`(或由`$nu.config-path`输出的路径)中设置`PROMPT_COMMAND`,将`M365Princess.mp.json`改为你喜欢的任何 [主题](https://ohmyposh.dev/docs/themes)。 +1. 按照[指南](https://ohmyposh.dev/docs/installation/linux)安装 Oh My Posh。 +2. 下载并安装一个 [nerd font](https://github.com/ryanoasis/nerd-fonts)。 +3. 生成 `.oh-my-posh.nu` 文件。默认情况下,它将生成到你的主目录。你可以使用 `--config` 来指定一个主题,否则,oh-my-posh 会使用一个默认主题。 +4. 通过在 `~/.config/nushell/config.nu`(或由 `$nu.config-path` 输出的路径)中添加 `source ~/.oh-my-posh.nu` 来初始化 oh-my-posh 提示符。 ```nu -$env.PROMPT_COMMAND = { oh-my-posh --config ~/.poshthemes/M365Princess.omp.json } +# 生成 .oh-my-posh.nu 文件 +oh-my-posh init nu --config ~/.poshthemes/M365Princess.omp.json + +# 在你的 config.nu 文件中添加此行,以便在 shell 启动时初始化 oh-my-posh.nu +source ~/.oh-my-posh.nu ``` MacOS 用户配置步骤: -1. 你可以通过`brew`安装`oh-my-posh`,可以参考这里的 [指南](https://ohmyposh.dev/docs/macos); -2. 下载并安装一个 [Nerdfonts 字体](https://github.com/ryanoasis/nerd-fonts); +1. 你可以通过`brew`安装`oh-my-posh`,可以参考这里的 [指南](https://ohmyposh.dev/docs/installation/macos); +2. 下载并安装一个 [nerd font](https://github.com/ryanoasis/nerd-fonts); 3. 在`$nu.config-path`输出的文件中设置`PROMPT_COMMAND`,可以参考下面的代码片段: ```nu @@ -36,7 +43,7 @@ let posh_theme = $'($posh_dir)/share/oh-my-posh/themes/' # Change the theme names to: zash/space/robbyrussel/powerline/powerlevel10k_lean/ # material/half-life/lambda Or double lines theme: amro/pure/spaceship, etc. # For more [Themes demo](https://ohmyposh.dev/docs/themes) -$env.PROMPT_COMMAND = { oh-my-posh prompt print primary --config $'($posh_theme)/zash.omp.json' } +$env.PROMPT_COMMAND = { || oh-my-posh prompt print primary --config $'($posh_theme)/zash.omp.json' } # Optional $env.PROMPT_INDICATOR = $"(ansi y)$> (ansi reset)" ``` @@ -47,9 +54,19 @@ $env.PROMPT_INDICATOR = $"(ansi y)$> (ansi reset)" [仓库](https://github.com/starship/starship) -1. 参照上面的链接,安装 Starship; -2. 根据你的喜好,安装 nerdfonts; -3. 使用下面的配置示例,请确保设置`STARSHIP_SHELL`环境变量; +1. 参照上面的链接,安装 Starship。 +2. 根据你的喜好,安装 nerdfonts。 +3. 使用下面的配置示例,请确保设置`STARSHIP_SHELL`环境变量。 + +::: tip +另一种启用 Starship 的方法在 [Starship 快速安装](https://starship.rs/#nushell)说明中有描述。 + +以上链接是 Starship 和 Nushell 的官方集成,也是让 Starship 运行起来的最简单方法,无需任何手动操作: + +- Starship 将创建自己的配置/环境设置脚本 +- 你只需在 `env.nu` 中创建它,并在 `config.nu` 中 `use` 它 + +::: 下面是一个关于 Starship 的配置示例: @@ -61,7 +78,7 @@ def create_left_prompt [] { } # Use nushell functions to define your right and left prompt -$env.PROMPT_COMMAND = { create_left_prompt } +$env.PROMPT_COMMAND = { || create_left_prompt } $env.PROMPT_COMMAND_RIGHT = "" # The prompt indicators are environmental variables that represent @@ -79,10 +96,6 @@ nushell on 📙 main is 📦 v0.60.0 via 🦀 v1.59.0 ❯ ``` -你可以在 [官方 Starship 配置文档](https://github.com/starship/starship#step-2-setup-your-shell-to-use-starship)中了解更多关于配置 Starship 的信息。 - -另一种启用 Starship 的方法在 [Starship 快速安装](https://starship.rs/#nushell)说明中有描述。 - ## Purs [仓库](https://github.com/xcambar/purs) diff --git a/zh-CN/book/README.md b/zh-CN/book/README.md index 1e04a2616e7..81661e7880f 100644 --- a/zh-CN/book/README.md +++ b/zh-CN/book/README.md @@ -1,75 +1,65 @@ # 介绍 -你好,欢迎来到 Nushell 项目。这个项目的目标是继承 Unix Shell 中用管道把简单的命令连接在一起的理念,并将其带到更具现代风格的开发中。因此,Nushell 不是一个纯粹的 shell 或编程语言,而是通过将一种丰富的编程语言和功能齐全的 shell 结合到一个软件包中,实现了二者的连接。 +你好!欢迎来到 Nushell 项目。 +这个项目的目标是继承 Unix Shell 中用管道把简单的命令连接在一起的理念,并将其带到更具现代风格的开发中。 +因此,Nushell 不是一个纯粹的 shell 或编程语言,而是通过将一种丰富的编程语言和功能齐全的 shell 结合到一个软件包中,实现了二者的连接。 -Nu 汲取了很多常见领域的灵感:传统 Shell 比如 Bash、基于对象的 Shell 比如 PowerShell、逐步类型化的语言比如 TypeScript、函数式编程、系统编程,等等。但是,Nu 并不试图成为万金油,而是把精力集中在做好这几件事上: +Nu 汲取了很多常见领域的灵感:传统 Shell 比如 Bash、基于对象的 Shell 比如 PowerShell、逐步类型化的语言比如 TypeScript、函数式编程、系统编程,等等。 +但是,Nu 并不试图成为万金油,而是把精力集中在做好这几件事上: -- 作为一个具有现代感的灵活的跨平台 Shell; -- 作为一种现代的编程语言,解决与数据有关的问题; -- 给予清晰的错误信息和干净的 IDE 支持。 +- 作为一个具有现代感的灵活的跨平台 Shell +- 作为一种现代的编程语言,解决与数据有关的问题 +- 给予清晰的错误信息和干净的 IDE 支持 -了解 Nu 能做什么的最简单的方法是从一些例子开始,所以让我们深入了解一下。 +## 本书内容 -当你运行 [`ls`](/commands/docs/ls.md) 这样的命令时,你会注意到的第一件事是,你得到的不是一个文本块,而是一个结构化的表格: +本书分为章节,每个章节又分为多个小节。 +你可以点击章节标题获取更多信息。 -@[code](@snippets/introduction/ls_example.sh) +- [安装](installation.md),帮助你把 Nushell 安装到系统中。 +- [入门指南](getting_started.md) 向你展示基本用法。它还解释了一些 Nushell 与典型 Shell(如 Bash)不同的设计原则。 +- [Nu 基础](nu_fundamentals.md) 解释了 Nushell 语言的基本概念。 +- [Nu 编程](programming_in_nu.md) 更深入地探讨语言特性,并展示几种组织和构建代码的方法。 +- [Nu 作为 Shell](nu_as_a_shell.md) 专注于 Shell 功能,特别是配置和环境。 +- [从其他环境迁移到 Nu](coming_to_nu.md) 旨在为来自其他 Shell 或语言的用户提供快速入门。 +- [设计说明](design_notes.md) 深入解释了 Nushell 的一些设计选择。 +- [(不那么)高级](advanced.md) 包括一些更高级的主题(它们并不是那么高级,一定要去看看!)。 -该表不仅仅是以不同的方式显示目录,就像电子表格中的表一样,它还允许我们以更加互动的方式来处理数据。 +## Nushell 的多个组成部分 -我们要做的第一件事是按大小对我们的表进行排序。要做到这一点,我们将从 [`ls`](/commands/docs/ls.md) 中获取输出,并将其输入到一个可以根据列的内容对表进行排序的命令中: +Nushell 项目由多个不同的仓库和子项目组成。 +你可以在 [GitHub 上的组织](https://github.com/nushell) 中找到它们。 -@[code](@snippets/introduction/ls_sort_by_reverse_example.sh) +- 主要的 Nushell 仓库可以在[这里](https://github.com/nushell/nushell)找到。它被分成多个 crate,如果你愿意,可以在你自己的项目中作为独立库使用。 +- 我们的 [nushell.sh](https://www.nushell.sh) 页面(包括本书)的仓库可以在[这里](https://github.com/nushell/nushell.github.io)找到。 +- Nushell 有自己的行编辑器,[它有自己的仓库](https://github.com/nushell/reedline) +- [`nu_scripts`](https://github.com/nushell/nu_scripts) 是一个与其他用户分享脚本和模块的地方,直到我们有某种包管理器。 +- [Nana](https://github.com/nushell/nana) 是一个实验性的努力,探索 Nushell 的图形用户界面。 +- [Awesome Nu](https://github.com/nushell/awesome-nu) 包含与 Nushell 生态系统配合使用的工具列表:插件、脚本、编辑器扩展、第三方集成等。 +- [Nu 展示](https://github.com/nushell/showcase) 是一个分享关于 Nushell 的作品的地方,无论是博客、艺术作品还是其他东西。 +- [请求评论 (RFC)](https://github.com/nushell/rfcs) 作为提出和讨论主要设计变更的地方。虽然目前利用不足,但我们预计在接近和超过 1.0 版本时会更多地使用它。 -你可以看到,为了达到这个目的,我们没有向 [`ls`](/commands/docs/ls.md) 传递命令行参数。取而代之的是,我们使用了 Nu 提供的 [`sort-by`](/commands/docs/sort-by.md) 命令来对 [`ls`](/commands/docs/ls.md) 命令的输出进行排序。为了在顶部看到最大的文件,我们还使用了 [`reverse`](/commands/docs/reverse.md) 命令。 +## 贡献 -Nu 提供了许多可以对表进行操作的命令,例如,我们可以过滤 [`ls`](/commands/docs/ls.md) 表的内容,使其只显示超过 1 千字节的文件。 +我们欢迎贡献! +[正如你所见](#nushell-的多个组成部分),有很多地方可以贡献。 +大多数仓库都包含 `CONTRIBUTING.md` 文件,其中包含提示和细节,应该能帮助你入门(如果没有,考虑贡献一个修复!)。 -@[code](@snippets/introduction/ls_where_example.sh) +Nushell 本身是用 [Rust](https://www.rust-lang.org) 编写的。 +然而,你不必是 Rust 程序员也能提供帮助。 +如果你了解一些 web 开发,你可以为改进这个网站或 Nana 项目做出贡献。 +[数据框](dataframes.md) 可以利用你的数据处理专业知识。 -就像在 Unix 哲学中一样,能够让命令相互对话给我们提供了在许多不同的组合中对命令进行混搭的方法。我们来看看一个不同的命令: +如果你写了一个很酷的脚本、插件或将 Nushell 集成到某处,我们欢迎你为 `nu_scripts` 或 Awesome Nu 做出贡献。 +发现带有重现步骤的 bug 并为它们提交 GitHub issue 也是一种有价值的帮助! +仅仅通过使用 Nushell,你就可以为 Nushell 做出贡献! -@[code](@snippets/introduction/ps_example.sh) +由于 Nushell 发展迅速,本书不断需要更新。 +为本书做出贡献不需要任何特殊技能,只需要对 Markdown 有基本的熟悉。 +此外,你可以考虑将其部分翻译成你的语言。 -如果你使用过 Linux,你可能对 [`ps`](/commands/docs/ps.md) 命令很熟悉。通过它,我们可以得到一个当前系统正在运行的所有进程的列表,它们的状态是什么,以及它们的名字是什么,我们还可以看到这些进程的 CPU 负载。 +## 社区 -如果我们想显示那些正在活跃使用 CPU 的进程呢?就像我们之前对 [`ls`](/commands/docs/ls.md) 命令所做的那样,我们也可以利用 [`ps`](/commands/docs/ps.md) 命令返回给我们的表格来做到: - -@[code](@snippets/introduction/ps_where_example.sh) - -到目前为止,我们一直在使用 [`ls`](/commands/docs/ls.md) 和 [`ps`](/commands/docs/ps.md) 来列出文件和进程。Nu 还提供了其他可以创建有用信息表格的命令。接下来,让我们试一下 [`date`](/commands/docs/date.md) 和 [`sys`](/commands/docs/sys.md)。 - -运行 [`date now`](/commands/docs/date_now.md) 输出关于当前日期和时间的信息: - -@[code](@snippets/introduction/date_example.sh) - -为了将获得的日期以表格形式展示,我们可以把它输入到 [`date to-table`](/commands/docs/date_to-table.md) 中: - -@[code](@snippets/introduction/date_table_example.sh) - -运行 [`sys`](/commands/docs/sys.md) 可以得到 Nu 所运行的系统的信息: - -@[code](@snippets/introduction/sys_example.sh) - -这与我们之前看到的表格有些不同。[`sys`](/commands/docs/sys.md) 命令输出了一个在单元格中包含结构化表格而非简单值的表格。要查看这些数据,我们需要**获取**([`get`](/commands/docs/get.md))待查看的列: - -@[code](@snippets/introduction/sys_get_example.sh) - -[`get`](/commands/docs/get.md) 命令让我们深入表的某一列内容中。在这里,我们要查看的是 `host` 列,它包含了 Nu 正在运行的主机的信息:操作系统名称、主机名、CPU,以及更多。让我们来获取系统上的用户名: - -@[code](@snippets/introduction/sys_get_nested_example.sh) - -现在,系统中只有一个名为 sophiajt 的用户。你会注意到,我们可以传递一个列路径(`host.sessions.name` 部分),而不仅仅是简单的列名称。Nu 会接受列路径并输出表中相应的数据。 - -你可能已经注意到其他一些不同之处:我们没有一个数据表,而只有一个元素:即字符串 `"sophiajt"`。Nu 既能处理数据表,也能处理字符串。字符串是使用 Nu 外部命令的一个重要部分。 - -让我们看看字符串在 Nu 外部命令里面是如何工作的。我们以之前的例子为例,运行外部的 `echo` 命令(`^` 告诉 Nu 不要使用内置的 [`echo`](/commands/docs/echo.md) 命令): - -@[code](@snippets/introduction/sys_get_external_echo_example.sh) - -敏锐的读者可能会发现这看起来和我们之前的非常相似!确实如此,但有一个重要的区别:我们用前面的值调用了 `^echo`。这允许我们把数据从 Nu 中传到外部命令 `echo`(或者 Nu 之外的任何命令,比如 `git`)。 - -### 获取帮助 - -任何 Nu 的内置命令的帮助文本都可以通过 [`help`](/commands/docs/help.md) 命令来找到。要查看所有命令,请运行 `help commands` 。你也可以通过执行 `help -f ` 来搜索一个主题: - -@[code](@snippets/introduction/help_example.sh) +讨论任何 Nushell 的主要地方是我们的 [Discord](https://discord.com/invite/NtAbbGn)。 +你也可以关注我们的 [博客](https://www.nushell.sh/blog) 获取新闻和更新。 +最后,你可以使用 GitHub 讨论或提交 GitHub issue。 diff --git a/zh-CN/book/advanced.md b/zh-CN/book/advanced.md new file mode 100644 index 00000000000..3dbadfbdaf0 --- /dev/null +++ b/zh-CN/book/advanced.md @@ -0,0 +1,20 @@ +# (并不那么)高级的功能 + +虽然“高级”这个标题可能听起来令人生畏,你可能会想跳过本章,但实际上,一些最有趣和最强大的功能可以在这里找到。 + +除了内置命令,Nushell 还有一个[标准库](standard_library.md)。 + +Nushell 操作的是 _结构化数据_。 +你可以说 Nushell 是一个“数据优先”的 shell 和编程语言。 +为了进一步探索以数据为中心的方向,Nushell 包含了一个功能齐全的数据帧处理引擎,使用 [Polars](https://github.com/pola-rs/polars) 作为后端。 +如果你想直接在 shell 中高效地处理大量数据,请务必查看[数据帧文档](dataframes.md)。 + +Nushell 中的值包含一些额外的[元数据](metadata.md)。 +例如,这些元数据可以用来[创建自定义错误](creating_errors.md)。 + +由于 Nushell 严格的作用域规则,[并行迭代集合](parallelism.md)非常容易,这可以帮助你通过只输入几个字符来加速长时间运行的脚本。 + +你可以使用 [`explore`](/commands/docs/explore.md) 命令[交互式地探索数据](explore.md)。 + +最后,你可以使用[插件](plugins.md)来扩展 Nushell 的功能。 +几乎任何东西都可以成为插件,只要它以 Nushell 理解的协议与 Nushell 通信。 diff --git a/zh-CN/book/aliases.md b/zh-CN/book/aliases.md index 8d5cb0cca2a..5ce84334a8e 100644 --- a/zh-CN/book/aliases.md +++ b/zh-CN/book/aliases.md @@ -1,6 +1,6 @@ # 别名 -Nushell 中的别名提供了一种简单的文本替换方式,这允许你为一个较长的命令创建一个简写名称,包括其默认参数。 +Nushell 中的别名提供了一种简单的命令调用替换方式(包括外部和内部命令)。这允许你为一个较长的命令(包括其默认参数)创建一个简写名称。 例如,让我们创建一个名为 `ll` 的别名,它将展开为 `ls -l`。 @@ -22,21 +22,106 @@ ll -a 可得到与输入了`ls -l -a`一样的结果。 -## 编写带有管道的别名 +## 列出所有已加载的别名 -如果你想在别名中添加管道,你必须用小括号把它括起来,小括号是一对圆括号`()`,用来标记你的一组带有管道命令: +你可以在 `scope aliases` 和 `help aliases` 中看到所有可用的别名。 + +## 持久化 + +为了使你的别名持久化,它必须被添加到你的 _config.nu_ 文件中。通过运行 `config nu` 打开编辑器并插入它们,然后重新启动 nushell。 +例如,对于上面的 `ll` 别名,你可以在 _config.nu_ 的任何地方添加 `alias ll = ls -l`。 ```nu -alias lsname = (ls | get name) +$env.config = { + # 主要配置 +} + +alias ll = ls -l + +# 其他一些配置和脚本加载 ``` -下面是一个带有多个管道的别名: +## 在别名中使用管道 + +请注意,`alias uuidgen = uuidgen | tr A-F a-f`(使 mac 上的 uuidgen 行为与 linux 一致)将无法工作。 +解决方案是定义一个不带参数的命令,通过 `^` 调用系统程序 `uuidgen`。 ```nu -alias lt = (ls | sort-by modified -r | sort-by type) +def uuidgen [] { ^uuidgen | tr A-F a-f } ``` -## 持久化 +更多信息请参阅本书的[自定义命令](custom_commands.md)部分。 + +或者一个更符合 nushell 风格的内部命令示例: + +```nu +def lsg [] { ls | sort-by type name -i | grid -c | str trim } +``` + +在网格中显示所有列出的文件和文件夹。 + +## 使用别名替换现有命令 + +::: warning 警告! +替换命令时,最好先“备份”命令,以避免递归错误。 +::: -为了使你的别名持久化,它必须被添加到你的 _config.nu_ 文件中。 -关于如何持久化别名,以便在你启动 Nushell 时它们是可用的,请参考 [配置](configuration.md) 部分。 +如何备份像 `ls` 这样的命令: + +```nu +alias core-ls = ls # 这将为 ls 创建一个名为 core-ls 的新别名 +``` + +现在你可以在你的 nu 编程中使用 `core-ls` 作为 `ls`。你将在下面看到如何使用 `core-ls`。 + +你需要使用别名的原因是,与 `def` 不同,别名是位置相关的。因此,在重新定义旧命令之前,你需要先用别名“备份”它。 +如果你不备份命令,而是使用 `def` 替换命令,你将会得到一个递归错误。 + +```nu +def ls [] { ls }; ls # *不要*这样做!这会抛出递归错误 + +#输出: +#Error: nu::shell::recursion_limit_reached +# +# × Recursion limit (50) reached +# ╭─[C:\Users\zolodev\AppData\Roaming\nushell\config.nu:807:1] +# 807 │ +# 808 │ def ls [] { ls }; ls +# · ───┬── +# · ╰── This called itself too many times +# ╰──── +``` + +推荐的替换现有命令的方法是遮蔽(shadow)该命令。 +这是一个遮蔽 `ls` 命令的例子。 + +```nu +# 将内置的 ls 命令别名为 ls-builtins +alias ls-builtin = ls + +# 列出目录中项目的的文件名、大小和修改时间。 +def ls [ + --all (-a), # 显示隐藏文件 + --long (-l), # 获取每个条目的所有可用列(较慢;列取决于平台) + --short-names (-s), # 只打印文件名,不打印路径 + --full-paths (-f), # 将路径显示为绝对路径 + --du (-d), # 显示目录的视在大小(“磁盘使用情况”)而不是目录元数据大小 + --directory (-D), # 列出指定的目录本身而不是其内容 + --mime-type (-m), # 在类型列中显示 mime-type 而不是 'file'(仅基于文件名;不检查文件内容) + --threads (-t), # 使用多个线程列出内容。输出将是不确定的。 + ...pattern: glob, # 要使用的 glob 模式。 +]: [ nothing -> table ] { + let pattern = if ($pattern | is-empty) { [ '.' ] } else { $pattern } + (ls-builtin + --all=$all + --long=$long + --short-names=$short_names + --full-paths=$full_paths + --du=$du + --directory=$directory + --mime-type=$mime_type + --threads=$threads + ...$pattern + ) | sort-by type name -i +} +``` diff --git a/zh-CN/book/background_jobs.md b/zh-CN/book/background_jobs.md new file mode 100644 index 00000000000..614f96681ca --- /dev/null +++ b/zh-CN/book/background_jobs.md @@ -0,0 +1,134 @@ +# 后台任务 + +Nushell 目前对基于线程的后台任务有实验性支持。 + +## 生成任务 + +任务可以使用 [`job spawn`](/commands/docs/job_spawn.md) 生成,它接收一个闭包并在后台线程中开始执行,返回一个生成的任务的唯一整数 ID: + +```nu +'i am' | save status.txt + +job spawn { sleep 10sec; ' inevitable' | save --append status.txt } +# => 1 + +open status.txt +# => i am + +# 等待 10 秒 +sleep 10sec + +open status.txt +# => i am inevitable +``` + +## 列出和终止任务 + +可以使用 [`job list`](/commands/docs/job_list.md) 命令查询活动任务,该命令返回一个包含当前正在执行的任务信息的表格。 +也可以使用 [`job kill`](/commands/docs/job_kill.md) 命令终止/中断任务,该命令会中断任务的线程并杀死任务的所有子进程: + +```nu +let id = job spawn { sleep 1day } + +job list +# => ┏━━━┳━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━┓ +# => ┃ # ┃ id ┃ type ┃ pids ┃ +# => ┣━━━╋━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━┫ +# => ┃ 0 ┃ 1 ┃ thread ┃ [list 0 items] ┃ +# => ┗━━━┻━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━┛ + +job kill $id + +job list +# => ╭────────────╮ +# => │ empty list │ +# => ╰────────────╯ +``` + +## 任务挂起 + +在 Unix 目标(如 Linux 和 macOS)上,Nushell 还支持使用 Ctrl+Z 挂起外部命令。当一个正在运行的进程被挂起时,它会变成一个“冻结”的后台任务: + +```nu +long_running_process # 这开始运行,然后按下 Ctrl+Z +# => Job 1 is frozen + +job list +# => ┏━━━┳━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━┓ +# => ┃ # ┃ id ┃ type ┃ pids ┃ +# => ┣━━━╋━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━┫ +# => ┃ 0 ┃ 1 ┃ frozen ┃ [list 1 items] ┃ +# => ┗━━━┻━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━┛ +``` + +可以使用 [`job unfreeze`](/commands/docs/job_unfreeze.md) 命令将冻结的任务带回前台: + +```nu +job unfreeze +# 进程从停止的地方恢复 +``` + +::: tip 提示 +对于熟悉其他 Unix shell 的人,你可以创建一个别名来模拟 `fg` 命令的行为: + +```nu +alias fg = job unfreeze +``` + +::: + +默认情况下,`job unfreeze` 将解冻最近冻结的任务。但是,你也可以指定要解冻的特定任务的 ID: + +```nu +vim +# => Job 1 is frozen + +long_running_process +# => Job 2 is frozen + +job unfreeze 1 +# 我们回到了 vim +``` + +## 任务间通信 + +可以使用 `job send ` 向任务发送数据,任务可以使用 `job recv` 接收数据: + +```nu +let jobId = job spawn { + job recv | save sent.txt +} + +'hello from the main thread' | job send $jobId + +sleep 1sec + +open sent.txt +# => hello from the main thread +``` + +主线程的任务 ID 为 0,因此你也可以反向发送数据: + +```nu +job spawn { + sleep 1sec + 'Hello from a background job' | job send 0 +} + +job recv +# => Hello from a background job +``` + +## 退出行为 + +与许多其他 shell 不同,Nushell 任务**不是**独立的进程, +而是作为后台线程实现的。 + +一个重要的副作用是,一旦 shell 进程退出,所有后台任务都将终止。 +因此,Nushell 没有类似 UNIX 的 `disown` 命令来防止任务在 shell 退出后终止。 +为了解决这个问题,未来计划实现 `job dispatch`, +用于生成独立的后台进程(有关进展,请参阅 [#15201](https://github.com/nushell/nushell/issues/15193?issue=nushell%7Cnushell%7C15201))。 + +此外,如果用户正在运行交互式 Nushell 会话并在有后台任务运行时运行 +[`exit`](/commands/docs/exit.md), +shell 会在提示用户确认 `exit` 之前警告他们。 diff --git a/zh-CN/book/cheat_sheet.md b/zh-CN/book/cheat_sheet.md new file mode 100644 index 00000000000..fd74396e6ff --- /dev/null +++ b/zh-CN/book/cheat_sheet.md @@ -0,0 +1,520 @@ +# Nushell 速查表 + +## 数据类型 + +将字符串转换为整数: + +```nu +"12" | into int +``` + +将当前日期转换为提供的时区: + +```nu +date now | date to-timezone "Asia/Shanghai" +``` + +更新记录的语言,如果未指定,则插入提供的值: + +```nu +{'name': 'nu', 'stars': 5, 'language': 'Python'} | upsert language 'Rust' +``` + +将字符串列表转换为 yaml: + +```nu +[one two three] | to yaml +``` + +打印表格数据: + +```nu +[[framework, language]; [Django, Python] [Laravel, PHP]] +``` + +从表中选择两个命名列并打印它们的值: + +```nu +[{name: 'Robert' age: 34 position: 'Designer'} + {name: 'Margaret' age: 30 position: 'Software Developer'} + {name: 'Natalie' age: 50 position: 'Accountant'} +] | select name position +``` + +## 字符串 + +插值文本: + +```nu +let name = "Alice" +$"greetings, ($name)!" +# => greetings, Alice! +``` + +按逗号分隔符拆分文本并将列表保存到 `string_list` 变量: + +```nu +let string_list = "one,two,three" | split row "," +$string_list +# => ╭───┬───────╮ +# => │ 0 │ one │ +# => │ 1 │ two │ +# => │ 2 │ three │ +# => ╰───┴───────╯ +``` + +检查字符串是否包含子字符串: + +```nu +"Hello, world!" | str contains "o, w" +# => true +``` + +用分隔符连接多个字符串: + +```nu +let str_list = [zero one two] +$str_list | str join ',' +# => zero,one,two +``` + +按索引切片文本: + +```nu +'Hello World!' | str substring 4..8 +# => o Wor +``` + +将字符串解析为命名列: + +```nu +'Nushell 0.80' | parse '{shell} {version}' +# => ╭───┬─────────┬─────────╮ +# => │ # │ shell │ version │ +# => ├───┼─────────┼─────────┤ +# => │ 0 │ Nushell │ 0.80 │ +# => ╰───┴─────────┴─────────╯ +``` + +解析逗号分隔值 (csv): + +```nu +"acronym,long\nAPL,A Programming Language" | from csv +# => ╭───┬─────────┬────────────────────────╮ +# => │ # │ acronym │ long │ +# => ├───┼─────────┼────────────────────────┤ +# => │ 0 │ APL │ A Programming Language │ +# => ╰───┴─────────┴────────────────────────╯ +``` + +在命令行终端中为文本着色: + +```nu +$'(ansi purple_bold)This text is a bold purple!(ansi reset)' +# => This text is a bold purple! +``` + +## 列表 + +在索引处插入列表值: + +```nu +[foo bar baz] | insert 1 'beeze' +# => ╭───┬───────╮ +# => │ 0 │ foo │ +# => │ 1 │ beeze │ +# => │ 2 │ bar │ +# => │ 3 │ baz │ +# => ╰───┴───────╯ +``` + +按索引更新列表值: + +```nu +[1, 2, 3, 4] | update 1 10 +# => ╭───┬────╮ +# => │ 0 │ 1 │ +# => │ 1 │ 10 │ +# => │ 2 │ 3 │ +# => │ 3 │ 4 │ +# => ╰───┴────╯ +``` + +在列表前添加值: + +```nu +let numbers = [1, 2, 3] +$numbers | prepend 0 +# => ╭───┬───╮ +# => │ 0 │ 0 │ +# => │ 1 │ 1 │ +# => │ 2 │ 2 │ +# => │ 3 │ 3 │ +# => ╰───┴───╯ +``` + +在列表后追加值: + +```nu +let numbers = [1, 2, 3] +$numbers | append 4 +# => ╭───┬───╮ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ 4 │ +# => ╰───┴───╯ +``` + +切片第一个列表值: + +```nu +[cammomile marigold rose forget-me-not] | first 2 +# => ╭───┬───────────╮ +# => │ 0 │ cammomile │ +# => │ 1 │ marigold │ +# => ╰───┴───────────╯ +``` + +遍历列表;`elt` 是当前列表值: + +```nu +let planets = [Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune] +$planets | each { |elt| $"($elt) is a planet of the solar system" } +# => ╭───┬─────────────────────────────────────────╮ +# => │ 0 │ Mercury is a planet of the solar system │ +# => │ 1 │ Venus is a planet of the solar system │ +# => │ 2 │ Earth is a planet of the solar system │ +# => │ 3 │ Mars is a planet of the solar system │ +# => │ 4 │ Jupiter is a planet of the solar system │ +# => │ 5 │ Saturn is a planet of the solar system │ +# => │ 6 │ Uranus is a planet of the solar system │ +# => │ 7 │ Neptune is a planet of the solar system │ +# => ╰───┴─────────────────────────────────────────╯ +``` + +带索引和值遍历列表: + +```nu +$planets | enumerate | each { |elt| $"($elt.index + 1) - ($elt.item)" } +# => ╭───┬─────────────╮ +# => │ 0 │ 1 - Mercury │ +# => │ 1 │ 2 - Venus │ +# => │ 2 │ 3 - Earth │ +# => │ 3 │ 4 - Mars │ +# => │ 4 │ 5 - Jupiter │ +# => │ 5 │ 6 - Saturn │ +# => │ 6 │ 7 - Uranus │ +# => │ 7 │ 8 - Neptune │ +# => ╰───┴─────────────╯ +``` + +将列表归约为单个值;`reduce` 提供对应用于列表中每个元素的累加器的访问: + +```nu +let scores = [3 8 4] +$"total = ($scores | reduce { |elt, acc| $acc + $elt })" +# => total = 15 +``` + +带初始值的归约 (`--fold`): + +```nu +let scores = [3 8 4] +$"total = ($scores | reduce --fold 1 { |elt, acc| $acc * $elt })" +# => total = 96 +``` + +访问列表中的第 3 个项目: + +```nu +let planets = [Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune] +$planets.2 +# => Earth +``` + +检查列表中是否有任何字符串以 `E` 开头: + +```nu +let planets = [Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune] +$planets | any {|elt| $elt | str starts-with "E" } +# => true +``` + +切片满足提供条件的项: + +```nu +let cond = {|x| $x < 0 }; [-1 -2 9 1] | take while $cond +# => ╭───┬────╮ +# => │ 0 │ -1 │ +# => │ 1 │ -2 │ +# => ╰───┴────╯ +``` + +## 表格 + +对表进行排序: + +```nu +ls | sort-by size +``` + +对表进行排序,获取前几行: + +```nu +ls | sort-by size | first 5 +``` + +连接具有相同列的两个表: + +```nu +let $a = [[first_column second_column third_column]; [foo bar snooze]] +let $b = [[first_column second_column third_column]; [hex seeze feeze]] +$a | append $b +# => ╭───┬──────────────┬───────────────┬──────────────╮ +# => │ # │ first_column │ second_column │ third_column │ +# => ├───┼──────────────┼───────────────┼──────────────┤ +# => │ 0 │ foo │ bar │ snooze │ +# => │ 1 │ hex │ seeze │ feeze │ +# => ╰───┴──────────────┴───────────────┴──────────────╯ +``` + +删除表的最后一列: + +```nu +let teams_scores = [[team score plays]; ['Boston Celtics' 311 3] ['Golden State Warriors', 245 2]] +$teams_scores | drop column +# => ╭───┬───────────────────────┬───────╮ +# => │ # │ team │ score │ +# => ├───┼───────────────────────┼───────┤ +# => │ 0 │ Boston Celtics │ 311 │ +# => │ 1 │ Golden State Warriors │ 245 │ +# => ╰───┴───────────────────────┴───────╯ +``` + +## 文件和文件系统 + +使用默认文本编辑器打开文本文件: + +```nu +start file.txt +``` + +将字符串保存到文本文件: + +```nu +'lorem ipsum ' | save file.txt +``` + +将字符串追加到文本文件末尾: + +```nu +'dolor sit amet' | save --append file.txt +``` + +将记录保存到 file.json: + +```nu +{ a: 1, b: 2 } | save file.json +``` + +按文件名递归搜索文件: + +```nu +glob **/*.{rs,toml} --depth 2 +``` + +监视文件,每当文件更改时运行命令: + +```nu +watch . --glob=**/*.rs {|| cargo test } +``` + +## 自定义命令 + +带有字符串类型参数的自定义命令: + +```nu +def greet [name: string] { + $"hello ($name)" +} +``` + +默认参数设置为 nushell 的自定义命令: + +```nu +def greet [name = "nushell"] { + $"hello ($name)" +} +``` + +通过为自定义命令定义标志来传递命名参数: + +```nu +def greet [ + name: string + --age: int +] { + [$name $age] +} + +greet world --age 10 +``` + +使用标志作为开关,并为 age 使用简写标志 (-a): + +```nu +def greet [ + name: string + --age (-a): int + --twice +] { + if $twice { + [$name $age $name $age] + } else { + [$name $age] + } +} +greet -a 10 --twice hello +``` + +使用 rest 参数接受任意数量的位置参数的自定义命令: + +```nu +def greet [...name: string] { + print "hello all:" + for $n in $name { + print $n + } +} +greet earth mars jupiter venus +# => hello all: +# => earth +# => mars +# => jupiter +# => venus +``` + +## 变量 + +不可变变量在声明后不能更改其值: + +```nu +let val = 42 +print $val +# => 42 +``` + +遮蔽变量(在不同作用域中声明同名变量): + +```nu +let val = 42 +do { let val = 101; $val } +# => 101 +$val +# => 42 +``` + +使用 mut 关键字声明可变变量: + +```nu +mut val = 42 +$val += 27 +$val +# => 69 +``` + +闭包和嵌套的 def 不能从其环境中捕获可变变量(会报错): + +```nu +mut x = 0 +[1 2 3] | each { $x += 1 } +# => Error: nu::parser::expected_keyword +# => +# => × Capture of mutable variable. +# => ╭─[entry #83:1:18] +# => 1 │ [1 2 3] | each { $x += 1 } +# => · ─┬ +# => · ╰── capture of mutable variable +# => ╰──── +``` + +常量变量是不可变的,并且在解析时完全求值: + +```nu +const file = 'path/to/file.nu' +source $file +``` + +如果提供的路径不正确,使用问号运算符 `?` 返回 null 而不是错误: + +```nu +let files = (ls) +$files.name?.0? +``` + +将管道的结果赋给变量: + +```nu +let big_files = (ls | where size > 10kb) +$big_files +``` + +## 模块 + +使用内联模块: + +```nu +module greetings { + export def hello [name: string] { + $"hello ($name)!" + } + + export def hi [where: string] { + $"hi ($where)!" + } +} +use greetings hello +hello "world" +``` + +从文件导入模块并在当前作用域中使用其环境: + +```nu +# greetings.nu +export-env { + $env.MYNAME = "Arthur, King of the Britons" +} +export def hello [] { + $"hello ($env.MYNAME)" +} + +use greetings.nu +$env.MYNAME +# => Arthur, King of the Britons +greetings hello +# => hello Arthur, King of the Britons! +``` + +在模块中使用 main 命令: + +```nu +# greetings.nu +export def hello [name: string] { + $"hello ($name)!" +} + +export def hi [where: string] { + $"hi ($where)!" +} + +export def main [] { + "greetings and salutations!" +} + +use greetings.nu +greetings +# => greetings and salutations! +greetings hello world +# => hello world! +``` diff --git a/zh-CN/book/coloring_and_theming.md b/zh-CN/book/coloring_and_theming.md index 22a488f3a20..86bf7999c1b 100644 --- a/zh-CN/book/coloring_and_theming.md +++ b/zh-CN/book/coloring_and_theming.md @@ -1,54 +1,82 @@ # Nu 的配色和主题 -Nushell 界面的许多部分都可以定制它们的颜色,所有这些都可以在`config.nu`配置文件中设置。如果你在配置文件中看到 hash/hashtag/pound 符号 `#`,这意味着它后面的文字被注释掉了。 - -1. 表的边框 -2. 原始值 -3. 形状(这是命令行的语法) -4. 提示 -5. LS_COLORS +Nushell 界面的许多部分都可以定制它们的颜色。所有这些都可以在`config.nu`配置文件中设置。如果你在配置文件中看到 `#` 符号,这意味着它后面的文字被注释掉了。 ## 表格边框 ---- - -表的边框由`config.nu`中的`table_mode`设置来控制。下面是一个例子: +表格边框由 `$env.config.table.mode` 设置控制。它可以在运行时更改,也可以在 `config.nu` 文件中更改: ```nu -$env.config = { - table_mode: rounded -} +$env.config.table.mode = 'rounded' ``` -下面是目前`table_mode`的可能选项: +`$env.config.table.mode` 的选项可以用 `table --list` 列出: + + -- `rounded` # 当然了,这个是最好的 :) +- `ascii_rounded` +- `basic_compact` - `basic` -- `compact` - `compact_double` +- `compact` +- `default` +- `dots` +- `double` +- `heavy` - `light` +- `markdown` +- `none` +- `psql` +- `reinforced` +- `restructured` +- `rounded` +- `single` - `thin` - `with_love` -- `reinforced` -- `heavy` -- `none` -- `other` -### 颜色符号 +示例: + +```nu +$env.config.table.mode = 'rounded' +table --list | first 5 +# => ╭───┬────────────────╮ +# => │ 0 │ basic │ +# => │ 1 │ compact │ +# => │ 2 │ compact_double │ +# => │ 3 │ default │ +# => │ 4 │ heavy │ +# => ╰───┴────────────────╯ + +$env.config.table.mode = 'psql' +table --list | first 5 +# => 0 | basic +# => 1 | compact +# => 2 | compact_double +# => 3 | default +# => 4 | heavy +``` + +## 颜色配置 ---- +颜色配置在 `$env.config.color_config` 中定义。当前的配置可以用以下命令打印: + +```nu +$env.config.color_config | sort +``` + +颜色和样式属性可以用多种替代格式声明。 - `r` - 标准颜色红色的缩写 - `rb` - 标准颜色红色的缩写,带有粗体属性 - `red` - 标准颜色红色 - `red_bold` - 带有粗体属性的标准颜色红色 -- `"#ff0000"` - "#hex" 格式的颜色:前景色为红色(需要引号) -- `{ fg: "#ff0000" bg: "#0000ff" attr: b }` - 完整 "#hex" 格式:前景为红色,背景为蓝色的 "#hex" 格式,属性为粗体缩写。 +- `"#ff0000"` - "#hex" 格式的前景色红色(需要引号) +- `{ fg: "#ff0000" bg: "#0000ff" attr: b }` - "完整 #hex" 格式:前景为红色,背景为蓝色的 "#hex" 格式,属性为粗体缩写。 +- `{|x| 'yellow' }` - 返回带有上述颜色表示之一的字符串的闭包 +- `{|x| { fg: "#ff0000" bg: "#0000ff" attr: b } }` - 返回有效记录的闭包 ### 属性 ---- - | 编码 | 含义 | | ---- | -------- | | l | 闪烁 | @@ -64,150 +92,153 @@ $env.config = { ### "标准颜色"和缩写 -| 代码 | 名称 | -| ------ | ---------------------- | -| g | green | -| gb | green_bold | -| gu | green_underline | -| gi | green_italic | -| gd | green_dimmed | -| gr | green_reverse | -| gbl | green_blink | -| gst | green_strike | -| lg | light_green | -| lgb | light_green_bold | -| lgu | light_green_underline | -| lgi | light_green_italic | -| lgd | light_green_dimmed | -| lgr | light_green_reverse | -| lgbl | light_green_blink | -| lgst | light_green_strike | -| r | red | -| rb | red_bold | -| ru | red_underline | -| ri | red_italic | -| rd | red_dimmed | -| rr | red_reverse | -| rbl | red_blink | -| rst | red_strike | -| lr | light_red | -| lrb | light_red_bold | -| lru | light_red_underline | -| lri | light_red_italic | -| lrd | light_red_dimmed | -| lrr | light_red_reverse | -| lrbl | light_red_blink | -| lrst | light_red_strike | -| u | blue | -| ub | blue_bold | -| uu | blue_underline | -| ui | blue_italic | -| ud | blue_dimmed | -| ur | blue_reverse | -| ubl | blue_blink | -| ust | blue_strike | -| lu | light_blue | -| lub | light_blue_bold | -| luu | light_blue_underline | -| lui | light_blue_italic | -| lud | light_blue_dimmed | -| lur | light_blue_reverse | -| lubl | light_blue_blink | -| lust | light_blue_strike | -| b | black | -| bb | black_bold | -| bu | black_underline | -| bi | black_italic | -| bd | black_dimmed | -| br | black_reverse | -| bbl | black_blink | -| bst | black_strike | -| ligr | light_gray | -| ligrb | light_gray_bold | -| ligru | light_gray_underline | -| ligri | light_gray_italic | -| ligrd | light_gray_dimmed | -| ligrr | light_gray_reverse | -| ligrbl | light_gray_blink | -| ligrst | light_gray_strike | -| y | yellow | -| yb | yellow_bold | -| yu | yellow_underline | -| yi | yellow_italic | -| yd | yellow_dimmed | -| yr | yellow_reverse | -| ybl | yellow_blink | -| yst | yellow_strike | -| ly | light_yellow | -| lyb | light_yellow_bold | -| lyu | light_yellow_underline | -| lyi | light_yellow_italic | -| lyd | light_yellow_dimmed | -| lyr | light_yellow_reverse | -| lybl | light_yellow_blink | -| lyst | light_yellow_strike | -| p | purple | -| pb | purple_bold | -| pu | purple_underline | -| pi | purple_italic | -| pd | purple_dimmed | -| pr | purple_reverse | -| pbl | purple_blink | -| pst | purple_strike | -| lp | light_purple | -| lpb | light_purple_bold | -| lpu | light_purple_underline | -| lpi | light_purple_italic | -| lpd | light_purple_dimmed | -| lpr | light_purple_reverse | -| lpbl | light_purple_blink | -| lpst | light_purple_strike | -| c | cyan | -| cb | cyan_bold | -| cu | cyan_underline | -| ci | cyan_italic | -| cd | cyan_dimmed | -| cr | cyan_reverse | -| cbl | cyan_blink | -| cst | cyan_strike | -| lc | light_cyan | -| lcb | light_cyan_bold | -| lcu | light_cyan_underline | -| lci | light_cyan_italic | -| lcd | light_cyan_dimmed | -| lcr | light_cyan_reverse | -| lcbl | light_cyan_blink | -| lcst | light_cyan_strike | -| w | white | -| wb | white_bold | -| wu | white_underline | -| wi | white_italic | -| wd | white_dimmed | -| wr | white_reverse | -| wbl | white_blink | -| wst | white_strike | -| dgr | dark_gray | -| dgrb | dark_gray_bold | -| dgru | dark_gray_underline | -| dgri | dark_gray_italic | -| dgrd | dark_gray_dimmed | -| dgrr | dark_gray_reverse | -| dgrbl | dark_gray_blink | -| dgrst | dark_gray_strike | +| 代码 | 名称 | +| --------- | ------------------------- | +| `g` | `green` | +| `gb` | `green_bold` | +| `gu` | `green_underline` | +| `gi` | `green_italic` | +| `gd` | `green_dimmed` | +| `gr` | `green_reverse` | +| `bg_g` | `bg_green` | +| `lg` | `light_green` | +| `lgb` | `light_green_bold` | +| `lgu` | `light_green_underline` | +| `lgi` | `light_green_italic` | +| `lgd` | `light_green_dimmed` | +| `lgr` | `light_green_reverse` | +| `bg_lg` | `bg_light_green` | +| `r` | `red` | +| `rb` | `red_bold` | +| `ru` | `red_underline` | +| `ri` | `red_italic` | +| `rd` | `red_dimmed` | +| `rr` | `red_reverse` | +| `bg_r` | `bg_red` | +| `lr` | `light_red` | +| `lrb` | `light_red_bold` | +| `lru` | `light_red_underline` | +| `lri` | `light_red_italic` | +| `lrd` | `light_red_dimmed` | +| `lrr` | `light_red_reverse` | +| `bg_lr` | `bg_light_red` | +| `u` | `blue` | +| `ub` | `blue_bold` | +| `uu` | `blue_underline` | +| `ui` | `blue_italic` | +| `ud` | `blue_dimmed` | +| `ur` | `blue_reverse` | +| `bg_u` | `bg_blue` | +| `lu` | `light_blue` | +| `lub` | `light_blue_bold` | +| `luu` | `light_blue_underline` | +| `lui` | `light_blue_italic` | +| `lud` | `light_blue_dimmed` | +| `lur` | `light_blue_reverse` | +| `bg_lu` | `bg_light_blue` | +| `b` | `black` | +| `bb` | `black_bold` | +| `bu` | `black_underline` | +| `bi` | `black_italic` | +| `bd` | `black_dimmed` | +| `br` | `black_reverse` | +| `bg_b` | `bg_black` | +| `ligr` | `light_gray` | +| `ligrb` | `light_gray_bold` | +| `ligru` | `light_gray_underline` | +| `ligri` | `light_gray_italic` | +| `ligrd` | `light_gray_dimmed` | +| `ligrr` | `light_gray_reverse` | +| `bg_ligr` | `bg_light_gray` | +| `y` | `yellow` | +| `yb` | `yellow_bold` | +| `yu` | `yellow_underline` | +| `yi` | `yellow_italic` | +| `yd` | `yellow_dimmed` | +| `yr` | `yellow_reverse` | +| `bg_y` | `bg_yellow` | +| `ly` | `light_yellow` | +| `lyb` | `light_yellow_bold` | +| `lyu` | `light_yellow_underline` | +| `lyi` | `light_yellow_italic` | +| `lyd` | `light_yellow_dimmed` | +| `lyr` | `light_yellow_reverse` | +| `bg_ly` | `bg_light_yellow` | +| `p` | `purple` | +| `pb` | `purple_bold` | +| `pu` | `purple_underline` | +| `pi` | `purple_italic` | +| `pd` | `purple_dimmed` | +| `pr` | `purple_reverse` | +| `bg_p` | `bg_purple` | +| `lp` | `light_purple` | +| `lpb` | `light_purple_bold` | +| `lpu` | `light_purple_underline` | +| `lpi` | `light_purple_italic` | +| `lpd` | `light_purple_dimmed` | +| `lpr` | `light_purple_reverse` | +| `bg_lp` | `bg_light_purple` | +| `m` | `magenta` | +| `mb` | `magenta_bold` | +| `mu` | `magenta_underline` | +| `mi` | `magenta_italic` | +| `md` | `magenta_dimmed` | +| `mr` | `magenta_reverse` | +| `bg_m` | `bg_magenta` | +| `lm` | `light_magenta` | +| `lmb` | `light_magenta_bold` | +| `lmu` | `light_magenta_underline` | +| `lmi` | `light_magenta_italic` | +| `lmd` | `light_magenta_dimmed` | +| `lmr` | `light_magenta_reverse` | +| `bg_lm` | `bg_light_magenta` | +| `c` | `cyan` | +| `cb` | `cyan_bold` | +| `cu` | `cyan_underline` | +| `ci` | `cyan_italic` | +| `cd` | `cyan_dimmed` | +| `cr` | `cyan_reverse` | +| `bg_c` | `bg_cyan` | +| `lc` | `light_cyan` | +| `lcb` | `light_cyan_bold` | +| `lcu` | `light_cyan_underline` | +| `lci` | `light_cyan_italic` | +| `lcd` | `light_cyan_dimmed` | +| `lcr` | `light_cyan_reverse` | +| `bg_lc` | `bg_light_cyan` | +| `w` | `white` | +| `wb` | `white_bold` | +| `wu` | `white_underline` | +| `wi` | `white_italic` | +| `wd` | `white_dimmed` | +| `wr` | `white_reverse` | +| `bg_w` | `bg_white` | +| `dgr` | `dark_gray` | +| `dgrb` | `dark_gray_bold` | +| `dgru` | `dark_gray_underline` | +| `dgri` | `dark_gray_italic` | +| `dgrd` | `dark_gray_dimmed` | +| `dgrr` | `dark_gray_reverse` | +| `bg_dgr` | `bg_dark_gray` | +| `def` | `default` | +| `defb` | `default_bold` | +| `defu` | `default_underline` | +| `defi` | `default_italic` | +| `defd` | `default_dimmed` | +| `defr` | `default_reverse` | +| `bg_def` | `bg_default` | + + ### `"#hex"` 格式 ---- - "#hex" 格式是你通常看到的一种表示颜色的方式。它由简单的`#`字符以及后面的 6 个字符组成。前两个代表 `红色`,接下来两个代表 `绿色`,最后两个代表 `蓝色`。重要的是,这个字符串必须用引号包围,否则 Nushell 会认为它是一个被注释掉的字符串。 例子:红色的主要颜色是 "#ff0000" 或 "#FF0000"。字母的大写和小写没有区别。 这种 `"#hex"`格式允许我们为 Nushell 的不同部分指定 24 位真彩色调。 -## `完整 "#hex"` 格式 - ---- +### 完整 `"#hex"` 格式 `完整 "#hex"`格式是对 `"#hex"` 格式的一种改进,但允许人们在一行中指定前景色、背景色和属性。 @@ -217,9 +248,33 @@ $env.config = { - 背景色蓝色为 "#hex" 格式 - 属性为加粗的缩写形式 -## 原始值 +### 闭包 ---- +注意:闭包仅对表格输出执行。它们在其他上下文中不起作用,例如对于 `shape_` 配置,当直接打印值时,或作为列表中的值时。 + +例如: + +```nu +$env.config.color_config.filesize = {|x| if $x == 0b { 'dark_gray' } else if $x < 1mb { 'cyan' } else { 'blue' } } +$env.config.color_config.bool = {|x| if $x { 'green' } else { 'light_red' } } +{a:true,b:false,c:0mb,d:0.5mb,e:10mib} +``` + +打印 + +```nu +╭───┬───────────╮ +│ a │ true │ +│ b │ false │ +│ c │ 0 B │ +│ d │ 488.3 KiB │ +│ e │ 10.0 MiB │ +╰───┴───────────╯ +``` + +其中 `true` 是绿色的,`false` 是浅红色的,`0 B` 是深灰色的,`488.3 KiB` 是青色的,`10.0 MiB` 是蓝色的。 + +## 原始值 原始值是像`int`和`string`这样的值。原始值和形状可以用上面看到的各种颜色符号来设置。 @@ -256,7 +311,7 @@ $env.config = { | `vardecl` | | | | `variable` | | | -#### 特殊的 "原始值"(不是真正的原始值,它们的存在仅仅是为了着色) +### 特殊的 "原始值"(不是真正的原始值,它们的存在仅仅是为了着色) | 原始值 | 默认颜色 | 可配置 | | --------------------------- | -------------------------- | ------ | @@ -269,47 +324,39 @@ $env.config = { 下面是一个改变其中一些数值的小例子。 ```nu -let config = { - color_config: { - separator: purple - leading_trailing_space_bg: "#ffffff" - header: gb - date: wd - filesize: c - row_index: cb - bool: red - int: green - duration: blue_bold - range: purple - float: red - string: white - nothing: red - binary: red - cellpath: cyan - hints: dark_gray - } -} +$env.config.color_config.separator = purple +$env.config.color_config.leading_trailing_space_bg = "#ffffff" +$env.config.color_config.header = gb +$env.config.color_config.date = wd +$env.config.color_config.filesize = c +$env.config.color_config.row_index = cb +$env.config.color_config.bool = red +$env.config.color_config.int = green +$env.config.color_config.duration = blue_bold +$env.config.color_config.range = purple +$env.config.color_config.float = red +$env.config.color_config.string = white +$env.config.color_config.nothing = red +$env.config.color_config.binary = red +$env.config.color_config.cellpath = cyan +$env.config.color_config.hints = dark_gray ``` 下面是另一个使用多种颜色语法的小例子,其中有一些注释: ```nu -let config = { - color_config: { - separator: "#88b719" # this sets only the foreground color like PR #486 - leading_trailing_space_bg: white # this sets only the foreground color in the original style - header: { # this is like PR #489 - fg: "#B01455", # note, quotes are required on the values with hex colors - bg: "#ffb900",# note, commas are not required, it could also be all on one line - attr: bli # note, there are no quotes around this value. it works with or without quotes - } - date: "#75507B" - filesize: "#729fcf" - row_index: { - # note that this is another way to set only the foreground, no need to specify bg and attr - fg: "#e50914" - } - } +$env.config.color_config.separator = "#88b719" # this sets only the foreground color like PR #486 +$env.config.color_config.leading_trailing_space_bg = white # this sets only the foreground color in the original style +$env.config.color_config.header = { # this is like PR #489 + fg: "#B01455", # note, quotes are required on the values with hex colors + bg: "#ffb900", # note, commas are not required, it could also be all on one line + attr: bli # note, there are no quotes around this value. it works with or without quotes +} +$env.config.color_config.date = "#75507B" +$env.config.color_config.filesize = "#729fcf" +$env.config.color_config.row_index = { + # note, that this is another way to set only the foreground, no need to specify bg and attr + fg: "#e50914" } ``` @@ -337,6 +384,7 @@ let config = { | `shape_literal` | fg(Color::Blue) | \* | | `shape_nothing` | fg(Color::LightCyan) | \* | | `shape_operator` | fg(Color::Yellow) | \* | +| `shape_pipe` | fg(Color::Purple).bold() | \* | | `shape_range` | fg(Color::Yellow).bold() | \* | | `shape_record` | fg(Color::Cyan).bold() | \* | | `shape_signature` | fg(Color::Green).bold() | \* | @@ -348,13 +396,9 @@ let config = { 这里有一个小例子,说明如何对这些项目应用颜色。任何没有显示指定的都会被设置为默认的颜色。 ```nu -$env.config = { - color_config: { - shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b} - shape_bool: green - shape_int: { fg: "#0000ff" attr: b} - } -} +$env.config.color_config.shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b} +$env.config.color_config.shape_bool: green +$env.config.color_config.shape_int: { fg: "#0000ff" attr: b} ``` ## "提示"的配置和着色 @@ -367,6 +411,7 @@ Nushell 的提示符可以通过这些环境变量进行配置: - `PROMPT_INDICATOR_VI_INSERT` = ": " - `PROMPT_INDICATOR_VI_NORMAL` = "v " - `PROMPT_MULTILINE_INDICATOR` = "::: " +- `render_right_prompt_on_last_line`: 布尔值,用于启用或禁用在提示的最后一行呈现右提示 例如:对于一个简单的提示,我们可以这样做。注意`PROMPT_COMMAND`需要一个`block`而其他的需要一个`string`。 @@ -380,22 +425,76 @@ $env.PROMPT_COMMAND = { $"(date now | format date '%m/%d/%Y %I:%M:%S%.3f'): (pwd $env.PROMPT_INDICATOR = "> " ``` -提示的颜色由 `PROMPT_COMMAND` 中的 `block` 控制,在这里你可以编写自己的自定义提示。我们写了一个稍微花哨点的,有 git 状态的,位于 [nu_scripts 仓库](https://github.com/nushell/nu_scripts/blob/main/prompt/oh-my.nu)。 +如果你正在使用 `starship`,你很可能希望在提示的最后一行显示右提示,就像 zsh 或 fish 一样。你可以修改 `config.nu` 文件,只需将 `render_right_prompt_on_last_line` 设置为 true: -## `ls` 命令的配色:`LS_COLORS` +```nu +$env.config.render_right_prompt_on_last_line = true +``` -Nushell 将尊重并使用 Mac、Linux 和 Windows 上的 `LS_COLORS` 环境变量设置。这个设置允许你在做[`ls`](/commands/docs/ls.md)时定义文件类型的颜色。例如,你可以让目录变成一种颜色,`_.md` markdown 文件一种颜色,`_.toml` 文件变成另一种颜色,等等。有很多方法可以给你的文件类型着色。 +提示的颜色由 `PROMPT_COMMAND` 中的 `block` 控制,在这里你可以编写自己的自定义提示。我们写了一个稍微花哨点的,有 git 状态的,位于 [nu_scripts 仓库](https://github.com/nushell/nu_scripts/blob/main/modules/prompt/oh-my.nu)。 -有一个详尽的清单可以在 [这里](https://github.com/trapd00r/LS_COLORS) 看到,不过它可能太多了,但可以让你初步了解如何创建一个`ls_colors`文件,而`dircolors`可以把它变成`LS_COLORS`环境变量。 +### 瞬态提示符 -[这](https://www.linuxhowto.net/how-to-set-colors-for-ls-command/) 是对`LS_COLORS`的一个相当好的介绍。我相信你可以在网上找到更多相关教程。 +如果你想为以前输入的命令显示不同的提示符,你可以使用 Nushell 的瞬态提示符功能。如果你的提示符有很多对于前几行来说没有必要显示的信息(例如时间和 Git 状态),这会很有用,因为你可以让前几行显示更短的提示符。 -我喜欢`vivid`应用程序,目前在我的`config.nu`中是这样配置的。你可以在 [这里](https://github.com/sharkdp/vivid) 找到`vivid`。 +每个 `PROMPT_*` 变量都有一个相应的 `TRANSIENT_PROMPT_*` 变量,用于在显示过去的提示时更改该段:`TRANSIENT_PROMPT_COMMAND`、`TRANSIENT_PROMPT_COMMAND_RIGHT`、`TRANSIENT_PROMPT_INDICATOR`、`TRANSIENT_PROMPT_INDICATOR_VI_INSERT`、`TRANSIENT_PROMPT_INDICATOR_VI_NORMAL`、`TRANSIENT_PROMPT_MULTILINE_INDICATOR`。默认情况下,`PROMPT_*` 变量用于显示过去的提示。 -`$env.LS_COLORS = (vivid generate molokai | str trim)`。 +例如,如果你想让过去的提示完全不显示左提示,只留下指示符,你可以使用: + +```nu +$env.TRANSIENT_PROMPT_COMMAND = "" +``` + +如果你想回到正常的左提示,你必须取消设置 `TRANSIENT_PROMPT_COMMAND`: + +```nu +hide-env TRANSIENT_PROMPT_COMMAND +``` + +## `LS_COLORS` 命令的配色:`LS_COLORS` + +Nushell 将尊重并使用 Mac、Linux 和 Windows 上的 `LS_COLORS` 环境变量设置。这个设置允许你在做[`ls`](/commands/docs/ls.md)时定义文件类型的颜色。例如,你可以让目录变成一种颜色,`_.md` markdown 文件一种颜色,`_.toml` 文件变成另一种颜色,等等。有很多方法可以给你的文件类型着色。 如果没有设置 `LS_COLORS`,Nushell 将默认使用内置的 `LS_COLORS` 设置,基于 8 位(扩展)ANSI 颜色。 +### 理解 `LS_COLORS` + +`LS_COLORS` 包含一个以冒号(`:`)分隔的记录列表,这些记录将文件类型和文件名映射到样式属性(`selector=attributes`)。 + +选择器可以是一个文件类型,如 `di` 表示“目录标识符”,或 `*.nu` 表示具有 `.nu` 文件扩展名的文件。 + +属性是一个以分号(`;`)分隔的数字列表。请注意,支持哪些属性和属性格式取决于你使用的终端。 + +- 样式属性,如 `0` 正常,`1` 粗体,`3` 斜体,`5` 闪烁,[等](https://en.wikipedia.org/wiki/ANSI_escape_code#Select_Graphic_Rendition_parameters) +- [前景色](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit) `30`-`37` 和 `90`-`97` +- [背景色](https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit) `40`-`47` 和 `100`-`107` +- [RGB 前景](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit) 前缀为 `38;2`,可选地后跟其他属性 +- [RGB 背景](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit) 前缀为 `48;2`,可选地后跟其他属性 + +例如: + +`$env.LS_COLORS = "di=1;34:*.nu=3;33;46"`:粗体目录,斜体黄色前景青色背景 `*.nu` 文件 + +`$env.LS_COLORS = "di=48;2;200;0;0;5"`:红色背景闪烁目录 + +### vivid 主题 + +例如,你可以使用第三方工具 [vivid](https://github.com/sharkdp/vivid),它在多个平台上运行,有[许多已定义的主题](https://github.com/sharkdp/vivid/tree/master/themes),并从中生成 `LS_COLORS` 配置。 + +下载并解压二进制文件后,你可以使用它: + +```nu +$env.LS_COLORS = (vivid generate molokai) +``` + +或使用备用主题: + +```nu +$env.LS_COLORS = (vivid generate ayu) +``` + +你可以将此命令放入你的 [Nushell 配置](/zh-CN/book/configuration.md)中,使其成为默认颜色。 + ## 主题 主题设计结合了上述所有的着色。这里有一个快速的例子,我们把它放在一起,以证明主题定制的能力。这是对我们在网络上看到的 `base16` 主题的一种转换。 @@ -442,68 +541,95 @@ let base16_theme = { cellpath: $base08 hints: dark_gray - # shape_garbage: { fg: $base07 bg: $base08 attr: b} # base16 white on red + # shape_garbage: { fg: $base07 bg: $base08 attr: b } # base16 white on red # but i like the regular white on red for parse errors - shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b} + shape_garbage: { fg: "#FFFFFF" bg: "#FF0000" attr: b } shape_bool: $base0d - shape_int: { fg: $base0e attr: b} - shape_float: { fg: $base0e attr: b} - shape_range: { fg: $base0a attr: b} - shape_internalcall: { fg: $base0c attr: b} + shape_int: { fg: $base0e attr: b } + shape_float: { fg: $base0e attr: b } + shape_range: { fg: $base0a attr: b } + shape_internalcall: { fg: $base0c attr: b } shape_external: $base0c - shape_externalarg: { fg: $base0b attr: b} + shape_externalarg: { fg: $base0b attr: b } shape_literal: $base0d shape_operator: $base0a - shape_signature: { fg: $base0b attr: b} + shape_signature: { fg: $base0b attr: b } shape_string: $base0b shape_filepath: $base0d - shape_globpattern: { fg: $base0d attr: b} + shape_globpattern: { fg: $base0d attr: b } shape_variable: $base0e - shape_flag: { fg: $base0d attr: b} - shape_custom: {attr: b} + shape_flag: { fg: $base0d attr: b } + shape_custom: { attr: b } } # now let's apply our regular config settings but also apply the "color_config:" theme that we specified above. -let config = { - filesize_metric: true - table_mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other - use_ls_colors: true - color_config: $base16_theme # <-- this is the theme - use_grid_icons: true - footer_mode: always #always, never, number_of_rows, auto - animate_prompt: false - float_precision: 2 - use_ansi_coloring: true - filesize_format: "b" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, auto - edit_mode: emacs # vi - max_history_size: 10000 - log_level: error -} +$env.config.animate_prompt: false +$env.config.color_config: $base16_theme # <-- this is the theme +$env.config.edit_mode: emacs # vi +$env.config.filesize_format: "b" # b, kb, kib, mb, mib, gb, gib, tb, tib, pb, pib, eb, eib, auto +$env.config.filesize_metric: true +$env.config.float_precision: 2 +$env.config.footer_mode: always #always, never, number_of_rows, auto +$env.config.log_level: error +$env.config.max_history_size: 10000 +$env.config.table_mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other +$env.config.use_ansi_coloring: true +$env.config.use_grid_icons: true +$env.config.use_ls_colors: true ``` -如果你想在主题设计上火力全开,你需要把我在一开始提到的所有项目作为主题,包括`LS_COLORS`和提示。祝您好运! +如果你想在主题设计上火力全开,你需要把我在一开始提到的所有项目作为主题,包括`LS_COLORS`和提示。祝你好运! -### 在终端上使用浅色背景 +### 在浅色背景终端上工作 -Nushell 的默认配置文件包含一个浅色主题定义,如果你在浅色背景的终端上工作,你可以很容易地应用浅色主题: +Nushell 的[标准库](/zh-CN/book/standard_library.md)包含一个带有默认浅色和深色主题的 `config` 模块。 +如果你在浅色背景的终端上工作,你可以很容易地应用浅色主题。 ```nu -# in $nu.config-file +# in $nu.config-path +use std/config light-theme # add this line to load the theme into scope + $env.config = { - ... - color_config: $dark_theme # 如果你需要浅色主题, 可以将 `$dark_theme` 替换为 `$light_theme` - ... + # ... + color_config: (light_theme) # if you want a light theme, replace `$dark_theme` to `$light_theme` + # ... } ``` -你只需要将 `$dark_theme` 替换为 `$light_theme` 就可以切换到浅色主题了: +你也可以加载深色主题。 ```nu -# in $nu.config-file +# in $nu.config-path +use std/config dark-theme + +$env.config = { + # ... + color_config: (dark_theme) + # ... +} +``` + +## 辅助功能 + +在使用屏幕阅读器时,通常希望有最少的装饰。在这些情况下,可以通过以下选项禁用表格和错误的边框和其他装饰: + +```nu +# in $nu.config-path $env.config = { ... - color_config: $light_theme # if you want a light theme, replace `$dark_theme` to `$light_theme` + table: { + ... + mode: "none" + ... + } + error_style: "plain" ... } ``` + +## 行编辑器菜单(补全、历史、帮助…) + +Reedline(Nu 的行编辑器)样式不使用 `color_config` 键。 +相反,每个菜单都有自己的样式需要单独配置。 +有关此内容的更多信息,请参阅[专门介绍 Reedline 菜单配置的部分](line_editor.md#menus)。 diff --git a/zh-CN/book/coming_from_bash.md b/zh-CN/book/coming_from_bash.md index d88f7e1df33..cde361a4b0d 100644 --- a/zh-CN/book/coming_from_bash.md +++ b/zh-CN/book/coming_from_bash.md @@ -1,60 +1,102 @@ # 从 Bash 到 Nu -如果你是来自 Windows 上的`Git Bash`用户,那么你习惯的外部命令(bash、grep 等)在`nu`中默认是不可用的(除非你在 Windows 路径环境变量中明确包含了它们)。 +::: tip +如果你是来自 Windows 上的`Git Bash`用户,那么你习惯的外部命令(例如 `ln`、`grep`、`vi` 等)在`nu`中默认是不可用的(除非你已经在 Windows 路径环境变量中明确包含了它们)。 要使这些命令在`nu`中可用,请在你的`config.nu`中添加以下一行,用`append`或`prepend`。 ```nu $env.Path = ($env.Path | prepend 'C:\Program Files\Git\usr\bin') ``` +::: -注意:本表针对 Nu 0.60.0 或更高版本。 - -| Bash | Nu | Task | -| ------------------------------------ | -------------------------------------------------- | ------------------------------------------ | -| `ls` | `ls` | 列出当前目录中的文件 | -| `ls ` | `ls ` | 列出给定目录中的文件 | -| `ls pattern*` | `ls pattern*` | 列出匹配给定模式的文件 | -| `ls -la` | `ls --long --all` or `ls -la` | 列出包含所有可用信息的文件,包括隐藏文件 | -| `ls -d */` | `ls \| where type == dir` | 列出目录 | -| `find . -name *.rs` | `ls **/*.rs` | 递归地查找匹配给定模式的所有文件 | -| `find . -name Makefile \| xargs vim` | `ls \*\*/Makefile \| get name \| vim $in` | 将值作为命令参数传递 | -| `cd ` | `cd ` | 切换到给定目录 | -| `cd` | `cd` | 切换到用户主目录 | -| `cd -` | `cd -` | 切换到前一个目录 | -| `mkdir ` | `mkdir ` | 创建给定的路径 | -| `mkdir -p ` | `mkdir ` | 创建给定的路径,如果父目录不存在则自动创建 | -| `touch test.txt` | `touch test.txt` | 新建文件 | -| `> ` | `\| save --raw ` | 保存字符串到给定文件 | -| `>> ` | `\| save --raw --append ` | 追加字符串到给定文件 | -| `cat ` | `open --raw ` | 显示给定文件的内容 | -| | `open ` | 将文件作为结构化数据读取 | -| `mv ` | `mv ` | 移动文件到新的位置 | -| `cp ` | `cp ` | 复制文件到新的位置 | -| `cp -r ` | `cp -r ` | 递归地将目录复制到一个新的位置 | -| `rm ` | `rm ` | 删除给定的文件 | -| | `rm -t ` | 将给定的文件移到系统垃圾箱 | -| `rm -rf ` | `rm -r ` | 递归地删除给定的路径 | -| `date -d ` | `"" \| into datetime -f ` | 解析日期 ([日期格式文档](https://docs.rs/chrono/0.4.15/chrono/format/strftime/index.html)) | -| `sed` | `str replace` | 查找和替换一个字符串中的模式 | -| `grep ` | `where $it =~ ` or `find ` | 过滤包含特定字符串的字符串 | -| `man ` | `help ` | 获得特定命令的帮助信息 | -| | `help commands` | 列出所有可用的命令 | -| | `help --find ` | 在所有可用的命令中搜索 | -| `command1 && command2` | `command1; command2` | 运行一条命令,如果成功的话,再运行第二条 | -| `stat $(which git)` | `stat (which git).path` | 使用命令输出作为其他命令的参数 | -| `echo $PATH` | `echo $env.PATH` | 查看当前路径 | -| `` | `vim $nu.config-path` | 永久地更新 PATH | -| `export PATH = $PATH:/usr/other/bin` | `$env.PATH = ($env.PATH \| append /usr/other/bin)` | 临时更新 PATH | -| `export` | `echo $env` | 列出当前的环境变量 | -| `` | `vim $nu.config-path` | 永久地更新环境变量 | -| `FOO=BAR ./bin` | `FOO=BAR ./bin` | 临时修改环境变量 | -| `export FOO=BAR` | `$env.FOO = BAR` | 为当前会话设置环境变量 | -| `echo $FOO` | `echo $env.FOO` | 使用环境变量 | -| `unset FOO` | `hide FOO` | 取消对当前会话的环境变量设置 | -| `alias s="git status -sb"` | `alias s = git status -sb` | 临时定义一个别名 | -| `type FOO` | `which FOO` | 显示一个命令的信息(内置、别名或可执行) | -| `` | `vim $nu.config-path` | 永久添加和编辑别名(新开Shell会话生效) | -| `bash -c ` | `nu -c ` | 运行一组命令(需要0.9.1或更高版本) | -| `bash - - - - - - - - - - -
CommandDescription
{{ command.title }}{{ command.frontmatter.usage }}
diff --git a/zh-CN/book/configuration.md b/zh-CN/book/configuration.md index bc4916871ec..3ac005a13c5 100644 --- a/zh-CN/book/configuration.md +++ b/zh-CN/book/configuration.md @@ -1,124 +1,686 @@ # 配置 -## Nushell 配置与`env.nu`和`config.nu` +## 快速入门 -Nushell 使用一个配置系统,在启动时加载并运行两个 Nushell 脚本文件: -首先是`env.nu`,然后是`config.nu`。 -这些文件的路径可以通过调用`echo $nu.env-path`和`echo $nu.config-path`找到。 -`env.nu`是用来定义环境变量的,之后这些环境变量定义将在`config.nu`中可用; -`config.nu`可以用来在全局命名空间中添加定义、别名等等。 +虽然 Nushell 提供了许多用于管理其启动和配置的选项,但新用户只需几个简单的步骤即可开始: -_(你可以把 Nushell 的配置加载顺序想象成在启动时执行两行 [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop):`source /path/to/env.nu`和`source /path/to/config.nu`。因此,用`env.nu`表示环境,用`config.nu`表示其他配置只是一个约定。)_ +1. 告诉 Nushell 使用哪个编辑器: -当你在没有设置这些文件的情况下启动 Nushell,Nushell 会提示你下载[`default env.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/default_env.nu)和[`default config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/default_config.nu)。 -你可以通过浏览这些默认文件,了解环境变量的默认值和所有可配置项的列表。 + ```nu + $env.config.buffer_editor = + ``` -### 配置 `$env.config` + 例如: -Nushell 的主要设置是以记录的形式保存在 `config` 环境变量中。这个记录可以用以下方式创建: + ```nu + $env.config.buffer_editor = "code" + # 或 + $env.config.buffer_editor = "nano" + # 或 + $env.config.buffer_editor = "hx" + # 或 + $env.config.buffer_editor = "vi" + # 带参数 + $env.config.buffer_editor = ["emacsclient", "-s", "light", "-t"] + # 等等 + ``` + +2. 使用以下命令编辑 `config.nu`: + + ```nu + config nu + ``` + + 这将在上面定义的编辑器中打开当前的 `config.nu`。 + +3. 将每次 Nushell 启动时应运行的命令添加到此文件中。一个很好的初始示例可能是上面的 `buffer_editor` 设置。 + + 你可以使用以下命令找到可用设置的详细列表: + + ```nu + config nu --doc | nu-highlight | less -R + ``` + +4. 保存、退出编辑器,然后启动一个新的 Nushell 会话以加载这些设置。 + +就是这样!需要时,下面有更多详细信息... + +--- + +[[toc]] + +::: tip +要从 Nushell 内部查看此文档的简化版本,请运行: + +```nu +config nu --doc | nu-highlight | less -R +``` + +::: + +## 配置概述 + +Nushell 使用多个可选的配置文件。这些文件按以下顺序加载: + +1. 加载的第一个文件是 `env.nu`,历史上用于覆盖环境变量。但是,当前的“最佳实践”建议是使用 `config.nu` 和下面的自动加载目录来设置所有环境变量(和其他配置)。 + +2. `config.nu` 通常用于覆盖默认的 Nushell 设置、定义(或导入)自定义命令,或运行任何其他启动任务。 +3. 加载 `$nu.vendor-autoload-dirs` 中的 `*.nu` 文件。这些目录旨在用于供应商和包管理器的启动文件。 +4. 加载 `$nu.user-autoload-dirs` 中的 `*.nu` 文件。这些文件可用于任何启动任务,是模块化配置的好方法。 +5. `login.nu` 运行仅在 Nushell 作为登录 shell 运行时才应发生的命令或处理配置。 + +默认情况下,`env.nu`、`config.nu` 和 `login.nu` 从 `$nu.default-config-dir` 目录中读取。例如: + +```nu +$nu.default-config-dir +# macOS +# => /Users/me/Library/Application Support/nushell +# Linux +# => /home/me/.config/nushell +# Windows +# => C:\Users\me\AppData\Roaming\nushell +``` + +Nushell 首次启动时,将创建配置目录以及一个空的(除了注释)`env.nu` 和 `config.nu`。 + +::: tip +你可以使用 `config nu` 命令在默认文本编辑器中快速打开 `config.nu`。同样,`config env` 命令将打开 `env.nu`。 + +这要求你已使用以下任一方式配置了默认编辑器: + +- Nushell 的 `$env.config.buffer_editor` 设置 +- `$env.VISUAL` 或 `$env.EDITOR` 环境变量 + +例如,将此行放入你的 `config.nu` 中,以便在 Visual Studio Code 中编辑文件: + +```nu +$env.config.buffer_editor = 'code' +``` + +::: + +## `config.nu` 中的常见配置任务: + +::: tip +一些用户可能更喜欢一个“单体”配置文件,其中大部分或所有启动任务都在一个地方。`config.nu` 可用于此目的。 + +其他用户可能更喜欢“模块化”配置,其中每个文件处理一组更小、更集中的任务。自动加载目录中的文件可用于创建此体验。 +::: + +`config.nu` 通常用于: + +- 为 Nushell 和其他应用程序[设置环境变量](#set-environment-variables) +- 在 [`$env.config`](#nushell-settings-in-the-envconfig-record) 中设置 Nushell 设置 +- 加载模块或源文件,以便其命令随时可用 +- 在启动时运行任何其他应用程序或命令 + +## 设置环境变量 + +::: tip 另请参阅 +[环境](./environment.md)章节涵盖了有关如何设置和访问环境变量的其他信息。 +::: + +### 路径配置 + +与大多数 shell 一样,Nushell 搜索名为 `PATH`(或其变体)的环境变量。 + +:::tip +与某些 shell 不同,Nushell 尝试对环境变量“不区分大小写”。`Path`、`path`、`PATH`,甚至 `pAtH` 都是同一环境变量的允许变体。有关详细信息,请参阅[环境 - 大小写敏感性](./environment.md#case-sensitivity)。 +::: + +当 Nushell 启动时,它通常继承 `PATH` 作为一个字符串。但是,Nushell 会自动将其转换为 Nushell 列表以便于访问。这意味着你可以使用例如以下方式*追加*到路径: + +```nu +$env.path ++= ["~/.local/bin"] +``` + +标准库还包括一个辅助命令。默认的 `path add` 行为是*前置*一个目录,使其比路径的其余部分具有更高的优先级。例如,可以将以下内容添加到你的启动配置中: + +```nu +use std/util "path add" +path add "~/.local/bin" +path add ($env.CARGO_HOME | path join "bin") +``` + +::: tip +请注意上面示例中 `path join` 的使用。此命令正确连接两个路径组件,无论路径分隔符是否存在。有关此类别中的更多命令,请参阅 `help path`。 +::: + +### 提示符配置 + +Nushell 提供了许多提示符配置选项。默认情况下,Nushell 包括: + +- 一个包含当前目录的提示符,如果它在主目录下(或其子目录),则使用 `~` 缩写。 +- 一个紧接在提示符右侧的提示指示符。在正常编辑模式下,默认为 `> `,在 Vi 插入模式下为 `: `。请注意字符后的额外空格,以提供命令与提示符之间的分隔。 +- 一个带有日期和时间的右提示符 +- 一个在当前命令行跨越多行时显示的指示符 - 默认为 `::: ` + +控制这些提示符组件的环境变量是: + +- `$env.PROMPT_COMMAND`: 提示符本身 +- `$env.PROMPT_COMMAND_RIGHT`: 可以出现在终端右侧的提示符 +- `$env.PROMPT_INDICATOR`: Emacs 模式指示符 +- `$env.PROMPT_INDICATOR_VI_NORMAL`: Vi-normal 模式指示符 +- `$env.PROMPT_INDICATOR_VI_INSERT`: Vi-insert 模式指示符 +- `$env.PROMPT_MULTILINE_INDICATOR`: 多行指示符 + +这些变量中的每一个都接受以下任一值: + +- 一个字符串,在这种情况下,组件将静态显示为该字符串。 +- 一个闭包(无参数),在这种情况下,组件将根据闭包的代码动态显示。 +- `null`,在这种情况下,组件将恢复为其内部默认值。 + +::: tip +例如,要禁用右提示符,请将以下内容添加到你的启动配置中: + +```nu +$env.PROMPT_COMMAND_RIGHT = "" +# 或 +$env.PROMPT_COMMAND_RIGHT = {||} +``` + +::: + +#### 瞬态提示符 + +Nushell 还支持瞬态提示符,它允许在执行命令行*后*显示不同的提示符。这在几种情况下很有用: + +- 使用多行提示符时,瞬态提示符可以是更精简的版本。 +- 删除瞬态多行指示符和右提示符可以简化从终端的复制。 + +与上面的普通提示符命令一样,每个瞬态提示符都可以接受一个(静态)字符串、一个(动态)闭包或一个 `null` 以使用 Nushell 内部默认值。 + +控制瞬态提示符组件的环境变量是: + +- `$env.TRANSIENT_PROMPT_COMMAND`: 执行命令行后的提示符本身 +- `$env.TRANSIENT_PROMPT_COMMAND_RIGHT`: 可以出现在终端右侧的提示符 +- `$env.TRANSIENT_PROMPT_INDICATOR`: Emacs 模式指示符 +- `$env.TRANSIENT_PROMPT_INDICATOR_VI_NORMAL`: Vi-normal 模式指示符 +- `$env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT`: Vi-insert 模式指示符 +- `$env.TRANSIENT_PROMPT_MULTILINE_INDICATOR`: 多行指示符 + +::: tip +Nushell 将 `TRANSIENT_PROMPT_COMMAND_RIGHT` 和 `TRANSIENT_PROMPT_MULTILINE_INDICATOR` 设置为空字符串 (`""`),以便在输入上一个命令后它们都消失。这简化了从终端的复制和粘贴。 + +要禁用此功能并始终显示这些项目,请设置: + +```nu +$env.TRANSIENT_PROMPT_COMMAND_RIGHT = null +$env.TRANSIENT_PROMPT_MULTILINE_INDICATOR = null +``` + +::: + +### ENV_CONVERSIONS + +某些变量,例如包含多个路径的变量,通常在其他 shell 中存储为以冒号分隔的字符串。Nushell 可以自动将这些转换为更方便的 Nushell 列表。`ENV_CONVERSIONS` 变量指定了环境变量如何: + +- 在 Nushell 启动时从字符串转换为值 (from_string) +- 在运行外部命令时从值转换回字符串 (to_string) + +`ENV_CONVERSIONS` 是一个记录,其中: + +- 每个键是要转换的环境变量 +- 每个值是另一个包含以下内容的记录: + ```nu + { + from_string: + to_string: + } + ``` + +::: tip +如上所述,操作系统路径变量由 Nushell 自动转换。因此,它可以在你的启动配置中作为列表处理,而无需出现在 `ENV_CONVERSIONS` 中。其他以冒号分隔的路径,如 `XDG_DATA_DIRS`,则不会自动转换。 +::: + +要添加其他转换,请使用 [`merge`](/commands/docs/merge.md) 将其合并到 `$env.ENV_CONVERSIONS` 记录中。例如,要为 `XDG_DATA_DIRS` 变量添加转换: + +```nu +$env.ENV_CONVERSIONS = $env.ENV_CONVERSIONS | merge { + "XDG_DATA_DIRS": { + from_string: {|s| $s | split row (char esep) | path expand --no-symlink } + to_string: {|v| $v | path expand --no-symlink | str join (char esep) } + } +} +``` + +### `LS_COLORS` + +与许多类似 `ls` 的实用程序一样,Nushell 的目录列表利用 `LS_COLORS` 环境变量来定义应用于某些文件类型和模式的样式/颜色。 + +## `$env.config` 记录中的 Nushell 设置 + +### 在 `$env.config` 记录中更改设置 + +更改 Nushell 行为的主要机制是 `$env.config` 记录。虽然此记录作为环境变量访问,但与大多数其他变量不同,它: + +- 不会从父进程继承。相反,它由 Nushell 本身用某些默认值填充。 +- 不会导出到由 Nushell 启动的子进程。 + +要检查 `$env.config` 中的当前设置,只需键入变量名: + +```nu +$env.config +``` + +::: tip +由于 Nushell 提供了如此多的自定义选项,最好将其发送到分页器,例如: + +```nu +$env.config | table -e | less -R +# 或者,如果安装了 bat: +$env.config | table -e | bat -p +``` + +::: + +记录每个设置的附录将很快提供。同时,可以使用以下命令在 Nushell 中查看每个设置的缩写文档: + +```nu +config nu --doc | nu-highlight | bat +# 或 +config nu --doc | nu-highlight | less -R +``` + +为避免覆盖现有设置,最好只为所需的配置键分配更新的值,而不是整个 `config` 记录。换句话说: + +::: warning 错误 ```nu $env.config = { - ... + show_banner: false } ``` -你也可以隐藏(shadow)`$env.config`并更新它: +这将重置任何*其他*已更改的设置,因为整个记录将被覆盖。 +::: + +::: tip 正确 + +```nu +$env.config.show_banner = false +``` + +这*仅*更改 `show_banner` 键/值对,使所有其他键保持其现有值。 +::: + +某些键本身也是记录。覆盖这些记录是可以的,但最佳实践是在这样做时设置所有值。例如: ```nu -$env.config = ($env.config | upsert ) +$env.config.history = { + file_format: sqlite + max_size: 1_000_000 + sync_on_enter: true + isolation: true +} +``` + +### 删除欢迎消息 + +:::note +本节直接从横幅消息链接,因此它重复了上面的一些信息。 +::: + +要删除每次 Nushell 启动时显示的欢迎消息: + +1. 键入 `config nu` 来编辑你的配置文件。 +2. 如果你收到有关未定义编辑器的错误: + + ```nu + $env.config.buffer_editor = + # 例如: + $env.config.buffer_editor = "code" + $env.config.buffer_editor = "vi" + # 或带编辑器参数: + $env.config.buffer_editor = ["emacsclient", "-s", "light", "-t"] + ``` + + 然后重复步骤 1。 + +3. 将以下行添加到文件末尾: + + ```nu + $env.config.show_banner = false + ``` + +4. 保存并退出你的编辑器。 +5. 重新启动 Nushell 以测试更改。 + +## 其他启动配置 + +### 更改默认目录 + +::: warning 重要 +如下所述,本节中的变量必须在 Nushell 启动**之前**设置。 +::: + +一些控制 Nushell 启动文件位置的变量必须在 Nushell 加载**之前**设置。这通常由父进程完成,例如: + +- 运行 Nushell 的终端应用程序 + +- 操作系统或窗口管理器。当将 Nushell 作为登录 shell 运行时,这可能是唯一可用的机制。 + + 例如,在 Windows 上,你可以通过控制面板设置环境变量。选择开始菜单并搜索*“环境变量”*。 + + 在使用 PAM 的 Linux 系统上,可以使用 `/etc/environment`(以及其他系统特定的机制)。 + +- 父 shell。例如,在运行 `nu` 之前从 `bash` 导出值。 + +### 启动变量 + +影响 Nushell 文件位置的变量是: + +- `$env.XDG_CONFIG_HOME`: 如果设置了此环境变量,它将用于更改 Nushell 搜索其配置文件(如 `env.nu`、`config.nu`、`login.nu` 和 `/autoload` 目录)的目录。历史记录和插件文件默认也存储在此目录中。 + + Nushell 启动后,此值存储在 `$nu.default-config-dir` 常量中。请参阅下面的[使用常量](#using-constants)。 + +- `$env.XDG_DATA_HOME`: 如果设置了此环境变量,Nushell 会将 `$nu.data-dir` 常量设置为此值。`data-dir` 用于多个启动任务: + + - `($nu.data-dir)/completions` 被添加到 `$env.NU_LIB_DIRS` 搜索路径中。 + - `($nu.data-dir)/vendor/autoload` 作为 `nu.vendor-autoload-dirs` 中的最后一个路径添加。此目录中的文件将在其他供应商自动加载目录之后读取,从而覆盖其任何设置。 + + 请注意,由 `$nu.data-dir` 表示的目录及其任何子目录默认情况下都不会创建。这些目录的创建和使用由用户决定。 + +- `$env.XDG_DATA_DIRS` _(仅限 Unix 平台)_:如果设置了此环境变量,它将用于按列出的顺序填充 `$nu.vendor-auto-load` 目录。列表中的第一个目录首先处理,这意味着最后读取的目录将能够覆盖以前的定义。 + +::: warning +`XDG_*` 变量**不是** Nushell 特定的,不应设置为仅包含 Nushell 文件的目录。相反,应将环境变量设置为包含 `nushell` 目录的*上级*目录。 + +例如,如果你将 `$env.XDG_CONFIG_HOME` 设置为: + +``` +/users/username/dotfiles/nushell ``` -按照约定,这个变量被定义在`config.nu`文件中。 +... Nushell 将在 `/Users/username/dotfiles/nushell/nushell` 中查找配置文件。正确的设置应该是: -### 环境 +``` +/users/username/dotfiles +``` + +另请记住,如果系统已经设置了 `XDG` 变量,那么这些目录中可能已经有正在使用的文件。更改位置可能需要你将其他应用程序的文件移动到新目录。 +::: + +::: tip +你可以使用以下方法在“全新”环境中轻松测试配置更改。以下内容在 Nushell 内部运行,但可以适应其他 shell: + +```nu +# 创建一个空的临时目录 +let temp_home = (mktemp -d) +# 将配置路径设置为此目录 +$env.XDG_CONFIG_HOME = $temp_home +# 将数据目录设置为此目录 +$env.XDG_DATA_HOME = $temp_home +# 删除其他潜在的自动加载目录 +$env.XDG_DATA_HOME = "" +# 在此环境中运行 Nushell +nu + +# 编辑配置 +config nu +# 退出子 shell +exit +# 运行临时配置 +nu +``` -你可以在 Nushell 会话期间使用 `$env. = ` 在 `env.nu` 文件中设置环境变量。比如: +测试完配置后: ```nu -$env.FOO = 'BAR' +# 如果需要,删除临时配置目录 +rm $temp_home ``` -_(尽管 `$env.config` 是一个环境变量,按照惯例它仍然在 `config.nu` 中定义。)_ +**重要提示:** 然后退出父 shell,以免 `XDG` 更改意外传播到其他进程。 +::: -以下是值得关注的且为 Nushell 所特有的几个相对重要的环境变量: +### 使用常量 -- `LS_COLORS`: 在`ls`中为每个文件类型设置颜色 -- `PROMPT_COMMAND`: 为设置提示而执行的代码(块或字符串) -- `PROMPT_COMMAND_RIGHT`: 为设置正确的提示而执行的代码(块) -- `PROMPT_INDICATOR = "〉"`: 提示后打印的提示符(默认为 ">" 类似的 Unicode 符号) -- `PROMPT_INDICATOR_VI_INSERT = ": "` -- `PROMPT_INDICATOR_VI_NORMAL = "〉 "` -- `PROMPT_MULTILINE_INDICATOR = "::: "` +一些重要的命令,如 `source` 和 `use`,有助于定义自定义命令(和其他定义),它们是解析时关键字。除其他外,这意味着所有参数必须在解析时已知。 -### 使用内置命令进行配置 +换句话说, **_解析时关键字不允许使用变量参数_** 。 -从 Nushell 0.64 版本开始新增了两个内置命令(`config nu` 和 `config env`), 通过这两个命令,你可以使用自定义的编辑器或者 IDE 快速地更改 Nushell 配置信息 +但是,Nushell 创建了一些方便的*常量*,可用于帮助识别常见的文件位置。例如,你可以使用以下命令从默认配置目录中加载文件: -Nushell 遵循如下的规则来匹配编辑器: +```nu +source ($nu.default-config-dir | path join "myfile.nu") +``` -1. `$env.config.buffer_editor` -3. `$env.VISUAL` -2. `$env.EDITOR` -4. 如果上面都未匹配成功,则针对 Windows 运行`notepad`, 其他系统`nano` +因为常量值在解析时已知,所以它可以与 `source` 和 `use` 等解析时关键字一起使用。 -### 颜色配置部分 +:::tip 另请参阅 +有关此过程的更多详细信息,请参阅[解析时常量求值](./how_nushell_code_gets_run.md#parse-time-constant-evaluation)。 +::: -你可以在 [相关章节](coloring_and_theming.md) 中了解更多关于设置颜色和主题的信息。 +#### `$nu` 常量 -## 将 Nu 配置为登录 Shell +要查看内置 Nushell 常量的列表,请使用 `$nu`(包括美元符号)检查记录常量。 -要把 Nu 作为一个登录 Shell,你需要配置`$env`变量。这样,在你将使用 Nu 为登录 Shell 时才有足够的支持来运行外部命令。 +#### `NU_LIB_DIRS` 常量 -你可以通过在另一个 Shell(如 Bash)中运行 Nu 来建立完整的环境变量集。一旦你进入 Nu,你可以运行这样的命令: +Nushell 还可以使用 `NU_LIB_DIRS` _常量_,其作用类似于上面提到的 `$env.NU_LIB_DIRS` 变量。但是,与 `$env.NU_LIB_DIRS` 不同,它可以在 `config.nu` 中定义*和*使用。例如: ```nu -$env | reject config | transpose key val | each {|r| echo $"$env.($r.key) = '($r.val)'"} | str join (char nl) +# 定义模块和源搜索路径 +const NU_LIB_DIRS = [ + '~/myscripts' +] +# 从 ~/myscripts 目录加载 myscript.nu +source myscript.nu ``` -这将打印出 `$env. = ` 行,每个环境变量一行包含其设置。您可能不需要所有这些变量,例如,`PS1` 变量是 `bash` 特有的。 +如果同时定义了变量 `$env.NU_LIB_DIRS` 和常量 `NU_LIB_DIRS`,则将搜索两组路径。常量 `NU_LIB_DIRS` 将*首先*被搜索并具有优先权。如果在这些目录中找到匹配名称的文件,搜索将停止。否则,它将继续进入 `$env.NU_LIB_DIRS` 搜索路径。 -接下来,在一些发行版上,你还需要确保 Nu 在`/etc/shells`列表中: +#### `NU_PLUGIN_DIRS` 常量 + +`const NU_PLUGIN_DIRS` 对插件搜索路径的作用方式相同。 + +以下 `NU_PLUGIN_DIRS` 配置将允许从以下位置加载插件: + +- `nu` 可执行文件所在的目录。这通常是发布包中插件所在的位置。 +- `$nu.data-dirs` 中以正在运行的 Nushell 版本命名的目录(例如 `0.100.0`)。 +- `$nu.config-path` 中的 `plugins` 目录。 ```nu -cat /etc/shells -# => # /etc/shells: valid login shells -# => /bin/sh -# => /bin/dash -# => /bin/bash -# => /bin/rbash -# => /usr/bin/screen -# => /usr/bin/fish -# => /home/sophia/.cargo/bin/nu +const NU_PLUGIN_DIRS = [ + ($nu.current-exe | path dirname) + ($nu.data-dir | path join 'plugins' | path join (version).version) + ($nu.config-path | path dirname | path join 'plugins') +] ``` -这样你就可以使用`chsh`命令来将 Nu 设置为你的登录 Shell。在你注销后下次登录时,应该可以看到一个闪亮的 Nu 提示。 +### 颜色、主题和语法高亮 -### 使用 `login.nu` 进行配置 +你可以在[相关章节](coloring_and_theming.md)中了解有关设置颜色和主题的更多信息。 -如果 Nushell 被用作登录 Shell,你可以使用一个特定的配置文件,该文件只在该情况下才会被加载。因此,一个名为 `login.nu` 的文件必须在标准配置目录中。 +### 将 Nu 配置为登录 Shell -文件 `login.nu` 的加载在 `env.nu` 和 `config.nu` 之后,所以你可以覆盖这些配置,如果你需要的话。 +登录 shell 通常负责设置某些将由子 shell 和其他进程继承的环境变量。将 Nushell 设置为用户的默认登录 shell 时,你需要确保 `login.nu` 处理此任务。 -有一个环境变量 `$nu.loginshell-path` 包含该文件的路径。 +许多应用程序会假定 POSIX 或 PowerShell 登录 shell,并提供修改由 POSIX 登录 shell 加载的系统或用户 `profile`(或 PowerShell 系统上的 `.ps1` 文件)的说明。 -### macOS: 保持 `open` 为 `/usr/bin/open` +正如你现在可能已经注意到的,Nushell 不是 POSIX shell,也不是 PowerShell,它无法处理为这些 shell 编写的配置文件。你需要改为在 `login.nu` 中设置这些值。 -一些工具(例如 Emacs)依靠`open`命令来打开 Mac 上的文件。 -由于 Nushell 有自己的[`open`](/commands/docs/open.md)命令,它有不同的语义并隐藏了`/usr/bin/open`,这样某些工具在试图使用它时将出错。 -一个解决这个问题的方法是为 Nushell 的`open`定义一个自定义命令,并在你的`config.nu`文件中为系统的`open`创建一个别名,像这样: +要查找可能需要通过 `login.nu` 设置的环境变量,请通过从你以前的登录 shell 中运行 `nu` 来检查从登录 shell 继承的环境。运行: ```nu -def nuopen [arg, --raw (-r)] { if $raw { open -r $arg } else { open $arg } } -alias open = ^open +$env | reject config | transpose key val | each {|r| echo $"$env.($r.key) = '($r.val)'"} | str join (char nl) ``` -## `PATH` 配置 +查找第三方应用程序可能需要的任何值,并将这些值复制到你的 `login.nu` 中。其中许多值将不需要。例如,`PS1` 设置是 POSIX shell 中的当前提示符,在 Nushell 中将无用。 -要在 [PATH 变量]() 中添加一个路径,你可以在 `env.nu` 中使用 `$env. = ` 和 [`append`](/commands/docs/append.md) 完成,如下: +准备好后,将 Nushell 添加到你的 `/etc/shells` (Unix) 并 `chsh`,如[安装章节](./default_shell.md)中所述。 + +### macOS: 保持 `/usr/bin/open` 为 `open` + +某些工具(如 Emacs)依赖于 [`open`](/commands/docs/open.md) 命令在 Mac 上打开文件。 + +由于 Nushell 有自己的 [`open`](/commands/docs/open.md) 命令,其含义不同,它会遮蔽(覆盖)`/usr/bin/open`,因此这些工具在尝试使用该命令时会产生错误。 + +解决此问题的一种方法是为 Nushell 的 [`open`](/commands/docs/open.md) 定义一个自定义命令,并在你的 `config.nu` 文件中为系统的 [`open`](/commands/docs/open.md) 创建一个别名,如下所示: ```nu -$env.PATH = ($env.PATH | split row (char esep) | append '/some/path') +alias nu-open = open +alias open = ^open ``` -这将把 `/some/path` 追加到 `PATH` 的末尾;你也可以使用 [`prepend`](/commands/docs/prepend.md) 将该路径添加到`PATH`的开头。 +将此行放入你的 `config.nu` 中以使其永久生效。 + +`^` 符号告诉 Nushell 将以下命令作为*外部*命令运行,而不是作为 Nushell 内置命令。运行这些命令后,`nu-open` 将是 Nushell *内部*版本,而 `open` 别名将调用 Mac 的外部 `open`。 + +有关更多信息,请参阅[运行系统(外部)命令](./running_externals.md)。 + +## 详细的配置启动过程 + +本节包含有关如何使用不同的配置(和标志)选项来更改 Nushell 启动行为的更详细描述。 + +### 启动阶段 + +以下阶段及其步骤*可能*在启动期间发生,具体取决于传递给 `nu` 的标志。有关每个标志如何影响该过程,请参阅紧随此表之后的[标志行为](#flag-behavior): + +| 步骤 | 阶段 | Nushell 操作 | +| ---- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 0. | (杂项) | 通过其内部 Rust 实现设置内部默认值。实际上,这可能要到“首次使用”设置或变量时才会发生,但对于控制 Nushell 行为的大多数(但不是所有)设置和变量,通常会有一个 Rust 默认值。然后,这些默认值可以被以下步骤取代。 | +| 1. | (主要) | 从调用进程继承其初始环境。这些最初将转换为 Nushell 字符串,但稍后可以使用 `ENV_CONVERSIONS`(见下文)转换为其他结构。 | +| 2. | (主要) | 获取配置目录。这取决于操作系统(请参阅 [dirs::config_dir](https://docs.rs/dirs/latest/dirs/fn.config_dir.html)),但可以在所有平台上使用 `XDG_CONFIG_HOME` 覆盖,如[上文](#changing-default-directories)所述。 | +| 3. | (主要) | 创建初始的 `$env.NU_LIB_DIRS` 变量。默认情况下,它是一个空列表。 | +| 4. | (主要) | 创建初始的 `$NU_LIB_DIRS` 变量。默认情况下,它包括(1)配置目录下的 `scripts` 目录,以及(2)默认数据目录(`$env.XDG_DATA_HOME` 或 [dirs crate 提供的默认值](https://docs.rs/dirs/latest/dirs/fn.data_dir.html))下的 `nushell/completions`。这些目录默认情况下不会创建。 | +| 5. | (主要) | 创建初始的 `$env.NU_PLUGIN_DIRS` 变量。默认情况下,它是一个空列表。 | +| 6. | (主要) | 创建初始的 `$NU_PLUGIN_DIRS` 变量。默认情况下,这将包括(1)配置目录下的 `plugins` 目录,以及(2)当前运行的 `nu`/`nu.exe` 所在的目录。 | +| 7. | (主要) | 初始化内存中的 SQLite 数据库。这允许在以下配置文件中使用 `stor` 系列命令。 | +| 8. | (主要) | 处理命令行参数,例如 `--plugin-config `、`--plugins ` 等。有关完整列表,请参阅 `nu --help`。 | +| 9. | (主要) | 获取 `env.nu` 和 `config.nu` 的路径。默认情况下,这些位于配置目录中,但可以使用 `--env-config ` 和 `--config ` 标志覆盖其中一个或两个。 | +| 10. | (主要) | 如果使用了 `--include-path (-I)` 标志,它将覆盖上面获得的默认 `$env.NU_LIB_DIRS`。 | +| 11. | (主要) | 从内部默认值加载初始的 `$env.config` 值。 | +| 12. | (主要) | 将搜索路径从继承的 `string` 转换为 Nushell `list`。 | +| 13. | (stdlib) | 将[标准库](./standard_library.md)和 `std-rfc` 加载到虚拟文件系统中。此时它不会被解析或求值。 | +| 14. | (stdlib) | 解析并求值 `std/prelude`,这将 `banner` 和 `pwd` 命令带入作用域。 | +| 15. | (主要) | 生成初始的 [`$nu` 记录常量](#using-constants),以便可以在以下配置文件中使用 `$nu.default-config-dir` 等项。 | +| 16. | (主要) | 加载使用 `--plugin` 标志指定的任何插件。 | +| 17. | (repl) | 设置仅在 REPL 中应用的几个默认环境变量(与提示符相关的和 `SHLVL`)。请注意,使用闭包的与提示符相关的变量在 `default_env.nu` 中设置。 | +| 18. | (配置文件) (插件) | 处理用户的 `plugin.msgpackz`(位于配置目录中)中的签名,以便可以在以下配置文件中使用添加的插件。 | +| 19. | (配置文件) | 如果这是 Nushell 首次启动,则创建配置目录。“首次启动”取决于配置目录是否存在。 | +| 20. | (配置文件) | 同样,如果这是 Nushell 首次启动,则在该目录中创建一个基本上为空的(除了一些注释)`env.nu` 和 `config .nu`。 | +| 21. | (配置文件) (default_env.nu) | 从内部 `default_env.nu` 加载默认环境变量。可以使用以下命令查看此文件:`config env --default \| nu-highlight \| less -R`。 | +| 22. | (配置文件) (env.nu) | 将 `PATH` 变量转换为列表,以便在下一步中可以更轻松地访问它。 | +| 23. | (配置文件) (env.nu) | 加载(解析和求值)用户的 `env.nu`(其路径已在上面确定)。 | +| 24. | (配置文件) (config.nu) | 从内部 `default_config.nu` 加载一个最小的 `$env.config` 记录。可以使用以下命令查看此文件:`config nu --default \| nu-highlight \| less -R`。大多数未在 `default_config.nu` 中定义的值将使用其内部默认值自动填充到 `$env.config` 中。 | +| 25. | (配置文件) (config.nu) | 加载(解析和求值)用户的 `config.nu`(其路径已在上面确定)。 | +| 26. | (配置文件) (login) | 当 Nushell 作为登录 shell 运行时,加载用户的 `login.nu`。 | +| 27. | (配置文件) | 循环遍历供应商自动加载目录并加载找到的任何 `.nu` 文件。目录按 `$nu.vendor-autoload-dirs` 中的顺序处理,这些目录中的文件按字母顺序处理。 | +| 28. | (配置文件) | 循环遍历用户自动加载目录并加载找到的任何 `.nu` 文件。目录按 `$nu.user-autoload-dirs` 中的顺序处理,这些目录中的文件按字母顺序处理。 | +| 29. | (repl) 和 (stdlib) | 如果已配置,则显示横幅。 | +| 29. | (repl) | Nushell 进入正常的命令行(REPL)。 | + +### 标志行为 + +| 模式 | 命令/标志 | 行为 | +| ------------ | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 普通 Shell | `nu` (无标志) | 除标记为 **_(login)_** 的所有启动步骤都会发生。 | +| 登录 Shell | `nu --login/-l` | 所有启动步骤都会发生。 | +| 命令字符串 | `nu --commands ` (或 `-c`) | 除标记为 **_(config files)_** 或 **_(repl)_** 的所有启动阶段都会发生。但是,**_(default_env)_** 和 **_(plugin)_** 会发生。前者允许在那里定义的路径 `ENV_CONVERSIONS` 发生。后者允许在命令字符串中使用插件。 | +| 脚本文件 | `nu ` | 与命令字符串相同。 | +| 无配置 | `nu -n` | 无论其他标志如何,**_(config files)_** 阶段都**不会**发生。 | +| 无标准库 | `nu --no-std-lib` | 无论其他标志如何,标记为 **_(stdlib)_** 的步骤都**不会**发生。 | +| 强制配置文件 | `nu --config ` | 强制上面标记为 **_(config.nu)_** 的步骤使用提供的配置 `` 运行,除非还指定了 `-n` | +| 强制环境文件 | `nu --env-config ` | 强制上面标记为 **_(default_env.nu)_** 和 **_(env.nu)_** 的步骤使用指定的环境 `` 运行,除非还指定了 `-n` | + +### 场景 + +- `nu`: + + - ✅ 使标准库可用 + - ✅ 如果存在于配置目录中,则读取用户的 `plugin.msgpackz` 文件 + - ✅ 在内部加载 `default_env.nu` 文件 + - ✅ 如果存在于配置目录中,则加载用户的 `env.nu` 文件 + - ✅ 在内部加载 `default_config.nu` 文件 + - ✅ 如果存在于配置目录中,则加载用户的 `config.nu` 文件 + - ❌ 不读取个人 `login.nu` 文件 + - ✅ 进入 REPL + +- `nu -c "ls"`: + + - ✅ 使标准库可用 + - ✅ 如果存在于配置目录中,则读取用户的 `plugin.msgpackz` 文件 + - ✅ 在内部加载 `default_env.nu` 文件 + - ❌ 不加载用户的 `env.nu` + - ❌ 不读取内部 `default_config.nu` 文件 + - ❌ 不读取用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 运行 `ls` 命令并退出 + - ❌ 不进入 REPL + +- `nu -l -c "ls"`: + + - ✅ 使标准库可用 + - ✅ 如果存在于配置目录中,则读取用户的 `plugin.msgpackz` 文件 + - ✅ 在内部加载 `default_env.nu` 文件 + - ✅ 如果存在于配置目录中,则加载用户的 `env.nu` 文件 + - ✅ 在内部加载 `default_config.nu` 文件 + - ✅ 如果存在于配置目录中,则加载用户的 `config.nu` 文件 + - ✅ 如果存在于配置目录中,则加载用户的 `login.nu` 文件 + - ✅ 运行 `ls` 命令并退出 + - ❌ 不进入 REPL + +- `nu -l -c "ls" --config foo_config.nu` + + - 与上面相同,但从配置目录读取名为 `foo_config.nu` 的备用配置文件 + +- `nu -n -l -c "ls"`: + + - ✅ 使标准库可用 + - ❌ 不读取用户的 `plugin.msgpackz` + - ❌ 不读取内部 `default_env.nu` + - ❌ 不加载用户的 `env.nu` + - ❌ 不读取内部 `default_config.nu` 文件 + - ❌ 不读取用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 运行 `ls` 命令并退出 + - ❌ 不进入 REPL + +- `nu test.nu`: + + - ✅ 使标准库可用 + - ✅ 如果存在于配置目录中,则读取用户的 `plugin.msgpackz` 文件 + - ✅ 在内部加载 `default_env.nu` 文件 + - ❌ 不加载用户的 `env.nu` + - ❌ 不读取内部 `default_config.nu` 文件 + - ❌ 不读取用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 作为脚本运行 `test.nu` 文件 + - ❌ 不进入 REPL + +- `nu --config foo_config.nu test.nu` + + - ✅ 使标准库可用 + - ✅ 如果存在于配置目录中,则读取用户的 `plugin.msgpackz` 文件 + - ✅ 在内部加载 `default_env.nu` 文件 + - ❌ 不加载用户的 `env.nu`(未指定 `--env-config`) + - ✅ 在内部加载 `default_config.nu` 文件。请注意,`default_config.nu` 总是在用户配置之前处理 + - ✅ 如果存在于配置目录中,则加载用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 作为脚本运行 `test.nu` 文件 + - ❌ 不进入 REPL + +- `nu -n --no-std-lib` (最快的 REPL 启动): + + - ❌ 不使标准库可用 + - ❌ 不读取用户的 `plugin.msgpackz` + - ❌ 不读取内部 `default_env.nu` + - ❌ 不加载用户的 `env.nu` + - ❌ 不读取内部 `default_config.nu` 文件 + - ❌ 不读取用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 进入 REPL + +- `nu -n --no-std-lib -c "ls"` (最快的命令字符串调用): + + - ❌ 不使标准库可用 + - ❌ 不读取用户的 `plugin.msgpackz` + - ❌ 不读取内部 `default_env.nu` + - ❌ 不加载用户的 `env.nu` + - ❌ 不读取内部 `default_config.nu` 文件 + - ❌ 不读取用户的 `config.nu` 文件 + - ❌ 不读取用户的 `login.nu` 文件 + - ✅ 运行 `ls` 命令并退出 + - ❌ 不进入 REPL diff --git a/zh-CN/book/control_flow.md b/zh-CN/book/control_flow.md new file mode 100644 index 00000000000..0bde9c63bc8 --- /dev/null +++ b/zh-CN/book/control_flow.md @@ -0,0 +1,379 @@ +# 控制流 + +Nushell 提供了几个命令来帮助确定不同代码组的执行方式。在编程语言中,这种功能通常被称为*控制流*。 + +::: tip +需要注意的一点是,本页讨论的所有命令都使用[代码块](/zh-CN/book/types_of_data.html#blocks)。这意味着你可以在其中改变[环境变量](/zh-CN/book/environment.html)和其他[可变变量](/zh-CN/book/variables.html#mutable-variables)。 +::: + +## 已涵盖内容 + +下面我们介绍一些与控制流相关的命令,但在开始之前,值得注意的是,在其他章节中已经介绍了一些与控制流相关或可以在相同情况下使用的功能和概念。这些包括: + +- [管道](/zh-CN/book/pipelines.html)页面上的管道。 +- [数据类型](/zh-CN/book/types_of_data.html)页面上的闭包。 +- [使用列表](/zh-CN/book/working_with_lists.html)页面上的迭代命令。例如: + - [`each`](/commands/docs/each.html) + - [`where`](/commands/docs/where.html) + - [`reduce`](/commands/docs/reduce.html) + +## 选择(条件) + +以下命令根据给定的某些条件执行代码。 + +::: tip +选择/条件命令是表达式,因此它们返回值,与本页上的其他命令不同。这意味着以下代码是可行的。 + +```nu +'foo' | if $in == 'foo' { 1 } else { 0 } | $in + 2 +# => 3 +``` + +::: + +### `if` + +[`if`](/commands/docs/if.html) 根据一个或多个条件的结果来评估分支[代码块](/zh-CN/book/types_of_data.html#blocks),类似于其他编程语言中的 "if" 功能。例如: + +```nu +if $x > 0 { 'positive' } +``` + +当条件为 `true`(`$x` 大于零)时返回 `'positive'`,当条件为 `false`(`$x` 小于或等于零)时返回 `null`。 + +我们可以在第一个代码块之后为 `if` 添加一个 `else` 分支,当条件为 `false` 时,它会执行并返回 `else` 代码块的结果值。例如: + +```nu +if $x > 0 { 'positive' } else { 'non-positive' } +``` + +这次,当条件为 `true`(`$x` 大于零)时返回 `'positive'`,当条件为 `false`(`$x` 小于或等于零)时返回 `'non-positive'`。 + +我们还可以像下面这样将多个 `if` 链接在一起: + +```nu +if $x > 0 { 'positive' } else if $x == 0 { 'zero' } else { "negative" } +``` + +当第一个条件为 `true`(`$x` 大于零)时,它将返回 `'positive'`;当第一个条件为 `false` 且下一个条件为 `true`(`$x` 等于零)时,它将返回 `'zero'`;否则,它将返回 `'negative'`(当 `$x` 小于零时)。 + +### `match` + +[`match`](/commands/docs/match.html) 根据给定的值执行多个条件分支之一。你还可以进行一些[模式匹配](/zh-CN/cookbook/pattern_matching.html)来解包列表和记录等复合类型中的值。 + +[`match`](/commands/docs/match.html) 的基本用法可以像其他语言中常见的 "switch" 语句一样有条件地运行不同的代码。[`match`](/commands/docs/match.html) 检查 `match` 关键字后面的值是否等于每个分支开头 `=>` 前面的值,如果相等,则执行该分支 `=>` 后面的代码。 + +```nu +match 3 { + 1 => 'one', + 2 => { + let w = 'w' + 't' + $w + 'o' + }, + 3 => 'three', + 4 => 'four' +} +# => three +``` + +分支可以返回单个值,或者如第二个分支所示,可以返回[代码块](/zh-CN/book/types_of_data.html#blocks)的结果。 + +#### 捕获所有分支 + +你还可以有一个捕获所有条件的分支,用于当给定值不匹配任何其他条件时,方法是让一个分支的匹配值为 `_`。 + +```nu +let foo = match 7 { + 1 => 'one', + 2 => 'two', + 3 => 'three', + _ => 'other number' +} +$foo +# => other number +``` + +(提醒一下,[`match`](/commands/docs/match.html) 是一个表达式,这就是为什么我们可以在这里将结果赋给 `$foo`)。 + +#### 模式匹配 + +你可以使用[模式匹配](/zh-CN/cookbook/pattern_matching.html)从列表和记录等类型中“解包”值。然后,你可以将变量赋给你想要解包的部分,并在匹配的表达式中使用它们。 + +```nu +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` 和条件。 + +```nu +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 +``` + +--- + +你可以在[模式匹配手册页面](https://www.nushell.sh/zh-CN/cookbook/pattern_matching.html)中找到有关 [`match`](/commands/docs/match.html) 的更多详细信息。 + +## 循环 + +循环命令允许你多次重复一个代码块。 + +### 循环和其他迭代命令 + +循环命令的功能类似于将闭包应用于列表或表中的元素的命令,例如 [`each`](/commands/docs/each.html) 或 [`where`](/commands/docs/where.html),很多时候你可以用任何一种方法完成同样的事情。例如: + +```nu +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 风格](/zh-CN/book/thinking_in_nu.html)(惯用)的。原因在于使用循环有一个相当大的缺点。 + +#### 循环的缺点 + +循环最大的缺点是它们是语句,而 [`each`](/commands/docs/each.html) 是表达式。像 [`each`](/commands/docs/each.html) 这样的表达式总会产生某个输出值,而语句则不会。 + +这意味着它们不能很好地与不可变变量一起工作,而使用不可变变量被认为是更符合 [Nushell 风格](/zh-CN/book/thinking_in_nu.html#variables-are-immutable)的。在上一节的示例中,如果没有预先声明的可变变量,就不可能使用 [`for`](/commands/docs/each.html) 来获取递增数字的列表,或任何值。 + +语句在需要某些输出的 Nushell 管道中也无法工作。事实上,如果你尝试这样做,Nushell 会报错: + +```nu +[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`](/commands/docs/each.html) 这样的表达式命令通常比循环语句更自然。 + +#### 循环的优点 + +如果循环有这么大的缺点,为什么它们还存在呢?一个原因是,像 [`each`](/commands/docs/each.html) 使用的闭包不能修改周围环境中的可变变量。如果你尝试在闭包中修改可变变量,你会得到一个错误: + +```nu +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 +# => ╰──── +``` + +如果你在闭包中修改环境变量,你可以,但它只会在闭包的作用域内修改它,在其他地方保持不变。然而,循环使用[代码块](/zh-CN/book/types_of_data.html#blocks),这意味着它们可以在更大的作用域内修改常规的可变变量或环境变量。 + +```nu +mut result = [] +for $it in [1 2 3] { $result = ($result | append ($it + 1)) } +$result +# => ╭───┬───╮ +# => │ 0 │ 2 │ +# => │ 1 │ 3 │ +# => │ 2 │ 4 │ +# => ╰───┴───╯ +``` + +### `for` + +[`for`](/commands/docs/for.html) 循环遍历一个范围或集合,如列表或表。 + +```nu +for x in [1 2 3] { $x * $x | print } +# => 1 +# => 4 +# => 9 +``` + +#### 表达式命令替代方案 + +- [`each`](/commands/docs/each.html) +- [`par-each`](/commands/docs/par-each.html) +- [`where`](/commands/docs/where.html)/[`filter`](/commands/docs/filter.html) +- [`reduce`](/commands/docs/reduce.html) + +### `while` + +[`while`](/commands/docs/while.html) 循环执行同一个代码块,直到给定的条件为 `false`。 + +```nu +mut x = 0; while $x < 10 { $x = $x + 1 }; $x +# => 10 +``` + +#### 表达式命令替代方案 + +"until" 和其他 "while" 命令 + +- [`take until`](/commands/docs/take_until.html) +- [`take while`](/commands/docs/take_while.html) +- [`skip until`](/commands/docs/skip_until.html) +- [`skip while`](/commands/docs/skip_while.html) + +### `loop` + +[`loop`](/commands/docs/loop.html) 无限循环一个代码块。你可以使用 [`break`](/commands/docs/break.html)(如下一节所述)来限制它循环的次数。它对于连续运行的脚本也很有用,比如交互式提示符。 + +```nu +mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x +# => 11 +``` + +### `break` + +[`break`](/commands/docs/break.html) 将停止执行循环中的代码,并恢复在循环之后执行。有效地“跳出”循环。 + +```nu +for x in 1..10 { if $x > 3 { break }; print $x } +# => 1 +# => 2 +# => 3 +``` + +### `continue` + +[`continue`](/commands/docs/continue.html) 将停止执行当前循环,跳过循环中剩余的代码,并进入下一个循环。如果循环通常会结束,比如 [`for`](/commands/docs/for.html) 已经遍历了所有给定的元素,或者 [`while`](/commands/docs/while.html) 的条件现在为 false,它将不会再次循环,执行将在循环块之后继续。 + +```nu +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`](/commands/docs/error_make.html) 创建一个错误,停止代码和任何调用它的代码的执行,直到它被 [`try`](/commands/docs/try.html) 块处理,或者它结束脚本并输出错误消息。此功能与其他语言中的“异常”相同。 + +```nu +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 +# => ╰──── +``` + +传递给它的记录为捕获它的代码或最终的错误消息提供了一些信息。 + +你可以在[创建自己的错误页面](/zh-CN/book/creating_errors.html)上找到有关 [`error make`](/commands/docs/error_make.html) 和错误概念的更多信息。 + +### `try` + +[`try`](/commands/docs/try.html) 将捕获在 [`try`](/commands/docs/try.html) 的代码块中任何地方创建的错误,并在块之后恢复代码的执行。 + +```nu +try { error make { msg: 'Some error info' }}; print 'Resuming' +# => Resuming +``` + +这包括捕获内置错误。 + +```nu +try { 1 / 0 }; print 'Resuming' +# => Resuming +``` + +如果发生错误,结果值将是 `nothing`,如果没有发生错误,则为块的返回值。 + +如果在 [`try`](/commands/docs/try.html) 块之后包含一个 `catch` 块,如果 [`try`](/commands/docs/try.html) 块中发生错误,它将执行 `catch` 块中的代码。 + +```nu +try { 1 / 0 } catch { 'An error happened!' } | $in ++ ' And now I am resuming.' +# => An error happened! And now I am resuming. +``` + +如果没有发生错误,它将不会执行 `catch` 块。 + +`try` 也适用于外部命令: + +```nu +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`](/commands/docs/return.html) 提前结束一个闭包或命令,而不运行命令/闭包的其余部分,并返回给定的值。通常不是必需的,因为闭包或命令中的最后一个值也会被返回,但有时它可能很方便。 + +```nu +def 'positive-check' [it] { + if $it > 0 { + return 'positive' + }; + + 'non-positive' +} +``` + +```nu +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 +``` diff --git a/zh-CN/book/creating_errors.md b/zh-CN/book/creating_errors.md index 1843da5660a..94475b027c7 100644 --- a/zh-CN/book/creating_errors.md +++ b/zh-CN/book/creating_errors.md @@ -16,7 +16,7 @@ let span = (metadata $x).span; 接下来你可以通过 `error make` 命令来创建一个错误,该命令需要一个可以描述待创建错误的记录作为输入: ```nu -error make {msg: "this is fishy", label: {text: "fish right here", start: $span.start, end: $span.end } } +error make {msg: "this is fishy", label: {text: "fish right here", span: $span } } ``` 与你的自定义命令放在一起后,它可能看起来像这样: @@ -28,8 +28,7 @@ def my-command [x] { msg: "this is fishy", label: { text: "fish right here", - start: $span.start, - end: $span.end + span: $span } } } diff --git a/zh-CN/book/custom_commands.md b/zh-CN/book/custom_commands.md index d0bd2259da1..a916323cdf3 100644 --- a/zh-CN/book/custom_commands.md +++ b/zh-CN/book/custom_commands.md @@ -1,441 +1,974 @@ # 自定义命令 -Nu 具备组合长管道的能力使你对数据和系统有很强的控制力,但它的代价是需要大量的键盘输入。不过理想情况下,你可以保存精心设计的管道以便反复使用。 +与任何编程语言一样,你很快就会想要保存较长的管道和表达式,以便在需要时可以轻松地再次调用它们。 -这就是自定义命令(Custom Commands)的作用。 +这就是自定义命令的作用。 -下面看一个自定义命令的例子: +::: tip 注意 +自定义命令与许多语言中的函数类似,但在 Nushell 中,自定义命令*本身就是一等公民*。正如你将在下面看到的,它们与内置命令一起包含在帮助系统中,可以成为管道的一部分,会实时进行类型错误解析等等。 +::: + +[[toc]] + +## 创建和运行自定义命令 + +让我们从一个简单的 `greet` 自定义命令开始: ```nu def greet [name] { - echo "hello" $name + $"Hello, ($name)!" } ``` -在这个定义中,我们定义了`greet`命令,它需要一个参数`name`。在这个参数后面是自定义命令运行时将执行的代码块。当被调用时,自定义命令将把传递给`name`的值设置为`$name`变量,该变量在块内是可用的。 +在这里,我们定义了 `greet` 命令,它接受一个参数 `name`。此参数后面是表示自定义命令运行时将发生什么的块。调用时,自定义命令会将为 `name` 传递的值设置为 `$name` 变量,该变量将在块内可用。 -要运行上述命令,我们可以像调用内置命令一样调用它: +要运行此命令,我们可以像调用内置命令一样调用它: ```nu -greet "world" +greet "World" +# => Hello, World! ``` -当我们这样做的时候,就会得到输出,如同我们使用内置命令一样: +## 从命令返回值 -``` -───┬─────── - 0 │ hello - 1 │ world -───┴─────── +你可能会注意到上面的示例中没有 `return` 或 `echo` 语句。 + +像 PowerShell 和 JavaScript(使用箭头函数)等其他一些语言一样,Nushell 具有*隐式返回*的特性,即命令中最后一个表达式的值成为其返回值。 + +在上面的示例中,只有一个表达式——字符串。此字符串成为命令的返回值。 + +```nu +greet "World" | describe +# => string ``` -::: tip -`echo`将其参数分别返回给管道。如果你想用它来生成一个单一的字符串,请在管道中添加` | str join`: +当然,一个典型的命令将由多个表达式组成。为了演示,这里有一个包含 3 个表达式的无意义命令: ```nu -def greet [name] { - echo "hello " $name | str join +def eight [] { + 1 + 1 + 2 + 2 + 4 + 4 } -greet nushell +eight +# => 8 ``` -返回 `hello nushell` -::: +返回值同样只是命令中*最后一个*表达式的结果,即 `4 + 4` (8)。 + +其他示例: -## 命令名称 +::: details 提前返回 +由于某种情况需要提前退出的命令仍然可以使用 [`return` 语句](/commands/docs/return.md)返回值。 + +```nu +def process-list [] { + let input_length = length + if $input_length > 10_000 { + print "Input list is too long" + return null + } -在 Nushell 中,命令名是一串字符或一个带引号的字符串。下面是一些有效命令名的例子:`greet`, `get-size`, `mycommand123`, `"mycommand"`, `😊`, 和`123`。 + $in | each {|i| + # Process the list + $i * 4.25 + } +} +``` -_注意:在 Nushell 中,通常的做法是用`-`来分隔命令的多个单词,以提高可读性。_ 例如,使用 `get-size` 而不是 `getsize` 或者 `get_size`。 +::: -## 子命令 +::: details 抑制返回值 +你通常会希望创建一个作为*语句*而不是表达式的自定义命令,并且不返回值。 -你也可以使用空格来定义命令的子命令(Subcommand)。例如,如果我们想给`str`添加一个新的子命令,可以通过命名我们的子命令以 "str" 开头来做到。比如: +在这种情况下,你可以使用 `ignore` 关键字: ```nu -def "str mycommand" [] { - echo hello +def create-three-files [] { + [ file1 file2 file3 ] | each {|filename| + touch $filename + } | ignore } ``` -现在我们可以像调用`str`的内置子命令一样调用我们的自定义命令: +如果没有管道末尾的 `ignore`,该命令将从 `each` 语句返回一个空列表。 + +你也可以返回一个 `null` 作为最后一个表达式。或者,在这个人为的例子中,使用一个不返回值的 `for` 语句(见下一个例子)。 +::: + +::: details 不返回值的语句 +Nushell 中的一些关键字是*语句*,不返回值。如果你使用这些语句之一作为自定义命令的最后一个表达式,则*返回值*将为 `null`。在某些情况下,这可能是意料之外的。例如: ```nu -str mycommand +def exponents-of-three [] { + for x in [ 0 1 2 3 4 5 ] { + 3 ** $x + } +} +exponents-of-three ``` -## 参数类型 +上面的命令将不显示任何内容,返回值为 `null`,因为 `for` 是一个不返回值的*语句*。 + +要从输入列表中返回值,请使用诸如 `each` 命令之类的过滤器: + +````nu +def exponents-of-three [] { + [ 0 1 2 3 4 5 ] | each {|x| + 3 ** $x + } +} + +exponents-of-three -在定义自定义命令时,你可以为每个参数命名并选择性地设置其类型。例如,你可以把上面的内容写成: +# => ╭───┬─────╮ +# => │ 0 │ 1 │ +# => │ 1 │ 3 │ +# => │ 2 │ 9 │ +# => │ 3 │ 27 │ +# => │ 4 │ 81 │ +# => │ 5 │ 243 │ +# => ╰───┴─────╯ +::: +::: details Match 表达式 ```nu -def greet [name: string] { - echo "hello " $name | str join +# 返回当前目录中的一个随机文件 +def "random file" [] { + let files = (ls) + let num_files = ($files | length) + + match $num_files { + 0 => null # 对于空目录返回 null + _ => { + let random_file = (random int 0..($num_files - 1)) + ($files | get $random_file) + } + } } +```` + +在这种情况下,最后一个表达式是 `match` 语句,它可以返回: + +- 如果目录为空,则返回 `null` +- 否则,返回一个表示随机选择的文件的 `record` + +::: + +## 自定义命令和管道 + +与内置命令一样,自定义命令的返回值可以传递到管道中的下一个命令。自定义命令也可以接受管道输入。此外,只要有可能,管道输入和输出都会在可用时进行流式处理。 + +::: tip 重要! +另请参阅:[管道](./pipelines.html) +::: + +### 管道输出 + +```nu +ls | get name ``` -参数的类型是可选的。Nushell 支持不添加类型,此时会把参数类型当作`any`。如果你在参数上标注了一个类型,Nushell 将在你调用函数的时候检查该类型。 +让我们将 [`ls`](/commands/docs/ls.md) 移动到我们编写的命令中: + +```nu +def my-ls [] { ls } +``` -例如,假设你需要输入一个`int`类型: +我们可以像使用 [`ls`](/commands/docs/ls.md) 一样使用此命令的输出。 ```nu -def greet [name: int] { - echo "hello " $name | str join +my-ls | get name +# => ╭───┬───────────────────────╮ +# => │ 0 │ myscript.nu │ +# => │ 1 │ myscript2.nu │ +# => │ 2 │ welcome_to_nushell.md │ +# => ╰───┴───────────────────────╯ +``` + +这使我们可以轻松地构建自定义命令并处理其输出。请记住,我们不像其他语言那样使用 return 语句。相反,[隐式返回](#returning-values-from-a-command)允许我们构建输出数据流的管道,这些数据流可以连接到其他管道。 + +::: tip 注意 +在这种情况下,`ls` 内容仍然是流式的,即使它在一个单独的命令中。在慢速(例如,网络)文件系统上对长目录运行此命令将会在行可用时返回它们。 +::: + +### 管道输入 + +自定义命令也可以从管道中获取输入,就像其他命令一样。此输入会自动传递给自定义命令的块。 + +让我们创建自己的命令,将接收到的每个值都加倍: + +```nu +def double [] { + each { |num| 2 * $num } } +``` -greet world +现在,如果我们在管道中稍后调用上述命令,我们可以看到它对输入的作用: + +```nu +[1 2 3] | double +# => ╭───┬───╮ +# => │ 0 │ 2 │ +# => │ 1 │ 4 │ +# => │ 2 │ 6 │ +# => ╰───┴───╯ ``` -如果我们尝试运行上述内容,Nushell 会告诉我们,类型不匹配: +::: tip 酷! +此命令演示了输入和输出*流式处理*。尝试使用无限输入运行它: +```nu +1.. | each {||} | double ``` -error: Type Error - ┌─ shell:6:7 - │ -5 │ greet world - │ ^^^^^ Expected int, found world + +即使输入命令尚未结束,`double` 命令仍然可以在值可用时接收和输出它们。 + +按 Ctrl+C 停止命令。 +::: + +我们还可以使用 [`$in` 变量](pipelines.html#pipeline-input-and-the-special-in-variable)存储输入以供以后使用: + +```nu +def nullify [...cols] { + let start = $in + $cols | reduce --fold $start { |col, table| + $table | upsert $col null + } +} + +ls | nullify name size +# => ╭───┬──────┬──────┬──────┬───────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├───┼──────┼──────┼──────┼───────────────┤ +# => │ 0 │ │ file │ │ 8 minutes ago │ +# => │ 1 │ │ file │ │ 8 minutes ago │ +# => │ 2 │ │ file │ │ 8 minutes ago │ +# => ╰───┴──────┴──────┴──────┴───────────────╯ ``` -这可以帮助指导你的用户只使用支持的类型来调用你所定义的命令。 +## 命名命令 -目前可以支持的类型是(从 0.65.0 版本开始): +在 Nushell 中,命令名可以是一串字符。以下是一些有效命令名的示例:`greet`、`get-size`、`mycommand123`、`my command`、`命令`,甚至 `😊`。 -- `any` -- `block` -- `cell-path` -- `duration` -- `path` -- `expr` -- `filesize` -- `glob` -- `int` -- `math` -- `number` -- `operator` -- `range` -- `cond` -- `bool` -- `signature` -- `string` -- `variable` -- `record` -- `list` -- `table` -- `error` +应避免可能与其他解析器模式混淆的字符串。例如,以下命令名可能无法调用: + +- `1`、`"1"` 或 `"1.5"`:Nushell 不允许使用数字作为命令名 +- `4MiB` 或 `"4MiB"`:Nushell 不允许使用文件大小作为命令名 +- `"number#four"` 或 `"number^four"`:命令名中不允许使用插入符号和哈希符号 +- `-a`、`"{foo}"`、`"(bar)"`:将无法调用,因为 Nushell 会将它们解释为标志、闭包或表达式。 + +虽然像 `"+foo"` 这样的名称可能有效,但最好避免使用,因为解析器规则可能会随着时间的推移而改变。如有疑问,请使命令名尽可能简单。 + +::: tip +在 Nushell 中,通常的做法是用 `-` 分隔命令的多个单词以提高可读性。例如,使用 `get-size` 而不是 `getsize` 或 `get_size`。 +::: + +::: tip +因为 `def` 是一个解析器关键字,所以命令名必须在解析时已知。这意味着命令名不能是变量或常量。例如,以下是*不允许*的: + +```nu +let name = "foo" +def $name [] { foo } +``` -## 具有默认值的参数 +::: -若要使一个参数成为可选的,并具有默认值,你可以在命令定义中指定该默认值: +### 子命令 + +你还可以使用空格定义命令的子命令。例如,如果我们想向 [`str`](/commands/docs/str.md) 添加一个新子命令,我们可以通过以“str ”开头命名我们的子命令来创建它。例如: ```nu -def greet [name = "nushell"] { - echo "hello " $name | str join +def "str mycommand" [] { + "hello" } ``` -你可以在没有参数的情况下调用这个命令,也可以指定一个值来覆盖默认值: +现在我们可以像调用 [`str`](/commands/docs/str.md) 的内置子命令一样调用我们的自定义命令: ```nu -greet -# => hello nushell -greet world -# => hello world +str mycommand ``` -你也可以将默认值与[类型要求](#参数类型)相结合: +当然,名称中带空格的命令也以同样的方式定义: ```nu -def congratulate [age: int = 18] { - echo "Happy birthday! Wow you are " $age " years old now!" | str join +def "custom command" [] { + "This is a custom command with a space in the name!" } ``` -如果你想检查一个可选参数是否存在,而不是仅仅依赖一个默认值,请使用[可选位置参数](#可选位置参数)代替。 +## 参数 + +### 多个参数 -## 可选位置参数 +在 `def` 命令中,参数在 [`list`](./types_of_data.md#lists) 中定义。这意味着多个参数可以用空格、逗号或换行符分隔。 -默认情况下,位置参数(Positional Parameters)是必须的。如果没有传递位置参数,我们将遇到一个报错: +例如,这里是一个接受两个名称的 `greet` 版本。这三个定义中的任何一个都可以工作: +```nu +# 空格 +def greet [name1 name2] { + $"Hello, ($name1) and ($name2)!" +} + +# 逗号 +def greet [name1, name2] { + $"Hello, ($name1) and ($name2)!" +} + +# 换行符 +def greet [ + name1 + name2 +] { + $"Hello, ($name1) and ($name2)!" +} ``` - × Missing required positional argument. - ╭─[entry #23:1:1] - 1 │ greet - · ▲ - · ╰── missing name - ╰──── - help: Usage: greet + +### 必需的位置参数 + +上面使用的基本参数定义是*位置*的。传递给上面 `greet` 命令的第一个参数被分配给 `name1` 参数(并且,如上所述,是 `$name1` 变量)。第二个参数成为 `name2` 参数和 `$name2` 变量。 + +默认情况下,位置参数是*必需*的。使用我们之前定义的带有两个必需位置参数的 `greet`: + +```nu +def greet [name1, name2] { + $"Hello, ($name1) and ($name2)!" +} + +greet Wei Mei +# => Hello, Wei and Mei! + +greet Wei +# => Error: nu::parser::missing_positional +# => +# => × Missing required positional argument. +# => ╭─[entry #1:1:10] +# => 1 │ greet Wei +# => ╰──── +# => help: Usage: greet . Use `--help` for more information. ``` -我们可以在一个位置参数的名字后面加上一个问号(`?`),将其标记为可选参数。比如: +::: tip +尝试在此版本的 `greet` 之后键入第三个名称。请注意,解析器会自动检测到错误,并在执行前将第三个参数突出显示为错误。 +::: + +### 可选的位置参数 + +我们可以通过在位置参数的名称后加上问号 (`?`) 来将其定义为可选。例如: ```nu def greet [name?: string] { - echo "hello" $name | str join + $"Hello, ($name | default 'You')" } greet +# => Hello, You ``` -使一个位置参数成为可选参数并不改变它在命令体中被访问的名称。如上例所示,尽管参数列表中有`?`的后缀,但它仍然以`$name`的形式被访问。 +::: tip +请注意,用于访问变量的名称不包括 `?`;只有其在命令签名中的定义才包括。 +::: -当一个可选参数没有被传递,它在命令体中的值等于`null`和`$nothing`。我们可以利用这一点来对没有传递参数的情况进行处理: +当未传递可选参数时,其在命令主体中的值为 `null`。上面的示例使用 `default` 命令在 `name` 为 `null` 时提供默认值“You”。 + +你也可以直接比较该值: ```nu def greet [name?: string] { - if ($name == null) { - echo "hello, I don't know your name!" - } else { - echo "hello " $name | str join + match $name { + null => "Hello! I don't know your name!" + _ => $"Hello, ($name)!" } } greet +# => Hello! I don't know your name! ``` -如果你只是想在参数缺失时设置一个默认值,那么使用[默认值](#具有默认值的参数)来代替就更简单了。 +如果必需和可选的位置参数一起使用,则必需的参数必须首先出现在定义中。 -如果必需的和可选的位置参数一起使用,那么必需的参数必须先出现在定义中。 +#### 带默认值的参数 -## 标志 +你还可以为缺少的参数设置默认值。带默认值的参数在调用命令时也是可选的。 -除了传递位置参数之外, 你还可以通过为自定义命令定义标志(Flags)来传递命名参数。 +```nu +def greet [name = "Nushell"] { + $"Hello, ($name)!" +} +``` -比如: +你可以不带参数调用此命令,也可以带一个值来覆盖默认值: ```nu -def greet [ - name: string - --age: int -] { - echo $name $age +greet +# => Hello, Nushell! + +greet world +# => Hello, World! +``` + +你还可以将默认值与[类型注解](#parameter-types)结合起来: + +```nu +def congratulate [age: int = 18] { + $"Happy birthday! You are ($age) years old now!" } ``` -在上面的`greet`定义中,我们定义了`name`位置参数以及`age`标志。这允许`greet`的调用者也可以选择传递`age`参数。 +### 参数类型 -你可以用以下方法调用上述内容: +对于每个参数,你可以选择性地定义其类型。例如,你可以将基本的 `greet` 命令写成: ```nu -greet world --age 10 +def greet [name: string] { + $"Hello, ($name)" +} ``` -或者: +如果参数没有类型注解,Nushell 会将其视为 [`any` 类型](./types_of_data.html#any)。如果你在参数上注解了类型,Nushell 会在你调用函数时检查其类型。 + +例如,假设你只想接受一个 `int` 而不是 `string`: ```nu -greet --age 10 world +def greet [name: int] { + $"hello ($name)" +} + +greet World ``` -或者甚至完全不使用标志: +如果我们尝试运行上面的代码,Nushell 会告诉我们类型不匹配: ```nu -greet world +Error: nu::parser::parse_mismatch + + × Parse mismatch during operation. + ╭─[entry #1:1:7] + 1 │ greet World + · ──┬── + · ╰── expected int + ╰──── ``` -标志也可以指定一个缩写版本,这允许你传递一个更简单的标志,如同传递一个更容易阅读的全写标志那样。 +::: tip 酷! +类型检查是解析器的一项功能。在命令行中输入自定义命令时,Nushell 解析器甚至可以实时检测到无效的参数类型,并在执行命令前将其突出显示。 -让我们扩展前面的例子,为`age`添加一个缩写标志: +突出显示样式可以使用[主题](https://github.com/nushell/nu_scripts/tree/main/themes)或手动使用 `$env.config.color_config.shape_garbage` 进行更改。 +::: + +::: details 类型注解列表 +大多数类型都可以用作类型注解。此外,还有一些“形状”可以使用。例如: + +- `number`: 接受 `int` 或 `float` +- `path`: 一个字符串,其中 `~` 和 `.` 字符具有特殊含义,并将自动扩展为等效的完整路径。有关示例用法,请参阅语言参考指南中的[路径](/lang-guide/chapters/types/other_types/path.html)。 +- `directory`: `path`(上文)的子集。使用制表符补全参数时,只会提供目录。扩展方式与 `path` 相同。 +- `error`: 可用,但目前没有已知的有效用法。有关更多信息,请参阅语言参考指南中的[错误](/lang-guide/chapters/types/other_types/error.html)。 + +以下[类型](./types_of_data.html)可用于参数注解: + +- `any` +- `binary` +- `bool` +- `cell-path` +- `closure` +- `datetime` +- `duration` +- `filesize` +- `float` +- `glob` +- `int` +- `list` +- `nothing` +- `range` +- `record` +- `string` +- `table` + +::: + +### 标志 + +除了位置参数,你还可以定义命名标志。 + +例如: ```nu def greet [ name: string - --age (-a): int + --age: int ] { - echo $name $age + { + name: $name + age: $age + } } ``` -_注意:_ 标志是以其全称命名的,所以上面的例子的命令体内需要使用`$age`而不是`$a`。 +在此版本的 `greet` 中,我们定义了 `name` 位置参数以及一个 `age` 标志。位置参数(因为它没有 `?`)是必需的。命名标志是可选的。不带 `--age` 标志调用命令会将 `$age` 设置为 `null`。 -现在,我们可以使用缩写标志来调用这个新的定义: +`--age` 标志可以放在位置 `name` 之前或之后。示例: ```nu -greet -a 10 hello +greet Lucia --age 23 +# => ╭──────┬───────╮ +# => │ name │ Lucia │ +# => │ age │ 23 │ +# => ╰──────┴───────╯ + +greet --age 39 Ali +# => ╭──────┬─────╮ +# => │ name │ Ali │ +# => │ age │ 39 │ +# => ╰──────┴─────╯ + +greet World +# => ╭──────┬───────╮ +# => │ name │ World │ +# => │ age │ │ +# => ╰──────┴───────╯ ``` -标志也可以作为基本开关使用,这意味着它们的存在或不存在被当作定义的参数。延伸前面的例子: +标志也可以用简写版本定义。这允许你传递一个更简单的标志以及一个更易于阅读的长标志。 + +让我们扩展前面的示例,为 `age` 值使用一个简写标志: ```nu def greet [ name: string --age (-a): int - --twice ] { - if $twice { - echo $name $name $age $age - } else { - echo $name $age + { + name: $name + age: $age + } } +``` + +::: tip +结果变量始终基于长标志名称。在上面的示例中,变量仍然是 `$age`。`$a` 将无效。 +::: + +现在,我们可以使用简写标志调用此更新后的定义: + +```nu +greet Akosua -a 35 +# => ╭──────┬────────╮ +# => │ name │ Akosua │ +# => │ age │ 35 │ +# => ╰──────┴────────╯ +``` + +标志也可以用作基本开关。存在时,基于开关的变量为 `true`。不存在时,为 `false`。 + +```nu +def greet [ + name: string + --caps +] { + let greeting = $"Hello, ($name)!" + if $caps { + $greeting | str upcase + } else { + $greeting + } } + +greet Miguel --caps +# => HELLO, MIGUEL! + +greet Chukwuemeka +# => Hello, Chukwuemeka! +``` + +你还可以将其分配给 `true`/`false` 以启用/禁用该标志: + +```nu +greet Giulia --caps=false +# => Hello, Giulia! + +greet Hiroshi --caps=true +# => HELLO, HIROSHI! ``` -而这个定义可以通过如下方式调用: +::: tip +请注意以下错误: ```nu -greet -a 10 --twice hello +greet Gabriel --caps true ``` -或者只是没有开关标志: +键入空格而不是等号会将 `true` 作为位置参数传递,这可能不是期望的结果! + +为避免混淆,不允许在标志上注解布尔类型: ```nu -greet -a 10 hello +def greet [ + --caps: bool # 不允许 +] { ... } ``` -## 剩余参数 +::: -在某些情况下, 你可能想定义一个需要任意数量的位置参数的命令。我们可以用一个剩余参数(Rest Parameter)来实现这一点,通过下面的`...`语法: +标志可以包含破折号。可以通过在结果变量名中用下划线替换破折号来访问它们: ```nu -def greet [...name: string] { - print "hello all:" - for $n in $name { - echo $n +def greet [ + name: string + --all-caps +] { + let greeting = $"Hello, ($name)!" + if $all_caps { + $greeting | str upcase + } else { + $greeting + } +} +``` + +### 剩余参数 + +在某些情况下,你可能想定义一个需要任意数量的位置参数的命令。我们可以用一个“剩余”参数来实现这一点,使用下面的 `...` 语法: + +```nu +def multi-greet [...names: string] { + for $name in $names { + print $"Hello, ($name)!" } } -greet earth mars jupiter venus +multi-greet Elin Lars Erik +# => Hello, Elin! +# => Hello, Lars! +# => Hello, Erik! ``` -我们可以使用任意数量的参数来调用上述`greet`命令的定义,包括完全没有参数,所有的参数都将被收集到`$name`列表中。 +我们可以用任意数量的参数调用上面 `greet` 命令的定义,包括完全没有参数。所有参数都将收集到 `$names` 列表中。 剩余参数可以和位置参数一起使用: ```nu -def greet [vip: string, ...name: string] { - print $"hello to our VIP ($vip)" - print "and hello to everybody else:" - for $n in $name { - echo $n +def vip-greet [vip: string, ...names: string] { + for $name in $names { + print $"Hello, ($name)!" } + + print $"And a special welcome to our VIP today, ($vip)!" } -# $vip $name -# ---- ------------------------ -greet moon earth mars jupiter venus +# $vip $name +# ----- ------------------------- +vip-greet Rahul Priya Arjun Anjali Vikram +# => Hello, Priya! +# => Hello, Arjun! +# => Hello, Anjali! +# => Hello, Vikram! +# => And a special welcome to our VIP today, Rahul! ``` -## 为命令添加文档 +要将列表传递给剩余参数,你可以使用[展开运算符](/zh-CN/book/operators#spread-operator) (`...`)。使用上面的 `vip-greet` 命令定义: + +```nu +let vip = "Tanisha" +let guests = [ Dwayne, Shanice, Jerome ] +vip-greet $vip ...$guests +# => Hello, Dwayne! +# => Hello, Shanice! +# => Hello, Jerome! +# => And a special welcome to our VIP today, Tanisha! +``` -为了更好地帮助用户使用你的自定义命令,也可以为其添加额外的命令和参数描述。 +### 带包装的外部命令的剩余参数 -以我们之前的例子为例: +使用 `def --wrapped` 定义的自定义命令会将任何未知的标志和参数收集到一个剩余参数中,然后可以通过列表展开将其传递给外部命令。这允许自定义命令“包装”和扩展外部命令,同时仍然接受其所有原始参数。例如,外部 `eza` 命令显示目录列表。默认情况下,它显示网格排列: ```nu -def greet [ - name: string - --age (-a): int -] { - echo $name $age +eza commands +# => categories docs README.md +``` + +我们可以定义一个新命令 `ezal`,它将始终显示长列表,并添加图标: + +```nu +def --wrapped ezal [...rest] { + eza -l ...$rest } ``` -一旦定义完毕,我们可以运行`help greet`来获得该命令的帮助信息: +:::note +你也可以添加 `--icons`。我们在此示例中省略它,只是因为这些图标在本指南中显示效果不佳。 +::: +请注意,`--wrapped` 会将任何其他参数强制放入 `rest` 参数中,因此可以使用 `eza` 支持的任何参数调用该命令。这些附加参数将通过列表展开操作 `...$rest` 进行扩展。 + +```nu +ezal commands +# => drwxr-xr-x - ntd 7 Feb 11:41 categories +# => drwxr-xr-x - ntd 7 Feb 11:41 docs +# => .rw-r--r-- 936 ntd 14 Jun 2024 README.md + +ezal -d commands +# => drwxr-xr-x - ntd 14 Jun 2024 commands ``` -Usage: - > greet {flags} -Parameters: - +自定义命令可以检查某些参数并相应地更改其行为。例如,当使用 `-G` 选项强制使用网格时,我们可以省略向 `eza` 传递 `-l`: -Flags: - -h, --help: Display this help message - -a, --age +```nu +def --wrapped ezal [...rest] { + if '-G' in $rest { + eza ...$rest + } else { + eza -l --icons ...$rest + } +} + +ezal -G commands +# => categories docs README.md ``` -你可以看到我们定义的参数和标志,以及所有命令都会得到的`-h`帮助标志。 +## 管道输入输出签名 + +默认情况下,自定义命令接受 [`` 类型](./types_of_data.md#any)作为管道输入,同样可以输出 `` 类型。但自定义命令也可以被赋予明确的签名以缩小允许的类型范围。 -为了改进这个帮助,我们可以在定义中加入描述,这些描述将在帮助中显示出来: +例如,[`str stats`](/commands/docs/str_stats.md) 的签名如下所示: ```nu -# A greeting command that can greet the caller -def greet [ - name: string # The name of the person to greet - --age (-a): int # The age of the person -] { - echo $name $age -} +def "str stats" []: string -> record { } +``` + +在这里,`string -> record` 定义了命令的*管道输入和输出*的允许类型: + +- 它接受一个 `string` 作为管道输入 +- 它输出一个 `record` + +如果有多个输入/输出类型,它们可以放在括号内并用逗号或换行符分隔,如 [`str join`](/commands/docs/str_join.md) 中所示: + +```nu +def "str join" [separator?: string]: [ + list -> string + string -> string +] { } +``` + +这表示 `str join` 可以接受 `list` 或 `string` 作为管道输入。在任何一种情况下,它都将输出一个 `string`。 + +有些命令不接受或不需要数据作为管道输入。在这种情况下,输入类型将是 ``。如果命令返回 `null`(例如,[`rm`](/commands/docs/rm.md) 或 [`hide`](/commands/docs/hide.md)),输出类型也是如此: + +```nu +def xhide [module: string, members?]: nothing -> nothing { } ``` -我们给定义和参数添加的注释会作为描述出现在命令的`help`中。 +::: tip 注意 +上面的示例被重命名为 `xhide`,以便将其复制到 REPL 不会遮蔽内置的 `hide` 命令。 +::: -现在,如果我们运行`help greet`,就会得到一些更友好的帮助文本: +输入输出签名显示在命令的 `help` 中(包括内置和自定义命令),也可以通过以下方式进行内省: +```nu +help commands | where name == +scope commands | where name == ``` -A greeting command that can greet the caller +:::tip 酷! +输入输出签名允许 Nushell 在解析时捕获另外两类错误: + +- 尝试从命令返回错误的类型。例如: + + ```nu + def inc []: int -> int { + $in + 1 + print "Did it!" + } + + # => Error: nu::parser::output_type_mismatch + # => + # => × Command output doesn't match int. + # => ╭─[entry #1:1:24] + # => 1 │ ╭─▶ def inc []: int -> int { + # => 2 │ │ $in + 1 + # => 3 │ │ print "Did it!" + # => 4 │ ├─▶ } + # => · ╰──── expected int, but command outputs nothing + # => ╰──── + ``` + +- 以及尝试将无效类型传递给命令: + + ```nu + def inc []: int -> int { $in + 1 } + "Hi" | inc + # => Error: nu::parser::input_type_mismatch + # => + # => × Command does not support string input. + # => ╭─[entry #1:1:8] + # => 1 │ "Hi" | inc + # => · ─┬─ + # => · ╰── command doesn't support string input + # => ╰──── + ``` + +::: + +## 为你的命令添加文档 + +为了更好地帮助用户理解如何使用你的自定义命令,你还可以为它们添加额外的命令和参数描述。 + +运行 `help vip-greet` 来检查我们上面定义的最新命令: + +```text Usage: - > greet {flags} + > vip-greet ...(names) + +Flags: + -h, --help - Display the help message for this command Parameters: - The name of the person to greet + vip + ...names -Flags: - -h, --help: Display this help message - -a, --age : The age of the person +Input/output types: + ╭───┬───────┬────────╮ + │ # │ input │ output │ + ├───┼───────┼────────┤ + │ 0 │ any │ any │ + ╰───┴───────┴────────╯ ``` -## 管道输出 +::: tip 酷! +你可以看到 Nushell 仅根据我们到目前为止的定义就自动为该命令创建了一些基本帮助。Nushell 还自动向该命令添加了一个 `--help`/`-h` 标志,因此用户也可以使用 `vip-greet --help` 访问帮助。 +::: -自定义命令会像内置命令一样流式输出。例如,假设我们想重构这个管道: +我们可以通过一些简单的注释来进一步扩展帮助,描述命令及其参数: ```nu -ls | get name +# 问候客人和一位贵宾 +# +# 用于生日、毕业派对、 +# 退休,以及任何其他庆祝 +# 特定人物事件的活动。 +def vip-greet [ + vip: string # 特别嘉宾 + ...names: string # 其他客人 +] { + for $name in $names { + print $"Hello, ($name)!" + } + + print $"And a special welcome to our VIP today, ($vip)!" +} ``` -让我们把[`ls`](/commands/docs/ls.md)移到我们编写的命令中: +现在再次运行 `help vip-greet` 来看看有什么不同: -```nu -def my-ls [] { ls } +```text +问候客人和一位贵宾 + +用于生日、毕业派对、 +退休,以及任何其他庆祝 +特定人物事件的活动。 + +Category: default + +This command: +- does not create a scope. +- is not a built-in command. +- is not a subcommand. +- is not part of a plugin. +- is a custom command. +- is not a keyword. + +Usage: + > vip-greet + + +Flags: + + + -h, --help - Display the help message for this command + +Signatures: + + | vip-greet[ ] -> + +Parameters: + + vip: The special guest + ...rest: The other guests ``` -我们就可以像使用[`ls`](/commands/docs/ls.md)一样使用这个命令的输出: +请注意,`def` 语句前紧接的注释行成为帮助系统中命令的描述。可以使用多行注释。第一行(在空注释行之前)成为帮助的 `description`。此信息在制表符补全命令时也会显示。 + +其余的注释行成为其在帮助数据中的 `extra_description`。 + +::: tip +运行: ```nu -my-ls | get name -───┬─────────────────────── - 0 │ myscript.nu - 1 │ myscript2.nu - 2 │ welcome_to_nushell.md -───┴─────────────────────── +scope commands +| where name == 'vip-greet' +| wrap help ``` -这让我们可以很容易地创建自定义命令并处理它们的输出。注意,我们不像其他语言那样使用返回语句,取而代之的是我们创建管道,而其输出数据流可以连接到其他管道。 +这将显示 Nushell 创建的帮助*记录*。 +::: + +参数后面的注释成为它们的描述。对于参数,只允许单行注释。 -## 管道输入 +::: tip 注意 +用于参数文档目的的同一行上的 Nushell 注释需要在 `#` 磅号前有一个空格。 +::: -如同其他命令一样,自定义命令也可以从管道中获取输入,这个输入会自动传递给自定义命令所使用的代码块。 +## 在自定义命令中更改环境 -让我们创建一个把所有接收值都加倍的命令: +通常,环境变量定义和更改在块内是*作用域*的([./environment.html#scoping](./environment.html#scoping))。这意味着当它们在块结束时超出作用域时,对这些变量的更改将丢失,包括自定义命令的块。 ```nu -def double [] { - each { |elt| 2 * $elt } +def foo [] { + $env.FOO = 'After' } + +$env.FOO = "Before" +foo +$env.FOO +# => Before ``` -现在,如果我们在一个管道中调用上述命令,就可以看到它对输入的处理结果: +但是,使用 [`def --env`](/commands/docs/def.md) 或 [`export def --env`](/commands/docs/export_def.md)(对于[模块](modules.md))定义的命令将在调用方保留环境: ```nu -[1 2 3] | double -# => ───┬───── -# => 0 │ 2 -# => 1 │ 4 -# => 2 │ 6 -# => ───┴───── +def --env foo [] { + $env.FOO = 'After' +} + +$env.FOO = "Before" +foo +$env.FOO +# => After ``` -我们还可以使用`$in`变量来存储输入,以便在后面使用: +### 在自定义命令中更改目录 (cd) + +同样,使用 `cd` 命令更改目录会导致 `$env.PWD` 环境变量的更改。这意味着当自定义命令结束时,目录更改(`$env.PWD` 变量)也将被重置。如上所述,解决方案是使用 `def --env` 或 `export def --env`。 ```nu -def nullify [...cols] { - let start = $in - $cols | reduce --fold $start { |col, df| - $df | upsert $col null - } +def --env go-home [] { + cd ~ } + +cd / +go-home +pwd +# => Your home directory ``` ## 持久化 -关于如何持久化自定义命令,以便在你启动 Nushell 时它们是可用的,请参阅 [配置](configuration.md) 部分并添加你的启动脚本。 +要使自定义命令在将来的 Nushell 会话中可用,你需要将它们添加到你的启动配置中。你可以将命令定义添加到: + +- 直接在你的 `config.nu` 中 +- 由你的 `config.nu` 加载的文件中 +- 由你的 `config.nu` 导入的[模块](./modules.html)中 + +有关更多详细信息,请参阅[配置章节](configuration.md)。 diff --git a/zh-CN/book/custom_completions.md b/zh-CN/book/custom_completions.md index 7de4a1c670d..cdc48078616 100644 --- a/zh-CN/book/custom_completions.md +++ b/zh-CN/book/custom_completions.md @@ -1,31 +1,79 @@ # 自定义补全 -自定义补全允许你混合使用 Nushell 的两个功能:自定义命令和补全。有了它们,你就能够创建支持对位置参数和标志(Flags)参数进行自动补全的命令。这些自定义补全既适用于自定义命令,也适用于 [已知的外部或`extern`命令](externs.md)。 +自定义补全允许你混合使用 Nushell 的两个功能:自定义命令和补全。有了它们,你就能够创建支持对位置参数和标志(Flags)参数进行自动补全的命令。这些自定义补全既适用于[自定义命令](custom_commands.md),也适用于 [已知的外部或`extern`命令](externs.md)。 -自定义命令有两个部分:处理补全的命令和使用`@`将此命令附加到另一个命令的类型中。 +补全分两步定义: -## 自定义补全例子 +- 定义一个返回建议值的补全命令(又称 completer) +- 使用 `@` 将补全器附加到另一个命令参数的类型注解(shape)上 -我们来看一个例子: +下面是一个简单的例子: ```nu +# 补全命令 def animals [] { ["cat", "dog", "eel" ] } +# 待补全的命令 def my-command [animal: string@animals] { print $animal } my-command # => cat dog eel ``` -在第一行中,我们创建了一个自定义命令,将返回三个不同动物的列表。这些是我们想在补全中使用的值。一旦我们创建了这个命令,我们就可以用它来为其他自定义命令和 `extern` 提供补全。 +第一行定义了一个自定义命令,它返回一个包含三种不同动物的列表。这些是补全中要使用的可能值。 -在第二行,我们使用`string@animals`。这告诉了 Nushell 两件事:用于类型检查的参数的形状,以及如果用户想在该位置补全输入值,需要使用的自定义完成。 +::: tip +要抑制参数的补全(例如,一个可以接受任何整数的 `int`),可以定义一个返回空列表 (`[ ]`) 的补全器。 +::: -在第三行,我们输入我们的自定义命令的名称`my-command`,然后输入空格,再输入``键,就可以触发我们的自动补全功能。自定义补全的工作方式与系统中的其他补全方式相同,比如允许你输入`e`,然后按``键,得到 "eel" 自动补全。 +在第二行,`string@animals` 告诉 Nushell 两件事——用于类型检查的参数的形状,以及如果用户想在该位置补全输入值,需要使用的补全器。 + +第三行是补全的演示。输入自定义命令的名称 `my-command`,然后是一个空格,再按 Tab 键。这将显示一个包含可能补全的菜单。自定义补全的工作方式与系统中的其他补全相同,允许你输入 `e` 然后按 Tab 键自动补全 "eel"。 + +::: tip +当补全菜单显示时,提示符默认会包含 `|` 字符。要更改提示标记,请在 `$env.config.menus` 列表的 `completion_menu` 记录中修改 `marker` 的值。另请参阅[补全菜单配置](/zh-CN/book/line_editor.md#completion-menu)。 +::: + +::: tip +要回退到 Nushell 内置的文件补全,请返回 `null` 而不是建议列表。 +::: + +## 自定义补全选项 + +如果你想选择如何过滤和排序你的补全,你也可以返回一个记录而不是列表。补全建议列表应位于此记录的 `completions` 键下。可选地,它还可以在 `options` 键下包含一个记录,其中包含以下可选设置: + +- `sort` - 将此设置为 `false` 以阻止 Nushell 对你的补全进行排序。默认情况下,此值为 `true`,并且补全根据 `$env.config.completions.sort` 进行排序。 +- `case_sensitive` - 设置为 `true` 以使自定义补全区分大小写匹配,否则为 `false`。用于覆盖 `$env.config.completions.case_sensitive`。 +- `completion_algorithm` - 将此设置为 `prefix`、`substring` 或 `fuzzy` 以选择你的补全如何与键入的文本匹配。用于覆盖 `$env.config.completions.algorithm`。 + +这是一个演示如何设置这些选项的示例: + +```nu +def animals [] { + { + options: { + case_sensitive: false, + completion_algorithm: substring, + sort: false, + }, + completions: [cat, rat, bat] + } +} +def my-command [animal: string@animals] { print $animal } +``` + +现在,如果你尝试补全 `A`,你会得到以下补全: + +```nu +>| my-command A +cat rat bat +``` + +因为我们使匹配不区分大小写,Nushell 将在所有补全建议中找到子字符串 "a"。此外,因为我们设置了 `sort: false`,补全将保持其原始顺序。如果你的补全已经按与文本无关的特定顺序排序(例如按日期),这将非常有用。 ## 模块和自定义补全 -你可能更喜欢让你的自定义补全远离你的代码的公共 API。为此,你可以将模块和自定义补全结合起来。 +由于补全命令不应直接调用,因此通常在模块中定义它们。 -让我们把上面的例子放在一个模块中: +用模块扩展上面的例子: ```nu module commands { @@ -39,22 +87,145 @@ module commands { } ``` -在我们的模块中,我们选择只导出自定义命令`my-command`,而不导出自定义补全`animals`。这使得本模块的用户可以调用命令,甚至使用自定义补全逻辑,而不需要访问自定义补全。这样可以让 API 更干净,同时仍然提供所有相同的好处。 +在此模块中,只导出了自定义命令 `my-command`。`animals` 补全未导出。这使得此模块的用户可以调用该命令,甚至使用自定义补全逻辑,而无需访问补全命令本身。这会产生更清晰、更易于维护的 API。 + +::: tip +补全器在解析时使用 `@` 附加到自定义命令。这意味着,为了使对补全命令的更改生效,公共自定义命令也必须重新解析。导入模块通过单个 `use` 语句同时满足这两个要求。 +::: + +## 上下文感知的自定义补全 + +可以将上下文传递给补全命令。这在需要知道先前的参数或标志以生成准确补全的情况下很有用。 + +将此概念应用于前面的示例: + +```nu +module commands { + def animals [] { + ["cat", "dog", "eel" ] + } + + def animal-names [context: string] { + match ($context | split words | last) { + cat => ["Missy", "Phoebe"] + dog => ["Lulu", "Enzo"] + eel => ["Eww", "Slippy"] + } + } + + export def my-command [ + animal: string@animals + name: string@animal-names + ] { + print $"The ($animal) is named ($name)." + } +} +``` + +在这里,命令 `animal-names` 返回适当的名称列表。`$context` 是一个字符串,其值是到目前为止已键入的命令行。 -这是可能的,因为使用`@`的自定义补全标签在命令第一次被解析时就被锁定了。 +```nu +>| my-command +cat dog eel +>| my-command dog +Lulu Enzo +>my-command dog enzo +The dog is named Enzo +``` -## 自定义补全和 `extern` +在第二行,按 tab 键后,参数 `"my-command dog"` 作为上下文传递给 `animal-names` 补全器。 -一个强大的组合是为 [已知的`extern`命令](externs.md) 添加自定义补全。这些工作方式与向自定义命令添加自定义补全的方式相同:创建自定义补全,然后用`@`附加到 `extern` 的一个位置参数或标志参数的类型中。 +::: tip +补全器还可以使用以下方式获取命令行上的当前光标位置: + +```nu +def completer [context:string, position:int] {} +``` + +::: + +## 自定义补全和 [`extern`](/commands/docs/extern.md) + +一个强大的组合是为 [已知的`extern`命令](externs.md) 添加自定义补全。这些工作方式与向自定义命令添加自定义补全的方式相同:通过创建自定义补全,然后用`@`附加到 `extern` 的一个位置参数或标志参数的类型中。 如果你仔细看一下默认配置中的例子,你会看到这个: ```nu export extern "git push" [ - remote?: string@"nu-complete git remotes", # the name of the remote - refspec?: string@"nu-complete git branches"# the branch / refspec + remote?: string@"nu-complete git remotes", # the name of the remote + refspec?: string@"nu-complete git branches" # the branch / refspec ... ] ``` 自定义补全在这个例子中的作用与前面的例子中的作用相同。上面的例子根据用户当前所处的位置,调用了两个不同的自定义补全。 + +## 自定义描述和样式 + +作为返回字符串列表的替代方法,补全函数还可以返回一个记录列表,其中包含一个 `value` 字段以及可选的 `description` 和 `style` 字段。样式可以是以下之一: + +- 一个带有前景色的字符串,可以是十六进制代码或颜色名称,例如 `yellow`。有关有效颜色名称的列表,请参阅 `ansi --list`。 +- 一个包含 `fg`(前景色)、`bg`(背景色)和 `attr`(属性,如带下划线和粗体)字段的记录。此记录的格式与 `ansi --escape` 接受的格式相同。有关 `attr` 字段的可能值列表,请参阅 [`ansi`](/commands/docs/ansi) 命令参考。 +- 相同的记录,但转换为 JSON 字符串。 + +```nu +def my_commits [] { + [ + { value: "5c2464", description: "Add .gitignore", style: red }, + # "attr: ub" => underlined and bolded + { value: "f3a377", description: "Initial commit", style: { fg: green, bg: "#66078c", attr: ub } } + ] +} +``` + +::: tip 注意 +使用以下代码段: + +```nu +def my-command [commit: string@my_commits] { + print $commit +} +``` + +... 请注意,即使补全菜单会向你显示类似 + +```ansi +>_ my-command +5c2464 Add .gitignore +f3a377 Initial commit +``` + +... 的内容,也只有值(即 "5c2464" 或 "f3a377")将用于命令参数! +::: + +## 外部补全 + +也可以集成外部补全器,而不是仅仅依赖于 Nushell 的补全器。 + +为此,请将 `config.nu` 中的 `external_completer` 字段设置为一个[闭包](types_of_data.md#closures),如果未找到 Nushell 补全,则将对其进行求值。 + +```nu +$env.config.completions.external = { + enable: true + max_results: 100 + completer: $completer +} +``` + +你可以配置闭包以运行外部补全器,例如 [carapace](https://github.com/rsteube/carapace-bin)。 + +外部补全器是一个函数,它接受当前命令作为字符串列表,并输出一个带有 `value` 和 `description` 键的记录列表,就像自定义补全函数一样。当闭包返回 `null` 时,它默认为文件补全。 + +::: tip 注意 +此闭包将接受当前命令作为列表。例如,键入 `my-command --arg1 ` 将接收 `[my-command --arg1 " "]`。 +::: + +此示例将启用 carapace 外部补全: + +```nu +let carapace_completer = {|spans| + carapace $spans.0 nushell ...$spans | from json +} +``` + +[更多外部补全器的示例可以在 实战指南 中找到](../cookbook/external_completers.md)。 diff --git a/zh-CN/book/dataframes.md b/zh-CN/book/dataframes.md index 3de063f43ad..04d511ed1e7 100644 --- a/zh-CN/book/dataframes.md +++ b/zh-CN/book/dataframes.md @@ -1,175 +1,161 @@ -# DataFrames +# Dataframes -::: warning -要使用`DataFrame`支持,您需要使用 `cargo build --features dataframe` 构建一个具有完整功能的版本。从版本 0.72 开始,Nushell 的二进制发布版本中 *不* 包含`DataFrame`。[请参阅安装说明](/book/installation.md) 以获取更多详细信息。 +::: warning 重要! +此功能需要 Polars 插件。请参阅[插件章节](plugins.md)以了解如何安装它。 + +要测试此插件是否已正确安装,请运行 `help polars`。 ::: -正如我们到目前为止所看到的,Nushell 把数据处理作为其主要任务。`Lists` 和 `Tables`的存在是为了帮助你循环处理值,以便执行多种操作或轻而易举地找到数据。然而,在某些操作中,基于行的数据布局并不是处理数据的最有效方式,特别是在处理极其庞大的文件时。对于大型数据集的`group-by`或`join`等操作,如果不使用适当的数据格式,会占用大量的内存,并可能耗费大量的计算时间。 +正如我们到目前为止所看到的,Nushell 把处理数据作为其主要任务。 +`Lists` 和 `Tables` 的存在是为了帮助你循环处理值,以便执行多种操作或轻而易举地找到数据。然而,在某些操作中,基于行的数据布局并不是处理数据的最有效方式,特别是在处理极其庞大的文件时。对于大型数据集的`group-by`或`join`等操作,如果不使用适当的数据格式,会占用大量的内存,并可能耗费大量的计算时间。 + +出于这个原因,`DataFrame` 结构被引入到 Nushell 中。`DataFrame` 以列格式存储数据,以 [Apache Arrow](https://arrow.apache.org/) 规范为基础,并使用 [Polars](https://github.com/pola-rs/polars) 作为执行极其[快速列式操作](https://h2oai.github.io/db-benchmark/) 的引擎。 -出于这个原因,Nushell 引入了`DataFrame`结构。`DataFrame`以列格式存储数据,以 [Apache Arrow](https://arrow.apache.org/) 规范为基础,并使用 [Polars](https://github.com/pola-rs/polars) 作为执行极其 [快速列式操作](https://h2oai.github.io/db-benchmark/) 的马达。 +你现在可能想知道这个组合能有多快,以及它如何能使数据工作更容易、更可靠。出于这个原因,我们将从本章开始,介绍处理数据时常见操作的性能测试情况。 -你现在可能想知道这个组合能有多快,以及它如何能使数据工作更容易、更可靠。出于这个原因,让我们在本页开始时介绍一下处理数据时的常见操作的性能测试情况。 +[[toc]] ## 性能测试对比 -在这个小的性能测试练习中,我们将比较本地的 Nushell 原生命令、Nushell DataFrame 相关命令和[Python Pandas](https://pandas.pydata.org/)命令。暂时不要太在意`dataframe`命令,它们将在本页后面的章节中解释。 +在这个小的性能测试练习中,我们将比较本地的 Nushell 原生命令、Nushell DataFrame 相关命令和[Python Pandas](https://pandas.pydata.org/)命令。暂时不要太在意[`Dataframe` 命令](/commands/categories/dataframe.md),它们将在本页后面的章节中解释。 -> 系统细节:本节介绍的性能测试是用一台配备 Intel(R) Core(TM) i7-10710U -> (CPU @1.10GHz 1.61GHz)和 16 GB 内存的机器运行的。 -> -> 所有的例子都在 Nushell 0.78 版本上运行。 +::: tip 系统细节 +本节介绍的性能测试是用一台配备 M1 pro 处理器和 32gb 内存的 Macbook 运行的。所有的例子都在 Nushell 0.97 版本上使用 `nu_plugin_polars 0.97` 运行。 +::: ### 文件信息 我们将用于性能测试的文件是 [新西兰商业人口统计](https://www.stats.govt.nz/assets/Uploads/New-Zealand-business-demography-statistics/New-Zealand-business-demography-statistics-At-February-2020/Download-data/Geographic-units-by-industry-and-statistical-area-2000-2020-descending-order-CSV.zip) 数据集。 如果你想尝试这些测试,请下载该文件。 -该数据集有 5 列,5,429,252 行。我们可以通过使用`dfr ls`命令来检查: - -```nu -❯ let df = (dfr open .\Data7602DescendingYearOrder.csv) -❯ dfr ls - -╭───┬────────┬─────────┬─────────╮ -│ # │ name │ columns │ rows │ -├───┼────────┼─────────┼─────────┤ -│ 0 │ $df │ 5 │ 5429252 │ -╰───┴────────┴─────────┴─────────╯ -``` - -我们可以用 `first` 看一下文件的第一行: - -```nu -❯ $df | dfr first -╭───┬──────────┬─────────┬──────┬───────────┬──────────╮ -│ # │ anzsic06 │ Area │ year │ geo_count │ ec_count │ -├───┼──────────┼─────────┼──────┼───────────┼──────────┤ -│ 0 │ A │ A100100 │ 2000 │ 96 │ 130 │ -╰───┴──────────┴─────────┴──────┴───────────┴──────────╯ -``` - -...最后,我们可以了解一下推断出的数据类型: +该数据集有 5 列,5,429,252 行。我们可以通过使用`polars store-ls`命令来检查: ```nu -❯ $df | dfr dtypes -╭───┬───────────┬───────╮ -│ # │ column │ dtype │ -├───┼───────────┼───────┤ -│ 0 │ anzsic06 │ str │ -│ 1 │ Area │ str │ -│ 2 │ year │ i64 │ -│ 3 │ geo_count │ i64 │ -│ 4 │ ec_count │ i64 │ -╰───┴───────────┴───────╯ +let df_0 = polars open --eager Data7602DescendingYearOrder.csv +polars store-ls | select key type columns rows estimated_size +# => ╭──────────────────────────────────────┬───────────┬─────────┬─────────┬────────────────╮ +# => │ key │ type │ columns │ rows │ estimated_size │ +# => ├──────────────────────────────────────┼───────────┼─────────┼─────────┼────────────────┤ +# => │ b2519dac-3b64-4e5d-a0d7-24bde9052dc7 │ DataFrame │ 5 │ 5429252 │ 184.5 MB │ +# => ╰──────────────────────────────────────┴───────────┴─────────┴─────────┴────────────────╯ ``` -### 加载文件 +::: tip +从 nushell 0.97 开始,`polars open` 将作为惰性 dataframe 打开,而不是即时 dataframe。 +要作为即时 dataframe 打开,请使用 `--eager` 标志。 +::: -让我们先来比较一下各种方法的加载时间。首先,我们将使用 Nushell 的[`open`](/commands/docs/open.md)命令加载数据: +我们可以用 [`first`](/commands/docs/first.md) 看一下文件的第一行: ```nu -❯ timeit {open .\Data7602DescendingYearOrder.csv} -30sec 479ms 614us 400ns -``` - -使用原生的 Nushell 功能加载文件需要 30 秒。对于加载 500 万条记录来说,这还算不错!但我们可以做得更好一些。 - -现在让我们使用 Pandas。我们将使用以下脚本来加载文件: - -```python -import pandas as pd - -df = pd.read_csv("Data7602DescendingYearOrder.csv") +$df_0 | polars first +# => ╭───┬──────────┬─────────┬──────┬───────────┬──────────╮ +# => │ # │ anzsic06 │ Area │ year │ geo_count │ ec_count │ +# => ├───┼──────────┼─────────┼──────┼───────────┼──────────┤ +# => │ 0 │ A │ A100100 │ 2000 │ 96 │ 130 │ +# => ╰───┴──────────┴─────────┴──────┴───────────┴──────────╯ ``` -而它的性能测试结果是: +...最后,我们可以了解一下推断出的数据类型: ```nu -❯ timeit {python load.py} -2sec 91ms 872us 900ns +$df_0 | polars schema +# => ╭───────────┬─────╮ +# => │ anzsic06 │ str │ +# => │ Area │ str │ +# => │ year │ i64 │ +# => │ geo_count │ i64 │ +# => │ ec_count │ i64 │ +# => ╰───────────┴─────╯ ``` -这是一个很大的进步,从 30 秒到 2 秒。干得好,Pandas! +### `Group-by`比较 -也许我们加载数据可以再快一点,这一次我们将使用 Nushell 的 `dfr open` 命令: +为了输出更具统计意义的计时,让我们加载并使用 `std bench` 命令。 ```nu -❯ timeit {dfr open .\Data7602DescendingYearOrder.csv} -601ms 700us 700ns +use std/bench ``` -这一次,我们花了 0.6 秒。一点也不差。 - -### `Group-by`比较 - -这次让我们做一个稍微复杂的操作。我们将按年份对数据进行分组,并根据`geo_count`列对分组求和。 +我们将按年份对数据进行分组,并对 `geo_count` 列求和。 -同样,我们要从 Nushell 的原生命令开始: - -::: tip -如果你想运行这个例子,请注意接下来的命令将使用大量的内存,在该命令执行期间可能会影响你的系统性能。 -::: +首先,让我们测量一下 Nushell 原生命令管道的性能。 ```nu -❯ timeit { - open .\Data7602DescendingYearOrder.csv - | group-by year - | transpose header rows - | upsert rows { get rows | math sum } - | flatten +bench -n 10 --pretty { + open 'Data7602DescendingYearOrder.csv' + | group-by year --to-table + | update items {|i| + $i.items.geo_count + | math sum + } } - -6min 30sec 622ms 312us +# => 3sec 268ms +/- 50ms ``` -所以,执行这个聚合操作需要 6 分钟。 +所以,执行这个聚合操作需要 3.3 秒。 -让我们试试在`pandas`中进行同样的操作: +让我们试试在 pandas 中进行同样的操作: -```python -import pandas as pd +```nu +('import pandas as pd df = pd.read_csv("Data7602DescendingYearOrder.csv") res = df.groupby("year")["geo_count"].sum() -print(res) +print(res)' +| save load.py -f) ``` 而性能测试的结果是: ```nu -❯ timeit {python .\load.py} - -1sec 966ms 954us 800ns +bench -n 10 --pretty { + python load.py | complete | null +} +# => 1sec 322ms +/- 6ms ``` -一点也不差!同样,Pandas 设法在很短的时间内完成了它。 +一点也不差!Pandas 设法比 Nushell 快了 2.6 倍。 +对于更大的文件,Pandas 的优势应该会增加。 -为了进行比较,让我们试试 Nushell DataFrames。我们要把所有的操作放在一个`nu`文件中,以确保我们做的是类似的操作: +为了进行比较,让我们试试 Nushell dataframes。我们要把所有的操作放在一个`nu`文件中,以确保我们做的是正确的比较: ```nu -let df = (dfr open Data7602DescendingYearOrder.csv) -let res = ($df | dfr group-by year | dfr agg (dfr col geo_count | dfr sum)) -$res +( 'polars open Data7602DescendingYearOrder.csv + | polars group-by year + | polars agg (polars col geo_count | polars sum) + | polars collect' +| save load.nu -f ) ``` -当使用 DataFrames 时的性能测试结果是: +而 dataframes 的性能测试结果(为了公平比较,每次测试都加载一个新的 nushell 和 `polars` 实例)是: ```nu -❯ timeit {source load.nu} - -557ms 658us 500ns +bench -n 10 --pretty { + nu load.nu | complete | null +} +# => 135ms +/- 4ms ``` -幸运的是,Nushell DataFrame 设法将时间再次减半。这不是很好吗? +`polars` dataframes 插件设法比 `pandas` 和 python 快 10 倍。这不是很好吗? -正如你所看到的,Nushell 的[`Dataframe`](/commands/categories/dataframe.md)命令和现在最常见的做数据分析的工具一样快。这个发行版中的命令有可能成为你做数据分析的首选工具。通过组合复杂的 Nushell 管道,你可以以一种可靠的方式从数据中提取信息。 +正如你所看到的,Nushell 的 `polars` 插件和 `polars` 本身一样高效。 +结合 Nushell 命令和管道,它能够在不离开终端的情况下进行复杂的分析。 -## 使用 DataFrames +让我们清理一下我们在基准测试期间使用的 dataframe 的缓存。 +为此,让我们停止 `polars`。 +当我们执行下一个命令时,我们将启动一个新的插件实例。 + +```nu +plugin stop polars +``` -在看到了可以用[`Dataframe`](/commands/categories/dataframe.md)命令完成的事情之后,现在是时候开始测试它们了。首先,让我们创建一个样本 CSV 文件,该文件将成为我们的样本 DataFrame,并与示例一起使用。在你喜欢的编辑器中粘贴下面几行来创建样本 csv 文件: +## 使用 Dataframes -```text -int_1,int_2,float_1,float_2,first,second,third,word +在看到了可以用[`Dataframe` 命令](/commands/categories/dataframe.md)完成的事情之后,现在是时候开始测试它们了。首先,让我们创建一个样本 CSV 文件,该文件将成为我们的样本 dataframe,并与示例一起使用。在你喜欢的编辑器中粘贴下面几行来创建样本 csv 文件: + +```nu +("int_1,int_2,float_1,float_2,first,second,third,word 1,11,0.1,1.0,a,b,c,first 2,12,0.2,1.0,a,b,c,second 3,13,0.3,2.0,a,b,c,third @@ -179,275 +165,287 @@ int_1,int_2,float_1,float_2,first,second,third,word 7,17,0.7,6.0,b,c,a,third 8,18,0.8,7.0,c,c,b,eight 9,19,0.9,8.0,c,c,b,ninth -0,10,0.0,9.0,c,c,b,ninth +0,10,0.0,9.0,c,c,b,ninth" +| save --raw --force test_small.csv) ``` 保存该文件并随意命名,在这些例子中,该文件将被称为 `test_small.csv`。 -现在,要将该文件作为 DataFrame 进行读取,请使用 `dfr open` 命令,如下所示: +现在,要将该文件作为 dataframe 进行读取,请使用 `polars open` 命令,如下所示: ```nu -let df = (dfr open test_small.csv) +let df_1 = polars open --eager test_small.csv ``` -这应该会在内存中创建一个值 `$df`,用来存放我们刚刚创建的数据。 +这应该会在内存中创建一个值 `$df_1`,用来存放我们刚刚创建的数据。 ::: tip -`dfr open` 命令可以读取 **csv** 或 **parquet** 文件。 +`polars open` 命令可以读取 **csv**、**tsv**、**parquet**、**json(l)**、**arrow** 和 **avro** 格式的文件。 ::: -要查看存储在内存中的所有 DataFrames,你可以使用: +要查看存储在内存中的所有 dataframes,你可以使用: ```nu -❯ dfr ls -╭───┬──────┬─────────┬──────╮ -│ # │ name │ columns │ rows │ -├───┼──────┼─────────┼──────┤ -│ 0 │ $df │ 8 │ 10 │ -╰───┴──────┴─────────┴──────╯ +polars store-ls | select key type columns rows estimated_size +# => ╭──────────────────────────────────────┬───────────┬─────────┬──────┬────────────────╮ +# => │ key │ type │ columns │ rows │ estimated_size │ +# => ├──────────────────────────────────────┼───────────┼─────────┼──────┼────────────────┤ +# => │ e780af47-c106-49eb-b38d-d42d3946d66e │ DataFrame │ 8 │ 10 │ 403 B │ +# => ╰──────────────────────────────────────┴───────────┴─────────┴──────┴────────────────╯ ``` -正如你所看到的,该命令显示了所创建的 DataFrame 以及关于它们的基本信息。 +正如你所看到的,该命令显示了所创建的 dataframes 以及关于它们的基本信息。 -如果你想看到加载的 DataFrame 的预览,你可以将 DataFrame 变量发送到流中: +如果你想看到加载的 dataframe 的预览,你可以将 dataframe 变量发送到流中: ```nu -❯ $df -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ -│ 1 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ -│ 2 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ -│ 3 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ -│ 4 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ -│ 5 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ -│ 6 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ -│ 7 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ -│ 8 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ -│ 9 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +$df_1 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ +# => │ 1 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ +# => │ 2 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ +# => │ 3 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ +# => │ 4 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ +# => │ 5 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ +# => │ 6 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ +# => │ 7 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ +# => │ 8 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ +# => │ 9 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` -有了内存中的 DataFrame,我们就可以开始对 `DataFrame` 进行列操作。 +有了内存中的 dataframe,我们就可以开始对 `DataFrame` 进行列操作。 ::: tip -如果你想看到所有可用的 DataFrame 命令,你可以使用 `scope commands | where category =~ dataframe`。 +如果你想看到所有可用的 dataframe 命令,你可以使用 `scope commands | where category =~ dataframe`。 ::: ## 基本聚合 -让我们从 DataFrame 的基本聚合开始,通过使用`aggregate`命令对`df`中存在的所有列进行求和: +让我们从 dataframe 的基本聚合开始。让我们使用 `aggregate` 命令对 `df` 中存在的所有列进行求和: ```nu -❯ $df | dfr sum -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬──────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼──────┤ -│ 0 │ 40 │ 145 │ 4.50 │ 46.00 │ │ │ │ │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴──────╯ +$df_1 | polars sum | polars collect +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬──────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼──────┤ +# => │ 0 │ 40 │ 145 │ 4.50 │ 46.00 │ │ │ │ │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴──────╯ ``` -正如你所看到的,聚合函数(`aggregate`)为那些有意义的列计算出了总和。如果你想过滤掉文本列,你可以使用`select`命令来选择你想要的列。 +正如你所看到的,聚合函数为那些有意义的列计算出了总和。如果你想过滤掉文本列,你可以使用[`polars select`](/commands/docs/polars_select.md)命令来选择你想要的列。 ```nu -❯ $df | dfr sum | dfr select int_1 int_2 float_1 float_2 -╭───┬───────┬───────┬─────────┬─────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ -├───┼───────┼───────┼─────────┼─────────┤ -│ 0 │ 40 │ 145 │ 4.50 │ 46.00 │ -╰───┴───────┴───────┴─────────┴─────────╯ +$df_1 | polars sum | polars select int_1 int_2 float_1 float_2 | polars collect +# => ╭───┬───────┬───────┬─────────┬─────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ +# => ├───┼───────┼───────┼─────────┼─────────┤ +# => │ 0 │ 40 │ 145 │ 4.50 │ 46.00 │ +# => ╰───┴───────┴───────┴─────────┴─────────╯ ``` 你甚至可以像存储任何其他 Nushell 变量一样存储这个聚合的结果: ```nu -❯ let res = ($df | dfr sum | dfr select int_1 int_2 float_1 float_2) +let res = $df_1 | polars sum | polars select int_1 int_2 float_1 float_2 ``` ::: tip -输入 `let res = ( !! )` 并按回车,这将自动完成之前执行的命令。注意 ( 和 !! 之间的空格。 +输入 `let res = !!` 并按回车,这将自动完成之前执行的命令。注意 `=` 和 `!!` 之间的空格。 ::: -现在我们有两个 DataFrame 存储在内存中: +现在我们有两个 dataframe 存储在内存中: ```nu -❯ dfr ls -╭───┬──────┬─────────┬──────╮ -│ # │ name │ columns │ rows │ -├───┼──────┼─────────┼──────┤ -│ 0 │ $res │ 4 │ 1 │ -│ 1 │ $df │ 8 │ 10 │ -╰───┴──────┴─────────┴──────╯ +polars store-ls | select key type columns rows estimated_size +╭──────────────────────────────────────┬───────────┬─────────┬──────┬────────────────╮ +│ key │ type │ columns │ rows │ estimated_size │ +├──────────────────────────────────────┼───────────┼─────────┼──────┼────────────────┤ +│ e780af47-c106-49eb-b38d-d42d3946d66e │ DataFrame │ 8 │ 10 │ 403 B │ +│ 3146f4c1-f2a0-475b-a623-7375c1fdb4a7 │ DataFrame │ 4 │ 1 │ 32 B │ +╰──────────────────────────────────────┴───────────┴─────────┴──────┴────────────────╯ ``` 很整洁,不是吗? -你可以在 DataFrame 上进行若干聚合,以便从中提取基本信息,也可以对你的全新 DataFrame 进行基本数据分析。 +你可以在 dataframe 上进行若干聚合,以便从中提取基本信息,也可以对你的全新 dataframe 进行基本数据分析。 ## 连接 DataFrame -也可以用一个列作为参考来连接(`join`)两个 DataFrame。我们将把我们的迷你 DataFrame 与另一个迷你 DataFrame 连接起来。在另一个文件中复制这些行,并创建相应的 DataFrame(在以下例子中,我们将称之为`test_small_a.csv`)。 +也可以用一个列作为参考来连接(`join`)两个 dataframe。我们将把我们的迷你 dataframe 与另一个迷你 dataframe 连接起来。在另一个文件中复制这些行,并创建相应的 dataframe(在以下例子中,我们将称之为`test_small_a.csv`)。 -```text -int_1,int_2,float_1,float_2,first +```nu +"int_1,int_2,float_1,float_2,first 9,14,0.4,3.0,a 8,13,0.3,2.0,a 7,12,0.2,1.0,a -6,11,0.1,0.0,b +6,11,0.1,0.0,b" +| save --raw --force test_small_a.csv ``` -我们使用 `dfr open` 命令来创建新的变量: +我们使用 `polars open` 命令来创建新的变量: ```nu -❯ let df_a = (dfr open test_small_a.csv) +let df_2 = polars open --eager test_small_a.csv ``` -现在,当第二个 DataFrame 加载到内存中时,我们可以使用左边 DataFrame 的`int_1`列和右边 DataFrame 的`int_1`列来连接它们。 +现在,当第二个 dataframe 加载到内存中时,我们可以使用左边 dataframe 的`int_1`列和右边 dataframe 的`int_1`列来连接它们。 ```nu -❯ $df | dfr join $df_a int_1 int_1 -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────┬─────────┬───────────┬───────────┬─────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ int_2_x │ float_1_x │ float_2_x │ first_x │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┼─────────┼───────────┼───────────┼─────────┤ -│ 0 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ 11 │ 0.10 │ 0.00 │ b │ -│ 1 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ 12 │ 0.20 │ 1.00 │ a │ -│ 2 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ 13 │ 0.30 │ 2.00 │ a │ -│ 3 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ 14 │ 0.40 │ 3.00 │ a │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────┴─────────┴───────────┴───────────┴─────────╯ +$df_1 | polars join $df_2 int_1 int_1 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────┬─────────┬───────────┬───────────┬─────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ int_2_x │ float_1_x │ float_2_x │ first_x │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┼─────────┼───────────┼───────────┼─────────┤ +# => │ 0 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ 11 │ 0.10 │ 0.00 │ b │ +# => │ 1 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ 12 │ 0.20 │ 1.00 │ a │ +# => │ 2 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ 13 │ 0.30 │ 2.00 │ a │ +# => │ 3 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ 14 │ 0.40 │ 3.00 │ a │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────┴─────────┴───────────┴───────────┴─────────╯ ``` ::: tip -在`Nu`中,当一个命令有多个参数,并期望得到多个值时,我们用方括号`[]`来包裹这些值。在`dfr join`的情况下,我们可以对多个列进行连接,只要它们具有相同的类型。 +在`Nu`中,当一个命令有多个参数,并期望得到多个值时,我们用方括号`[]`来包裹这些值。在[`polars join`](/commands/docs/polars_join.md)的情况下,我们可以对多个列进行连接,只要它们具有相同的类型。 ::: 例如: ```nu -❯ $df | dfr join $df_a [int_1 first] [int_1 first] -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────┬─────────┬───────────┬───────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ int_2_x │ float_1_x │ float_2_x │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┼─────────┼───────────┼───────────┤ -│ 0 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ 11 │ 0.10 │ 0.00 │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────┴─────────┴───────────┴───────────╯ +$df_1 | polars join $df_2 [int_1 first] [int_1 first] +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────┬─────────┬───────────┬───────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ int_2_x │ float_1_x │ float_2_x │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┼─────────┼───────────┼───────────┤ +# => │ 0 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ 11 │ 0.10 │ 0.00 │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────┴─────────┴───────────┴───────────╯ ``` -默认情况下,连接命令做的是内连接,也就是说,它将保留两个 DataFrame 都有相同值的记录。你可以选择一个左联接来保留左边 DataFrame 中缺失的行。你也可以保存这个结果,以便在以后的操作中使用它。 +默认情况下,连接命令做的是内连接,也就是说,它将保留两个 dataframe 都有相同值的记录。你可以选择一个左联接来保留左边 dataframe 中缺失的行。你也可以保存这个结果,以便在以后的操作中使用它。 ## DataFrame 分组 -可以用 DataFrame 进行的最强大的操作之一是`dfr group-by`。这个命令将允许你根据一个分组条件进行聚合操作。在 Nushell 中,`GroupBy`是一种可以被存储和重复使用的对象,可以被用于多个聚合。这是很方便的,因为在进行分组时,创建分组对是最昂贵的运算,如果你打算用同一个分组条件进行多个操作,就没有必要重复该运算。 +可以用 DataFrame 进行的最强大的操作之一是[`polars group-by`](/commands/docs/polars_group-by.md)。这个命令将允许你根据一个分组条件进行聚合操作。在 Nushell 中,`GroupBy`是一种可以被存储和重复使用的对象,可以被用于多个聚合。这是很方便的,因为在进行分组时,创建分组对是最昂贵的运算,如果你打算用同一个分组条件进行多个操作,就没有必要重复该运算。 -要创建一个`GroupBy`对象,你只需要使用`group-by`命令: +要创建一个`GroupBy`对象,你只需要使用[`polars_group-by`](/commands/docs/polars_group-by.md)命令: ```nu -❯ let group = ($df | dfr group-by first) -❯ $group -╭─────────────┬──────────────────────────────────────────────╮ -│ LazyGroupBy │ apply aggregation to complete execution plan │ -╰─────────────┴──────────────────────────────────────────────╯ +let group = ($df_1 | polars group-by first) +$group +# => ╭─────────────┬──────────────────────────────────────────────╮ +# => │ LazyGroupBy │ apply aggregation to complete execution plan │ +# => ╰─────────────┴──────────────────────────────────────────────╯ ``` 当打印 `GroupBy` 对象时,我们可以看到它在后台是一个懒惰的操作,等待着通过添加一个聚合来完成。使用 `GroupBy` 我们可以在一个列上创建聚合 ```nu -❯ $group | dfr agg (dfr col int_1 | dfr sum) -╭───┬───────┬───────╮ -│ # │ first │ int_1 │ -├───┼───────┼───────┤ -│ 0 │ b │ 17 │ -│ 1 │ a │ 6 │ -│ 2 │ c │ 17 │ -╰───┴───────┴───────╯ +$group | polars agg (polars col int_1 | polars sum) +# => ╭────────────────┬───────────────────────────────────────────────────────────────────────────────────────╮ +# => │ plan │ AGGREGATE │ +# => │ │ [col("int_1").sum()] BY [col("first")] FROM │ +# => │ │ DF ["int_1", "int_2", "float_1", "float_2"]; PROJECT */8 COLUMNS; SELECTION: "None" │ +# => │ optimized_plan │ AGGREGATE │ +# => │ │ [col("int_1").sum()] BY [col("first")] FROM │ +# => │ │ DF ["int_1", "int_2", "float_1", "float_2"]; PROJECT 2/8 COLUMNS; SELECTION: "None" │ +# => ╰────────────────┴───────────────────────────────────────────────────────────────────────────────────────╯ ``` 或者我们可以在相同或不同的列上定义多个聚合: ```nu -❯ $group | dfr agg [ -∙ (dfr col int_1 | dfr n-unique) -∙ (dfr col int_2 | dfr min) -∙ (dfr col float_1 | dfr sum) -∙ (dfr col float_2 | dfr count) -∙ ] | dfr sort-by first -╭───┬───────┬───────┬───────┬─────────┬─────────╮ -│ # │ first │ int_1 │ int_2 │ float_1 │ float_2 │ -├───┼───────┼───────┼───────┼─────────┼─────────┤ -│ 0 │ a │ 3 │ 11 │ 0.60 │ 3 │ -│ 1 │ b │ 4 │ 14 │ 2.20 │ 4 │ -│ 2 │ c │ 3 │ 10 │ 1.70 │ 3 │ -╰───┴───────┴───────┴───────┴─────────┴─────────╯ +$group +| polars agg [ + (polars col int_1 | polars n-unique) + (polars col int_2 | polars min) + (polars col float_1 | polars sum) + (polars col float_2 | polars count) +] | polars sort-by first +# => ╭────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────╮ +# => │ plan │ SORT BY [col("first")] │ +# => │ │ AGGREGATE │ +# => │ │ [col("int_1").n_unique(), col("int_2").min(), col("float_1") │ +# => │ │ .sum(), col("float_2").count()] BY [col("first")] FROM │ +# => │ │ DF ["int_1", "int_2", "float_1", "float_2 │ +# => │ │ "]; PROJECT */8 COLUMNS; SELECTION: "None" │ +# => │ optimized_plan │ SORT BY [col("first")] │ +# => │ │ AGGREGATE │ +# => │ │ [col("int_1").n_unique(), col("int_2").min(), col("float_1") │ +# => │ │ .sum(), col("float_2").count()] BY [col("first")] FROM │ +# => │ │ DF ["int_1", "int_2", "float_1", "float_2 │ +# => │ │ "]; PROJECT 5/8 COLUMNS; SELECTION: "None" │ +# => ╰────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────╯ ``` 正如你所看到的,`GroupBy`对象是一个非常强大的变量,在你操作数据集时,它值得被保留在内存中。 ## 创建 DataFrames -也可以从基本的 Nushell 基础类型,如整数、小数或字符串,来构建 DataFrames。让我们使用 `into df` 命令来创建一个小的 DataFrame: +也可以从基本的 Nushell 基础类型,如整数、小数或字符串,来构建 dataframes。让我们使用 `polars into-df` 命令来创建一个小的 dataframe: ```nu -❯ let a = ([[a b]; [1 2] [3 4] [5 6]] | dfr into-df) -❯ $a -╭───┬───┬───╮ -│ # │ a │ b │ -├───┼───┼───┤ -│ 0 │ 1 │ 2 │ -│ 1 │ 3 │ 4 │ -│ 2 │ 5 │ 6 │ -╰───┴───┴───╯ +let df_3 = [[a b]; [1 2] [3 4] [5 6]] | polars into-df +$df_3 +# => ╭───┬───┬───╮ +# => │ # │ a │ b │ +# => ├───┼───┼───┤ +# => │ 0 │ 1 │ 2 │ +# => │ 1 │ 3 │ 4 │ +# => │ 2 │ 5 │ 6 │ +# => ╰───┴───┴───╯ ``` ::: tip -目前,并不是所有的 Nushell 基本类型都可以转换为 DataFrame。随着 DataFrame 功能的成熟,这一点将在未来发生变化。 +目前,并不是所有的 Nushell 基本类型都可以转换为 dataframe。随着 dataframe 功能的成熟,这一点将在未来发生变化。 ::: -我们可以在一个 DataFrame 中添加列,以创建一个新的变量。作为一个例子,让我们在迷你 DataFrame `$a` 上添加两列: +我们可以在一个 dataframe 中添加列,以创建一个新的变量。作为一个例子,让我们在迷你 dataframe `$df_3` 上添加两列: ```nu -❯ let a2 = ($a | dfr with-column $a.a --name a2 | dfr with-column $a.a --name a3) -❯ $a2 -╭───┬───┬───┬────┬────╮ -│ # │ a │ b │ a2 │ a3 │ -├───┼───┼───┼────┼────┤ -│ 0 │ 1 │ 2 │ 1 │ 1 │ -│ 1 │ 3 │ 4 │ 3 │ 3 │ -│ 2 │ 5 │ 6 │ 5 │ 5 │ -╰───┴───┴───┴────┴────╯ +let df_4 = $df_3 | polars with-column $df_3.a --name a2 | polars with-column $df_3.a --name a3 +$df_4 +# => ╭───┬───┬───┬────┬────╮ +# => │ # │ a │ b │ a2 │ a3 │ +# => ├───┼───┼───┼────┼────┤ +# => │ 0 │ 1 │ 2 │ 1 │ 1 │ +# => │ 1 │ 3 │ 4 │ 3 │ 3 │ +# => │ 2 │ 5 │ 6 │ 5 │ 5 │ +# => ╰───┴───┴───┴────┴────╯ ``` -Nushell 强大的管道语法允许我们通过从其他 DataFrame 中获取数据并将其附加到这些 DataFrame 中来创建新的 DataFrame。现在,如果你列出你的 DataFrame,你会看到总共有四个: +Nushell 强大的管道语法允许我们通过从其他 dataframes 中获取数据并将其附加到这些 dataframes 中来创建新的 dataframes。现在,如果你列出你的 dataframes,你会看到总共有五个: ```nu -❯ dfr ls -╭───┬───────┬─────────┬──────╮ -│ # │ name │ columns │ rows │ -├───┼───────┼─────────┼──────┤ -│ 0 │ $a2 │ 4 │ 3 │ -│ 1 │ $df_a │ 5 │ 4 │ -│ 2 │ $df │ 8 │ 10 │ -│ 3 │ $a │ 2 │ 3 │ -│ 4 │ $res │ 4 │ 1 │ -╰───┴───────┴─────────┴──────╯ +polars store-ls | select key type columns rows estimated_size +# => ╭──────────────────────────────────────┬─────────────┬─────────┬──────┬────────────────╮ +# => │ key │ type │ columns │ rows │ estimated_size │ +# => ├──────────────────────────────────────┼─────────────┼─────────┼──────┼────────────────┤ +# => │ e780af47-c106-49eb-b38d-d42d3946d66e │ DataFrame │ 8 │ 10 │ 403 B │ +# => │ 3146f4c1-f2a0-475b-a623-7375c1fdb4a7 │ DataFrame │ 4 │ 1 │ 32 B │ +# => │ 455a1483-e328-43e2-a354-35afa32803b9 │ DataFrame │ 5 │ 4 │ 132 B │ +# => │ 0d8532a5-083b-4f78-8f66-b5e6b59dc449 │ LazyGroupBy │ │ │ │ +# => │ 9504dfaf-4782-42d4-9110-9dae7c8fb95b │ DataFrame │ 2 │ 3 │ 48 B │ +# => │ 37ab1bdc-e1fb-426d-8006-c3f974764a3d │ DataFrame │ 4 │ 3 │ 96 B │ +# => ╰──────────────────────────────────────┴─────────────┴─────────┴──────┴────────────────╯ ``` -值得一提的是,在使用 DataFrame 时,内存是如何被优化的呢?这要感谢 **Apache Arrow** 和 **Polars**。在一个非常简单的表示中,DataFrame 中的每一列都是一个 Arrow 数组,它使用了几种内存规格,以塞满尽可能多的数据(查看 [Arrow 列格式](https://arrow.apache.org/docs/format/Columnar.html) );另一个优化技巧是,只要有可能,DataFrame 中的列就会在多个 DataFrame 之间共享,避免了相同数据的内存重复占用。这意味着 DataFrame `$a`和`$a2`共享我们用 `into df` 命令创建的两个列。由于这个原因,不能改变 DataFrame 中某一列的值。然而,你可以根据其他列或 DataFrame 的数据创建新的列。 +值得一提的是,在使用 dataframe 时,内存是如何被优化的呢?这要感谢 **Apache Arrow** 和 **Polars**。在一个非常简单的表示中,dataframe 中的每一列都是一个 Arrow 数组,它使用了几种内存规格,以塞满尽可能多的数据(查看 [Arrow 列格式](https://arrow.apache.org/docs/format/Columnar.html) );另一个优化技巧是,只要有可能,dataframe 中的列就会在多个 dataframes 之间共享,避免了相同数据的内存重复占用。这意味着 dataframe `$df_3`和`$df_4`共享我们用 `polars into-df` 命令创建的两个列。由于这个原因,不能改变 dataframe 中某一列的值。然而,你可以根据其他列或 dataframes 的数据创建新的列。 ## 使用系列 -系列(`Series`) 是 `DataFrame` 的基本组成部分。每个系列代表一个具有相同数据类型的列,我们可以创建多个不同类型的系列,如浮点、整型或字符串。 +`Series` 是 `DataFrame` 的基本组成部分。每个 Series 代表一个具有相同数据类型的列,我们可以创建多个不同类型的 Series,如浮点、整型或字符串。 -让我们通过使用 `into df` 命令创建一个系列,来开始我们对系列的探索: +让我们通过使用 `polars into-df` 命令创建一个系列,来开始我们对系列的探索: ```nu -❯ let new = ([9 8 4] | dfr into-df) -❯ $new -╭───┬───╮ -│ # │ 0 │ -├───┼───┤ -│ 0 │ 9 │ -│ 1 │ 8 │ -│ 2 │ 4 │ -╰───┴───╯ +let df_5 = [9 8 4] | polars into-df +$df_5 +# => ╭───┬───╮ +# => │ # │ 0 │ +# => ├───┼───┤ +# => │ 0 │ 9 │ +# => │ 1 │ 8 │ +# => │ 2 │ 4 │ +# => ╰───┴───╯ ``` 我们从一个整数列表创建了一个新的系列(我们也可以用浮点数或字符串做同样的事情)。 @@ -455,15 +453,15 @@ Nushell 强大的管道语法允许我们通过从其他 DataFrame 中获取数 系列已经定义了自己的基本操作,它们可以用来创建其他系列。让我们通过对先前创建的列进行一些运算来创建一个新的系列: ```nu -❯ let new_2 = ($new * 3 + 10) -❯ $new_2 -╭───┬────╮ -│ # │ 0 │ -├───┼────┤ -│ 0 │ 37 │ -│ 1 │ 34 │ -│ 2 │ 22 │ -╰───┴────╯ +let df_6 = $df_5 * 3 + 10 +$df_6 +# => ╭───┬────╮ +# => │ # │ 0 │ +# => ├───┼────┤ +# => │ 0 │ 37 │ +# => │ 1 │ 34 │ +# => │ 2 │ 22 │ +# => ╰───┴────╯ ``` 现在我们有一个新的系列,它是通过对前一个变量进行基本操作而构建的。 @@ -475,99 +473,99 @@ Nushell 强大的管道语法允许我们通过从其他 DataFrame 中获取数 让我们重新命名我们之前的系列为 `memorable` ```nu -❯ let new_2 = ($new_2 | dfr rename "0" memorable) -❯ $new_2 -╭───┬───────────╮ -│ # │ memorable │ -├───┼───────────┤ -│ 0 │ 37 │ -│ 1 │ 34 │ -│ 2 │ 22 │ -╰───┴───────────╯ +let df_7 = $df_6 | polars rename "0" memorable +$df_7 +# => ╭───┬───────────╮ +# => │ # │ memorable │ +# => ├───┼───────────┤ +# => │ 0 │ 37 │ +# => │ 1 │ 34 │ +# => │ 2 │ 22 │ +# => ╰───┴───────────╯ ``` 只要两个系列的数据类型相同,我们也可以对它们进行基本操作: ```nu -❯ $new - $new_2 -╭───┬─────────────────╮ -│ # │ sub_0_memorable │ -├───┼─────────────────┤ -│ 0 │ -28 │ -│ 1 │ -26 │ -│ 2 │ -18 │ -╰───┴─────────────────╯ +$df_5 - $df_7 +# => ╭───┬─────────────────╮ +# => │ # │ sub_0_memorable │ +# => ├───┼─────────────────┤ +# => │ 0 │ -28 │ +# => │ 1 │ -26 │ +# => │ 2 │ -18 │ +# => ╰───┴─────────────────╯ ``` 而且我们可以将它们添加到先前定义的 DataFrames 中: ```nu -❯ let new_df = ($a | dfr with-column $new --name new_col) -❯ $new_df -╭───┬───┬───┬─────────╮ -│ # │ a │ b │ new_col │ -├───┼───┼───┼─────────┤ -│ 0 │ 1 │ 2 │ 9 │ -│ 1 │ 3 │ 4 │ 8 │ -│ 2 │ 5 │ 6 │ 4 │ -╰───┴───┴───┴─────────╯ +let df_8 = $df_3 | polars with-column $df_5 --name new_col +$df_8 +# => ╭───┬───┬───┬─────────╮ +# => │ # │ a │ b │ new_col │ +# => ├───┼───┼───┼─────────┤ +# => │ 0 │ 1 │ 2 │ 9 │ +# => │ 1 │ 3 │ 4 │ 8 │ +# => │ 2 │ 5 │ 6 │ 4 │ +# => ╰───┴───┴───┴─────────╯ ``` 存储在 DataFrame 中的系列也可以直接使用,例如,我们可以将列`a`和`b`相乘来创建一个新系列: ```nu -❯ $new_df.a * $new_df.b -╭───┬─────────╮ -│ # │ mul_a_b │ -├───┼─────────┤ -│ 0 │ 2 │ -│ 1 │ 12 │ -│ 2 │ 30 │ -╰───┴─────────╯ +$df_8.a * $df_8.b +# => ╭───┬─────────╮ +# => │ # │ mul_a_b │ +# => ├───┼─────────┤ +# => │ 0 │ 2 │ +# => │ 1 │ 12 │ +# => │ 2 │ 30 │ +# => ╰───┴─────────╯ ``` 我们可以开始使用管道,以创建新的列和 DataFrames: ```nu -❯ let $new_df = ($new_df | dfr with-column ($new_df.a * $new_df.b / $new_df.new_col) --name my_sum) -❯ $new_df -╭───┬───┬───┬─────────┬────────╮ -│ # │ a │ b │ new_col │ my_sum │ -├───┼───┼───┼─────────┼────────┤ -│ 0 │ 1 │ 2 │ 9 │ 0 │ -│ 1 │ 3 │ 4 │ 8 │ 1 │ -│ 2 │ 5 │ 6 │ 4 │ 7 │ -╰───┴───┴───┴─────────┴────────╯ +let df_9 = $df_8 | polars with-column ($df_8.a * $df_8.b / $df_8.new_col) --name my_sum +$df_9 +# => ╭───┬───┬───┬─────────┬────────╮ +# => │ # │ a │ b │ new_col │ my_sum │ +# => ├───┼───┼───┼─────────┼────────┤ +# => │ 0 │ 1 │ 2 │ 9 │ 0 │ +# => │ 1 │ 3 │ 4 │ 8 │ 1 │ +# => │ 2 │ 5 │ 6 │ 4 │ 7 │ +# => ╰───┴───┴───┴─────────┴────────╯ ``` Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 ## 系列和掩码 -在使用 DataFrames 时,系列还有另一个关键用途,那就是我们可以用它们来建立布尔掩码(Mask)。让我们先用等于运算符创建一个简单的掩码: +系列在使用 `DataFrames` 时还有另一个关键用途,那就是我们可以用它们来建立布尔掩码(Mask)。让我们先用等于运算符创建一个简单的掩码: ```nu -❯ let mask = ($new == 8) -❯ $mask -╭───┬───────╮ -│ # │ 0 │ -├───┼───────┤ -│ 0 │ false │ -│ 1 │ true │ -│ 2 │ false │ -╰───┴───────╯ +let mask_0 = $df_5 == 8 +$mask_0 +# => ╭───┬───────╮ +# => │ # │ 0 │ +# => ├───┼───────┤ +# => │ 0 │ false │ +# => │ 1 │ true │ +# => │ 2 │ false │ +# => ╰───┴───────╯ ``` 有了这个掩码,我们现在可以过滤一个 DataFrame,像这样: ```nu -❯ $new_df | dfr filter-with $mask -╭───┬───┬───┬─────────┬────────╮ -│ # │ a │ b │ new_col │ my_sum │ -├───┼───┼───┼─────────┼────────┤ -│ 0 │ 3 │ 4 │ 8 │ 1 │ -╰───┴───┴───┴─────────┴────────╯ +$df_9 | polars filter-with $mask_0 +# => ╭───┬───┬───┬─────────┬────────╮ +# => │ # │ a │ b │ new_col │ my_sum │ +# => ├───┼───┼───┼─────────┼────────┤ +# => │ 0 │ 3 │ 4 │ 8 │ 1 │ +# => ╰───┴───┴───┴─────────┴────────╯ ``` 现在我们有一个新的 DataFrame,其中只有掩码为真的值。 @@ -575,95 +573,89 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 掩码也可以从 Nushell 列表中创建,比如: ```nu -❯ let mask1 = ([true true false] | dfr into-df) -❯ $new_df | dfr filter-with $mask1 -╭───┬───┬───┬─────────┬────────╮ -│ # │ a │ b │ new_col │ my_sum │ -├───┼───┼───┼─────────┼────────┤ -│ 0 │ 1 │ 2 │ 9 │ 0 │ -│ 1 │ 3 │ 4 │ 8 │ 1 │ -╰───┴───┴───┴─────────┴────────╯ +let mask_1 = ([true true false] | polars into-df) +$df_9 | polars filter-with $mask_1 +# => ╭───┬───┬───┬─────────┬────────╮ +# => │ # │ a │ b │ new_col │ my_sum │ +# => ├───┼───┼───┼─────────┼────────┤ +# => │ 0 │ 1 │ 2 │ 9 │ 0 │ +# => │ 1 │ 3 │ 4 │ 8 │ 1 │ +# => ╰───┴───┴───┴─────────┴────────╯ ``` 为了创建复杂的掩码,我们可以使用`AND`: ```nu -❯ $mask and $mask1 -╭───┬─────────╮ -│ # │ and_0_0 │ -├───┼─────────┤ -│ 0 │ false │ -│ 1 │ true │ -│ 2 │ false │ -╰───┴─────────╯ +$mask_0 and $mask_1 +# => ╭───┬─────────╮ +# => │ # │ and_0_0 │ +# => ├───┼─────────┤ +# => │ 0 │ false │ +# => │ 1 │ true │ +# => │ 2 │ false │ +# => ╰───┴─────────╯ ``` 或者 `OR` 操作: ```nu -❯ $mask or $mask1 -╭───┬────────╮ -│ # │ or_0_0 │ -├───┼────────┤ -│ 0 │ true │ -│ 1 │ true │ -│ 2 │ false │ -╰───┴────────╯ +$mask_0 or $mask_1 +# => ╭───┬────────╮ +# => │ # │ or_0_0 │ +# => ├───┼────────┤ +# => │ 0 │ true │ +# => │ 1 │ true │ +# => │ 2 │ false │ +# => ╰───┴────────╯ ``` 我们也可以通过检查某些值是否存在于其他系列来创建一个掩码。使用我们创建的第一个 DataFrame,我们可以这样做: ```nu -❯ let mask3 = ($df | dfr col first | dfr is-in [b c]) -❯ $mask3 -╭──────────┬─────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ │ ╭───┬─────────┬──────────────╮ │ -│ input │ │ # │ expr │ value │ │ -│ │ ├───┼─────────┼──────────────┤ │ -│ │ │ 0 │ column │ first │ │ -│ │ │ 1 │ literal │ Series[list] │ │ -│ │ ╰───┴─────────┴──────────────╯ │ -│ function │ IsIn │ -│ options │ FunctionOptions { collect_groups: ApplyFlat, input_wildcard_expansion: false, auto_explode: tru │ -│ │ e, fmt_str: "", cast_to_supertypes: true, allow_rename: false, pass_name_to_apply: false } │ -╰──────────┴─────────────────────────────────────────────────────────────────────────────────────────────────╯ +let mask_2 = ($df_1 | polars col first | polars is-in [b c]) +$mask_2 +# => ╭──────────┬─────────────────────────╮ +# => │ input │ [table 2 rows] │ +# => │ function │ Boolean(IsIn) │ +# => │ options │ FunctionOptions { ... } │ +# => ╰──────────┴─────────────────────────╯ ``` 而这个新的掩码可以用来过滤 DataFrame ```nu -❯ $df | dfr filter-with $mask3 -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ -│ 1 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ -│ 2 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ -│ 3 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ -│ 4 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ -│ 5 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ -│ 6 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +$df_1 | polars filter-with $mask_2 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ +# => │ 1 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ +# => │ 2 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ +# => │ 3 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ +# => │ 4 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ +# => │ 5 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ +# => │ 6 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` 另一个可以用掩码进行的操作是设置或替换一个系列的值。例如,我们可以改变列`first`中的值,如果该值包含`a`: ```nu -❯ $df | dfr get first | dfr set new --mask ($df.first =~ a) -╭───┬────────╮ -│ # │ string │ -├───┼────────┤ -│ 0 │ new │ -│ 1 │ new │ -│ 2 │ new │ -│ 3 │ b │ -│ 4 │ b │ -│ 5 │ b │ -│ 6 │ b │ -│ 7 │ c │ -│ 8 │ c │ -│ 9 │ c │ -╰───┴────────╯ +$df_1 | polars get first | polars set new --mask ($df_1.first =~ a) +# => ╭───┬────────╮ +# => │ # │ string │ +# => ├───┼────────┤ +# => │ 0 │ new │ +# => │ 1 │ new │ +# => │ 2 │ new │ +# => │ 3 │ b │ +# => │ 4 │ b │ +# => │ 5 │ b │ +# => │ 6 │ b │ +# => │ 7 │ c │ +# => │ 8 │ c │ +# => │ 9 │ c │ +# => ╰───┴────────╯ ``` ## 系列作为索引 @@ -671,30 +663,30 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 系列也可以作为过滤 DataFrame 的一种方式,将它们作为索引列表使用。例如,假设我们想从原始 DataFrame 中获取第1、4和6行。针对这一点,我们可以使用以下命令来提取这些信息: ```nu -❯ let indices = ([1 4 6] | dfr into-df) -❯ $df | dfr take $indices -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ -│ 1 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ -│ 2 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +let indices_0 = ([1 4 6] | polars into-df) +$df_1 | polars take $indices_0 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ +# => │ 1 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ +# => │ 2 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` -命令 `dfr take` 非常方便,特别是当我们把它与其他命令混合使用时。 -假设我们想提取 `first` 列中含有第一个重复的元素的的所有记录。为了做到这一点,我们可以使用 `arg-unique` 命令,如下例所示: +命令 [`polars take`](/commands/docs/polars_take.md) 非常方便,特别是当我们把它与其他命令混合使用时。 +假设我们想提取 `first` 列中含有第一个重复的元素的所有记录。为了做到这一点,我们可以使用 `polars arg-unique` 命令,如下例所示: ```nu -❯ let indices = ($df | dfr get first | dfr arg-unique) -❯ $df | dfr take $indices -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ -│ 1 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ -│ 2 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +let indices_1 = ($df_1 | polars get first | polars arg-unique) +$df_1 | polars take $indices_1 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ +# => │ 1 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ +# => │ 2 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` 或者,如果我们想使用一个特定的列来创建一个新的有序 DataFrame,该怎么办?我们可以使用`arg-sort`来完成这个任务。在下一个例子中,我们可以通过`word`列对 DataFrame 进行排序: @@ -704,43 +696,43 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 ::: ```nu -❯ let indices = ($df | dfr get word | dfr arg-sort) -❯ $df | dfr take $indices -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ -│ 1 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ -│ 2 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ -│ 3 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ -│ 4 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ -│ 5 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ -│ 6 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ -│ 7 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ -│ 8 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ -│ 9 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +let indices_2 = ($df_1 | polars get word | polars arg-sort) +$df_1 | polars take $indices_2 +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ +# => │ 1 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ +# => │ 2 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ +# => │ 3 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ +# => │ 4 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ +# => │ 5 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ +# => │ 6 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ +# => │ 7 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ +# => │ 8 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ +# => │ 9 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` 最后,我们可以通过在标记的索引中设置一个新值来创建新的系列。请看下一条命令: ```nu -❯ let indices = ([0 2] | dfr into-df); -❯ $df | dfr get int_1 | dfr set-with-idx 123 --indices $indices -╭───┬───────╮ -│ # │ int_1 │ -├───┼───────┤ -│ 0 │ 123 │ -│ 1 │ 2 │ -│ 2 │ 123 │ -│ 3 │ 4 │ -│ 4 │ 0 │ -│ 5 │ 6 │ -│ 6 │ 7 │ -│ 7 │ 8 │ -│ 8 │ 9 │ -│ 9 │ 0 │ -╰───┴───────╯ +let indices_3 = ([0 2] | polars into-df) +$df_1 | polars get int_1 | polars set-with-idx 123 --indices $indices_3 +# => ╭───┬───────╮ +# => │ # │ int_1 │ +# => ├───┼───────┤ +# => │ 0 │ 123 │ +# => │ 1 │ 2 │ +# => │ 2 │ 123 │ +# => │ 3 │ 4 │ +# => │ 4 │ 0 │ +# => │ 5 │ 6 │ +# => │ 6 │ 7 │ +# => │ 7 │ 8 │ +# => │ 8 │ 9 │ +# => │ 9 │ 0 │ +# => ╰───┴───────╯ ``` ## 唯一值 @@ -750,14 +742,14 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 第一个也是最常见的操作是`value_counts`。这个命令计算出一个系列中存在的唯一值的数量。例如,我们可以用它来计算 `first` 列各值的出现次数: ```nu -❯ $df | dfr get first | dfr value-counts -╭───┬───────┬────────╮ -│ # │ first │ counts │ -├───┼───────┼────────┤ -│ 0 │ b │ 4 │ -│ 1 │ a │ 3 │ -│ 2 │ c │ 3 │ -╰───┴───────┴────────╯ +$df_1 | polars get first | polars value-counts +# => ╭───┬───────┬───────╮ +# => │ # │ first │ count │ +# => ├───┼───────┼───────┤ +# => │ 0 │ a │ 3 │ +# => │ 1 │ b │ 4 │ +# => │ 2 │ c │ 3 │ +# => ╰───┴───────┴───────╯ ``` 正如预期的那样,该命令返回一个新的 DataFrame,可以用来做更多的查询。 @@ -765,44 +757,44 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 继续我们对 `Series` 的探索,我们要做的下一件事是只从一个系列中获得唯一值,像这样: ```nu -❯ $df | dfr get first | dfr unique -╭───┬───────╮ -│ # │ first │ -├───┼───────┤ -│ 0 │ c │ -│ 1 │ b │ -│ 2 │ a │ -╰───┴───────╯ +$df_1 | polars get first | polars unique +# => ╭───┬───────╮ +# => │ # │ first │ +# => ├───┼───────┤ +# => │ 0 │ a │ +# => │ 1 │ b │ +# => │ 2 │ c │ +# => ╰───┴───────╯ ``` 或者我们可以得到一个掩码,用来过滤出数据唯一或重复的行。例如,我们可以选择列 `word` 中含唯一值的行: ```nu -❯ $df | dfr filter-with ($df | dfr get word | dfr is-unique) -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬───────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼───────┤ -│ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ -│ 1 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴───────╯ +$df_1 | polars filter-with ($in.word | polars is-unique) +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬───────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼───────┤ +# => │ 0 │ 1 │ 11 │ 0.10 │ 1.00 │ a │ b │ c │ first │ +# => │ 1 │ 8 │ 18 │ 0.80 │ 7.00 │ c │ c │ b │ eight │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴───────╯ ``` 或所有含重复值的行: ```nu -❯ $df | dfr filter-with ($df | dfr get word | dfr is-duplicated) -╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ -│ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ -├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ -│ 0 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ -│ 1 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ -│ 2 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ -│ 3 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ -│ 4 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ -│ 5 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ -│ 6 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ -│ 7 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ -╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ +$df_1 | polars filter-with ($in.word | polars is-duplicated) +# => ╭───┬───────┬───────┬─────────┬─────────┬───────┬────────┬───────┬────────╮ +# => │ # │ int_1 │ int_2 │ float_1 │ float_2 │ first │ second │ third │ word │ +# => ├───┼───────┼───────┼─────────┼─────────┼───────┼────────┼───────┼────────┤ +# => │ 0 │ 2 │ 12 │ 0.20 │ 1.00 │ a │ b │ c │ second │ +# => │ 1 │ 3 │ 13 │ 0.30 │ 2.00 │ a │ b │ c │ third │ +# => │ 2 │ 4 │ 14 │ 0.40 │ 3.00 │ b │ a │ c │ second │ +# => │ 3 │ 0 │ 15 │ 0.50 │ 4.00 │ b │ a │ a │ third │ +# => │ 4 │ 6 │ 16 │ 0.60 │ 5.00 │ b │ a │ a │ second │ +# => │ 5 │ 7 │ 17 │ 0.70 │ 6.00 │ b │ c │ a │ third │ +# => │ 6 │ 9 │ 19 │ 0.90 │ 8.00 │ c │ c │ b │ ninth │ +# => │ 7 │ 0 │ 10 │ 0.00 │ 9.00 │ c │ c │ b │ ninth │ +# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯ ``` ## 惰性 Dataframe @@ -813,29 +805,27 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 让我们创建一个惰性 Dataframe 的小例子: ```nu -❯ let a = ([[a b]; [1 a] [2 b] [3 c] [4 d]] | dfr into-lazy) -❯ $a -╭────────────────┬─────────────────────────────────────────────────────────╮ -│ plan │ DF ["a", "b"]; PROJECT */2 COLUMNS; SELECTION: "None" │ -│ │ │ -│ optimized_plan │ DF ["a", "b"]; PROJECT */2 COLUMNS; SELECTION: "None" │ -│ │ │ -╰────────────────┴─────────────────────────────────────────────────────────╯ +let lf_0 = [[a b]; [1 a] [2 b] [3 c] [4 d]] | polars into-lazy +$lf_0 +# => ╭────────────────┬───────────────────────────────────────────────────────╮ +# => │ plan │ DF ["a", "b"]; PROJECT */2 COLUMNS; SELECTION: "None" │ +# => │ optimized_plan │ DF ["a", "b"]; PROJECT */2 COLUMNS; SELECTION: "None" │ +# => ╰────────────────┴───────────────────────────────────────────────────────╯ ``` 正如你所看到的,产生的 Dataframe 还没有被评估,它以一组可以对数据进行操作的指令的形式存在。 如果你要收集这个 Dataframe,你会得到如下结果: ```nu -❯ $a | dfr collect -╭───┬───┬───╮ -│ # │ a │ b │ -├───┼───┼───┤ -│ 0 │ 1 │ a │ -│ 1 │ 2 │ b │ -│ 2 │ 3 │ c │ -│ 3 │ 4 │ d │ -╰───┴───┴───╯ +$lf_0 | polars collect +# => ╭───┬───┬───╮ +# => │ # │ a │ b │ +# => ├───┼───┼───┤ +# => │ 0 │ 1 │ a │ +# => │ 1 │ 2 │ b │ +# => │ 2 │ 3 │ c │ +# => │ 3 │ 4 │ d │ +# => ╰───┴───┴───╯ ``` 正如你所看到的,`collect` 命令执行了计划并为你创建了一个 Nushell 表。 @@ -845,91 +835,111 @@ Nushell 的管道系统可以帮助你创建非常有趣的工作流程。 要找到所有惰性 Dataframe 操作,你可以使用: -```nu -scope commands | where category =~ lazyframe +```nu no-run +scope commands | where category =~ lazyframe | select name category usage ``` 在定义了你的惰性 Dataframe 后,我们可以开始对它进行链式操作。例如: ```nu -❯ $a | -∙ dfr reverse | -∙ dfr with-column [ -∙ ((dfr col a) * 2 | dfr as double_a) -∙ ((dfr col a) / 2 | dfr as half_a) -∙ ] | dfr collect -╭───┬───┬───┬──────────┬────────╮ -│ # │ a │ b │ double_a │ half_a │ -├───┼───┼───┼──────────┼────────┤ -│ 0 │ 4 │ d │ 8 │ 2 │ -│ 1 │ 3 │ c │ 6 │ 1 │ -│ 2 │ 2 │ b │ 4 │ 1 │ -│ 3 │ 1 │ a │ 2 │ 0 │ -╰───┴───┴───┴──────────┴────────╯ +$lf_0 +| polars reverse +| polars with-column [ + ((polars col a) * 2 | polars as double_a) + ((polars col a) / 2 | polars as half_a) +] +| polars collect +# => ╭───┬───┬───┬──────────┬────────╮ +# => │ # │ a │ b │ double_a │ half_a │ +# => ├───┼───┼───┼──────────┼────────┤ +# => │ 0 │ 4 │ d │ 8 │ 2 │ +# => │ 1 │ 3 │ c │ 6 │ 1 │ +# => │ 2 │ 2 │ b │ 4 │ 1 │ +# => │ 3 │ 1 │ a │ 2 │ 0 │ +# => ╰───┴───┴───┴──────────┴────────╯ ``` :::tip 你可以使用行缓存编辑器来轻松地格式化你的查询(ctr + o) ::: -这个查询使用惰性 `reverse` 命令来反转 Dataframe,使用 `dfr with-column` 命令并使用 `expressions` 来创建新的两列。 +这个查询使用惰性 `reverse` 命令来反转 Dataframe,使用 `polars with-column` 命令并使用 `expressions` 来创建新的两列。 `expression` 用于定义在惰性 Dataframe 上执行的操作。当它们组合在一起时,就形成了整个由惰性命令来查询数据的 指令集。要列出所有产生表达式的命令,你可以使用: -```nu -❯ scope commands | where category =~ expression +```nu no-run +scope commands | where category =~ expression | select name category usage ``` -在我们前面的例子中,我们使用 `dfr col` 命令来表示列 `a` 将被乘以2,然后它将被命名为 `double_a`。 -在某些情况下,可以推断出 `dfr col` 命令的使用,例如,使用 `dfr select` 命令,我们可以只使用一个字符串: +在我们前面的例子中,我们使用 `polars col` 命令来表示列 `a` 将被乘以2,然后它将被命名为 `double_a`。 +在某些情况下,可以推断出 `polars col` 命令的使用,例如,使用 `polars select` 命令,我们可以只使用一个字符串: ```nu -❯ $a | dfr select a | dfr collect +$lf_0 | polars select a | polars collect +# => ╭───┬───╮ +# => │ # │ a │ +# => ├───┼───┤ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ 4 │ +# => ╰───┴───╯ ``` -或者使用 `dfr col` 命令: +或者使用 `polars col` 命令: ```nu -❯ $a | dfr select (dfr col a) | dfr collect +$lf_0 | polars select (polars col a) | polars collect +# => ╭───┬───╮ +# => │ # │ a │ +# => ├───┼───┤ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ 4 │ +# => ╰───┴───╯ ``` 让我们尝试更复杂的东西,从一个惰性 Dataframe 中创建聚合: ```nu -❯ let a = ( [[name value]; [one 1] [two 2] [one 1] [two 3]] | dfr into-lazy ) -❯ $a | -∙ dfr group-by name | -∙ dfr agg [ -∙ (dfr col value | dfr sum | dfr as sum) -∙ (dfr col value | dfr mean | dfr as mean) -∙ ] | dfr collect -╭───┬──────┬─────┬──────╮ -│ # │ name │ sum │ mean │ -├───┼──────┼─────┼──────┤ -│ 0 │ two │ 5 │ 2.50 │ -│ 1 │ one │ 2 │ 1.00 │ -╰───┴──────┴─────┴──────╯ +let lf_1 = [[name value]; [one 1] [two 2] [one 1] [two 3]] | polars into-lazy + +$lf_1 +| polars group-by name +| polars agg [ + (polars col value | polars sum | polars as sum) + (polars col value | polars mean | polars as mean) +] +| polars collect +# => ╭───┬──────┬─────┬──────╮ +# => │ # │ name │ sum │ mean │ +# => ├───┼──────┼─────┼──────┤ +# => │ 0 │ two │ 5 │ 2.50 │ +# => │ 1 │ one │ 2 │ 1.00 │ +# => ╰───┴──────┴─────┴──────╯ ``` 我们可以在一个还没有被收集的惰性 Dataframe 上进行连接操作。让我们把产生的分组连接到原来的惰性 Dataframe 中去吧 ```nu -❯ let a = ( [[name value]; [one 1] [two 2] [one 1] [two 3]] | dfr into-lazy ) -❯ let group = ($a -∙ | dfr group-by name -∙ | dfr agg [ -∙ (dfr col value | dfr sum | dfr as sum) -∙ (dfr col value | dfr mean | dfr as mean) -∙ ]) -❯ $a | dfr join $group name name | dfr collect -╭───┬──────┬───────┬─────┬──────╮ -│ # │ name │ value │ sum │ mean │ -├───┼──────┼───────┼─────┼──────┤ -│ 0 │ one │ 1 │ 2 │ 1.00 │ -│ 1 │ two │ 2 │ 5 │ 2.50 │ -│ 2 │ one │ 1 │ 2 │ 1.00 │ -│ 3 │ two │ 3 │ 5 │ 2.50 │ -╰───┴──────┴───────┴─────┴──────╯ +let lf_2 = [[name value]; [one 1] [two 2] [one 1] [two 3]] | polars into-lazy +let group = $lf_2 + | polars group-by name + | polars agg [ + (polars col value | polars sum | polars as sum) + (polars col value | polars mean | polars as mean) + ] + +$lf_2 | polars join $group name name | polars collect +# => ╭───┬──────┬───────┬─────┬──────╮ +# => │ # │ name │ value │ sum │ mean │ +# => ├───┼──────┼───────┼─────┼──────┤ +# => │ 0 │ one │ 1 │ 2 │ 1.00 │ +# => │ 1 │ two │ 2 │ 5 │ 2.50 │ +# => │ 2 │ one │ 1 │ 2 │ 1.00 │ +# => │ 3 │ two │ 3 │ 5 │ 2.50 │ +# => ╰───┴──────┴───────┴─────┴──────╯ ``` 正如你所看到的,惰性 Dataframe 是一个强大的结构,它可以让你使用灵活的语法来查询数据,从而极快地获得结果。 @@ -942,67 +952,125 @@ scope commands | where category =~ lazyframe ::: warning 此列表可能已过时。要获取最新的命令列表,请参阅 -[Dataframe](/commands/categories/dataframe.md) -[Lazyframe](/commands/categories/lazyframe.md) 和 -[Dataframe Or Lazyframe](/commands/categories/dataframe_or_lazyframe.md) +[Dataframe](/commands/categories/dataframe.md), [Lazyframe](/commands/categories/lazyframe.md), [Dataframe Or Lazyframe](/commands/categories/dataframe_or_lazyframe.md), [Expressions](/commands/categories/expression.html) 命令类别。 ::: -| 命令名 | 应用于 | 描述 | Nushell 类似命令 | -| --------------- | --------------------------- | -------------------------------------------------- | ----------------------------- | -| aggregate | DataFrame, GroupBy, Series | 在一个 DataFrame、GroupBy 或系列对象上执行聚合操作 | math | -| all-false | Series | 如果所有的值都是假的,则返回真 | | -| all-true | Series | 如果所有的值都是真的,则返回真 | all | -| arg-max | Series | 返回系列中最大值的索引 | | -| arg-min | Series | 返回系列中最小值的索引 | | -| arg-sort | Series | 返回排序后的系列的索引 | | -| arg-true | Series | 返回值为真的索引 | | -| arg-unique | Series | 返回唯一值的索引 | | -| count-null | Series | 计算空值 | | -| count-unique | Series | 计算唯一值 | | -| drop | DataFrame | 通过删除选定的列来创建一个新的 DataFrame | drop | -| drop-duplicates | DataFrame | 删除 DataFrame 中的重复值 | | -| drop-nulls | DataFrame, Series | 丢弃 DataFrame 中的空值 | | -| dtypes | DataFrame | 显示 DataFrame 的数据类型 | | -| filter-with | DataFrame | 使用 Mask 作为参考来过滤 DataFrame | | -| first | DataFrame | 用第一行创建新的 DataFrame | first | -| get | DataFrame | 用选定的列创建 DataFrame | get | -| group-by | DataFrame | 创建一个 GroupBy 对象,可用于其他聚合 | group-by | -| is-duplicated | Series | 创建表示重复值的 Mask | | -| is-in | Series | 检查一个系列的元素是否包含在右边的系列中 | in | -| is-not-null | Series | 创建值为非空的 Mask | | -| is-null | Series | 创建值为空的 Mask | ` == $nothing` | -| is-unique | Series | 创建表示唯一值的 Mask | | -| join | DataFrame | 使用列作为参考来连接一个 DataFrame | | -| last | DataFrame | 用最后几行创建新的 DataFrame | last | -| ls-df | | 列出已存储的 DataFrame | | -| melt | DataFrame | 将一个 DataFrame 从宽格式转为长格式 | | -| not | Series Inverts boolean mask | | -| open | | 从 csv 文件中加载 DataFrame | open | -| pivot | GroupBy | 在 GroupBy 对象上执行透视操作 | pivot | -| rename | Dataframe, Series | 重命名一个系列 | rename | -| sample | DataFrame | 创建样本 DataFrame | | -| select | DataFrame | 用选定的列创建一个新的 DataFrame | select | -| set | Series | 在给定的 Mask 为真时设置值 | | -| set-with-idx | Series | 设置给定索引中的值 | | -| shift | Series | 将值移到一个给定的时段 | | -| show | DataFrame | 将 DataFrame 的一个部分转换为一个表或列表值 | | -| slice | DataFrame | 从行的切片中创建新的 DataFrame | | -| sort-by | DataFrame, Series | 创建新的排序 DataFrame 或系列 | sort | -| take | DataFrame, Series | 使用给定的索引创建新的 DataFrame | | -| to csv | DataFrame | 将 DataFrame 保存为 csv 文件 | to csv | -| into df | | 将一个管道里的表或列表转换为 DataFrame | | -| dummies | DataFrame | 创建一个带有假值的新 DataFrame | | -| to parquet | DataFrame | 将 DataFrame 保存到 parquet 文件中 | | -| unique | Series | 返回一个系列中的唯一值 | uniq | -| value-counts | Series | 返回一个带有系列中唯一值的计数的 DataFrame | | -| where | DataFrame | 过滤 DataFrame 以符合条件 | where | -| with-column | DataFrame | 在 DataFrame 中添加一个系列 | `insert \| upsert { }` | - -## DataFrames 的未来 + + +| 命令名 | 应用于 | 描述 | Nushell 类似命令 | +| ---------------------- | --------------------- | ------------------------------------------------------------------------------------------------ | ----------------------- | +| polars agg | dataframe | 在一个 group-by 上执行一系列的聚合操作。 | math | +| polars agg-groups | expression | 创建一个 agg_groups 表达式。 | | +| polars all-false | dataframe | 如果所有的值都是假的,则返回真。 | | +| polars all-true | dataframe | 如果所有的值都是真的,则返回真。 | all | +| polars append | dataframe | 追加一个新的 dataframe。 | | +| polars arg-max | dataframe | 返回系列中最大值的索引。 | | +| polars arg-min | dataframe | 返回系列中最小值的索引。 | | +| polars arg-sort | dataframe | 返回排序后的系列的索引。 | | +| polars arg-true | dataframe | 返回值为真的索引。 | | +| polars arg-unique | dataframe | 返回唯一值的索引。 | | +| polars arg-where | any | 创建一个表达式,返回表达式为真的参数。 | | +| polars as | expression | 创建一个别名表达式。 | | +| polars as-date | dataframe | 将字符串转换为日期。 | | +| polars as-datetime | dataframe | 将字符串转换为日期时间。 | | +| polars cache | dataframe | 在一个新的 LazyFrame 中缓存操作。 | | +| polars cast | expression, dataframe | 将一列转换为不同的数据类型。 | | +| polars col | any | 创建一个命名的列表达式。 | | +| polars collect | dataframe | 将惰性 dataframe 收集到即时 dataframe 中。 | | +| polars columns | dataframe | 显示 dataframe 的列。 | | +| polars concat-str | any | 创建一个连接字符串表达式。 | | +| polars concatenate | dataframe | 将字符串与其他数组连接起来。 | | +| polars contains | dataframe | 检查一个模式是否包含在一个字符串中。 | | +| polars count | expression | 创建一个计数表达式。 | | +| polars count-null | dataframe | 计算空值。 | | +| polars cumulative | dataframe | 对一个系列进行累积计算。 | | +| polars datepart | expression | 创建一个表达式,用于捕获列中指定的日期部分。 | | +| polars drop | dataframe | 通过删除选定的列来创建一个新的 dataframe。 | drop | +| polars drop-duplicates | dataframe | 删除 dataframe 中的重复值。 | | +| polars drop-nulls | dataframe | 丢弃 dataframe 中的空值。 | | +| polars dummies | dataframe | 创建一个带有假值的新 dataframe。 | | +| polars explode | expression, dataframe | 展开一个 dataframe 或创建一个展开表达式。 | | +| polars expr-not | expression | 创建一个 not 表达式。 | | +| polars fetch | dataframe | 将 lazyframe 收集到选定的行。 | | +| polars fill-nan | dataframe | 用给定的表达式替换 NaN 值。 | | +| polars fill-null | dataframe | 用给定的表达式替换 NULL 值。 | | +| polars filter | dataframe | 基于表达式过滤 dataframe。 | | +| polars filter-with | dataframe | 使用掩码或表达式作为参考来过滤 dataframe。 | | +| polars first | expression, dataframe | 只显示前几行或创建一个 first 表达式。 | first | +| polars flatten | expression, dataframe | polars explode 的别名。 | | +| polars get | dataframe | 用选定的列创建 dataframe。 | get | +| polars get-day | dataframe | 从日期中获取天。 | | +| polars get-hour | dataframe | 从日期中获取小时。 | | +| polars get-minute | dataframe | 从日期中获取分钟。 | | +| polars get-month | dataframe | 从日期中获取月份。 | | +| polars get-nanosecond | dataframe | 从日期中获取纳秒。 | | +| polars get-ordinal | dataframe | 从日期中获取序数。 | | +| polars get-second | dataframe | 从日期中获取秒。 | | +| polars get-week | dataframe | 从日期中获取星期。 | | +| polars get-weekday | dataframe | 从日期中获取星期几。 | | +| polars get-year | dataframe | 从日期中获取年份。 | | +| polars group-by | dataframe | 创建一个 group-by 对象,可用于其他聚合。 | group-by | +| polars implode | expression | 将一个组聚合到一个系列中。 | | +| polars into-df | any | 将一个列表、表或记录转换为 dataframe。 | | +| polars into-lazy | any | 将一个 dataframe 转换为惰性 dataframe。 | | +| polars into-nu | expression, dataframe | 将一个 dataframe 或表达式转换为 nushell 值以供访问和探索。 | | +| polars is-duplicated | dataframe | 创建表示重复值的掩码。 | | +| polars is-in | expression, dataframe | 创建一个 is-in 表达式或检查元素是否包含在右边的系列中。 | in | +| polars is-not-null | expression, dataframe | 创建值不为空的掩码。 | | +| polars is-null | expression, dataframe | 创建值为空的掩码。 | ` == null` | +| polars is-unique | dataframe | 创建表示唯一值的掩码。 | | +| polars join | dataframe | 将一个惰性 frame 与其他惰性 frame 连接。 | | +| polars last | expression, dataframe | 用尾部行创建新的 dataframe 或创建一个 last 表达式。 | last | +| polars lit | any | 创建一个字面量表达式。 | | +| polars lowercase | dataframe | 将列中的字符串转换为小写。 | | +| polars max | expression, dataframe | 创建一个 max 表达式或将列聚合到其最大值。 | | +| polars mean | expression, dataframe | 创建一个用于聚合的 mean 表达式或将列聚合到其平均值。 | | +| polars median | expression, dataframe | dataframe 中列的中值或为聚合创建表达式。 | | +| polars melt | dataframe | 将一个 DataFrame 从宽格式转为长格式。 | | +| polars min | expression, dataframe | 创建一个 min 表达式或将列聚合到其最小值。 | | +| polars n-unique | expression, dataframe | 计算唯一值。 | | +| polars not | dataframe | 反转布尔掩码。 | | +| polars open | any | 打开 CSV、JSON、JSON lines、arrow、avro 或 parquet 文件以创建 dataframe。 | open | +| polars otherwise | any | 完成一个 when 表达式。 | | +| polars quantile | expression, dataframe | 将列聚合到选定的分位数。 | | +| polars query | dataframe | 使用 SQL 查询 dataframe。注意:在查询的 from 子句中,dataframe 总是命名为 'df'。 | | +| polars rename | dataframe | 重命名 dataframe 列。 | rename | +| polars replace | dataframe | 用正则表达式模式替换最左边的(子)字符串。 | | +| polars replace-all | dataframe | 用正则表达式模式替换所有(子)字符串。 | | +| polars reverse | dataframe | 反转 LazyFrame。 | | +| polars rolling | dataframe | 对一个系列进行滚动计算。 | | +| polars sample | dataframe | 创建样本 dataframe。 | | +| polars save | dataframe | 将 dataframe 保存到磁盘。对于惰性 dataframes,如果文件类型支持(parquet、ipc/arrow、csv 和 ndjson),将使用 sink 操作。| | +| polars schema | dataframe | 显示 dataframe 的模式。 | | +| polars select | dataframe | 从 lazyframe 中选择列。 | select | +| polars set | dataframe | 在给定的掩码为真时设置值。 | | +| polars set-with-idx | dataframe | 在给定的索引中设置值。 | | +| polars shape | dataframe | 显示 dataframe 的列和行大小。 | | +| polars shift | dataframe | 将值移到一个给定的时段。 | | +| polars slice | dataframe | 从行的切片中创建新的 dataframe。 | | +| polars sort-by | dataframe | 基于表达式对惰性 dataframe 进行排序。 | sort | +| polars std | expression, dataframe | 为 dataframe 中列的 std 值聚合创建一个 std 表达式。 | | +| polars store-get | any, any | 从插件缓存中获取 Dataframe 或其他对象。 | | +| polars store-ls | | 列出存储的 dataframes。 | | +| polars store-rm | any | 从插件缓存中删除存储的 Dataframe 或其他对象。 | | +| polars str-lengths | dataframe | 获取所有字符串的长度。 | | +| polars str-slice | dataframe | 从起始位置切片字符串直到选定的长度。 | | +| polars strftime | dataframe | 根据字符串规则格式化日期。 | | +| polars sum | expression, dataframe | 创建一个用于聚合的 sum 表达式或将列聚合到其总和值。 | | +| polars summary | dataframe | 对于一个 dataframe,为其数值列生成描述性统计(摘要统计)。 | | +| polars take | dataframe | 使用给定的索引创建新的 dataframe。 | | +| polars unique | dataframe | 返回 dataframe 中的唯一值。 | uniq | +| polars uppercase | dataframe | 将列中的字符串转换为大写。 | | +| polars value-counts | dataframe | 返回一个带有系列中唯一值的计数的 dataframe。 | | +| polars var | expression, dataframe | 为聚合创建一个 var 表达式。 | | +| polars when | expression | 创建和修改一个 when 表达式。 | | +| polars with-column | dataframe | 在 dataframe 中添加一个系列。 | `insert \| upsert { }` | + +## Dataframes 的未来 我们希望在本页结束时,你已经牢固掌握了如何使用 DataFrame 相关命令。正如你所看到的,它们提供了强大的操作,可以帮助你更快更原生地处理数据。 -然而,DataFrames 的未来仍然是非常实验性的,随着这些命令的成熟,新的命令和利用这些命令的工具将被加入。例如,DataFrames 的下一步是引入惰性 DataFrames,这将允许你定义复杂的数据操作,这些操作将在你决定 "**完成**" 这个管道时才被执行。这将使 Nushell 有机会选择最佳计划来查询你所要求的数据。 +然而,DataFrames 的未来仍然是非常实验性的,随着这些命令的成熟,新的命令和利用这些命令的工具将被加入。 -请继续访问本书,以查看 DataFrames 的最新情况,以及它们如何帮助你更快更有效地处理数据。 +请继续访问本章以及我们的[博客](/blog/),以了解 DataFrames 的最新情况,以及它们如何帮助你更快更有效地处理数据。 diff --git a/zh-CN/book/default_shell.md b/zh-CN/book/default_shell.md new file mode 100644 index 00000000000..8bdc65aa945 --- /dev/null +++ b/zh-CN/book/default_shell.md @@ -0,0 +1,31 @@ +# 默认 Shell + +## 在你的终端上将 Nu 设置为默认 shell + +| 终端 | 平台 | 说明 | +| :--------------: | ------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | +| GNOME Terminal | Linux & BSDs | 打开 `编辑 > 首选项`。在右侧面板中,选择 `命令` 选项卡,勾选 `运行自定义命令以代替我的 shell`,并将 `自定义命令` 设置为 Nu 的路径。 | +| GNOME Console | Linux & BSDs | 输入命令 `gsettings set org.gnome.Console shell "['/usr/bin/nu']"`(将 `/usr/bin/nu` 替换为 Nu 的路径)。或者,使用 [dconf 编辑器](https://apps.gnome.org/DconfEditor/) 编辑 `/org/gnome/Console/shell` 键。 | +| Kitty | Linux & BSDs | 按 `Ctrl`+`Shift`+`F2` 打开 `kitty.conf`。转到 `shell` 变量,取消注释该行并将 `.` 替换为 Nu 的路径。 | +| Konsole | Linux & BSDs | 打开 `设置 > 编辑当前配置文件`。将 `命令` 设置为 Nu 的路径。 | +| XFCE Terminal | Linux & BSDs | 打开 `编辑 > 首选项`。选中 `运行自定义命令以代替我的 shell`,并将 `自定义命令` 设置为 Nu 的路径。 | +| Terminal.app | macOS | 打开 `终端 > 偏好设置`。确保你在 `描述文件` 选项卡上,这应该是默认选项卡。在右侧面板中,选择 `Shell` 选项卡。勾选 `运行命令`,将 Nu 的路径放入文本框中,并取消勾选 `在 shell 内运行`。 | +| iTerm2 | macOS | 打开 `iTerm > 偏好设置`。选择 `描述文件` 选项卡。在右侧面板的 `命令` 下,将下拉菜单从 `登录 Shell` 更改为 `自定义 Shell`,并将 Nu 的路径放入文本框中。 | +| Windows Terminal | Windows | 按 `Ctrl`+`,` 打开 `设置`。转到 `添加新的配置文件 > 新建空配置文件`。填写“名称”并在“命令行”文本框中输入 Nu 的路径。转到 `启动` 选项并选择 Nu 作为“默认配置文件”。点击 `保存`。 | + +## 将 Nu 设置为登录 shell (Linux, BSD & macOS) + +::: warning +Nu 不打算与 POSIX 兼容。 +请注意,你系统上的某些程序(或其文档)可能假定你的登录 shell 与 [POSIX](https://en.wikipedia.org/wiki/POSIX) 兼容。 +打破该假设可能会导致意外问题。有关更多详细信息,请参阅[配置 - 登录 Shell](./configuration.md#configuring-nu-as-a-login-shell)。 +::: + +要设置登录 shell,你可以使用 [`chsh`](https://linux.die.net/man/1/chsh) 命令。 +一些 Linux 发行版在 `/etc/shells` 中有一个有效 shell 的列表,并且在 Nu 被列入白名单之前将不允许更改 shell。 +如果你尚未更新 `shells` 文件,你可能会看到类似下面的错误: + +@[code](@snippets/installation/chsh_invalid_shell_error.sh) + +你可以通过将你的 Nu 二进制文件附加到 `shells` 文件中,将 Nu 添加到允许的 shell 列表中。 +要添加的路径可以通过命令 `which nu` 找到,通常是 `$HOME/.cargo/bin/nu`。 diff --git a/zh-CN/book/design_notes.md b/zh-CN/book/design_notes.md new file mode 100644 index 00000000000..f76929645fa --- /dev/null +++ b/zh-CN/book/design_notes.md @@ -0,0 +1,7 @@ +# 设计说明 + +本章旨在深入介绍 Nushell 某些方面的设计原理。这些内容对于基础使用并非必需,但阅读它们将帮助你理解 Nushell 的工作原理及其设计初衷。 + +我们计划在未来扩展本章内容。如果你发现某些主题难以理解,请告诉我们。这些主题可能很适合添加到本章中。 + +[Nushell代码执行原理](how_nushell_code_gets_run.md)解释了当你运行Nushell源代码时会发生什么。它说明了Nushell在许多方面更接近传统的编译型语言(如C或Rust),而不是其他Shell和动态语言,希望能消除由此产生的一些困惑。 diff --git a/zh-CN/book/directory_stack.md b/zh-CN/book/directory_stack.md new file mode 100644 index 00000000000..5bc6292c5ba --- /dev/null +++ b/zh-CN/book/directory_stack.md @@ -0,0 +1,136 @@ +# 目录栈 + +与其他一些Shell类似,Nushell提供了目录栈功能,可以方便地在多个目录之间切换。在Nushell中,此功能是[标准库](./standard_library.md)的一部分,可以通过多种方式访问。 + +::: note +在Nushell中,"栈"以`list`形式表示,但整体功能与其他Shell类似。 +::: + +[[toc]] + +## `dirs`模块和命令 + +要使用`dirs`命令及其子命令,首先需要导入模块: + +```nu +use std/dirs +``` + +::: tip +要使此功能在每次启动Nushell时都可用,请将上述命令添加到你的[启动配置](./configuration.md)中。 +::: + +这将提供以下新命令: + +| 命令 | 描述 | +| ----------- | ------------------------------------------------------------------------------------------------ | +| `dirs` | 列出栈中的目录 | +| `dirs add` | 添加一个或多个目录到列表中。第一个列出的目录将成为新的活动目录。类似于其他Shell中的`pushd`命令。 | +| `dirs drop` | 从列表中移除当前目录。列表中的前一个目录将成为新的活动目录。类似于其他Shell中的`popd`命令。 | +| `dirs goto` | 根据索引跳转到列表中的目录 | +| `dirs next` | 将列表中的下一个目录设为活动目录。如果当前活动目录是列表中的最后一个,则循环到列表开头。 | +| `dirs prev` | 将列表中的前一个目录设为活动目录。如果当前活动目录是列表中的第一个,则循环到列表末尾。 | + +当我们开始使用`dirs`时,列表中只有一个目录,即活动目录。你仍然可以使用`cd`命令更改此目录。 + +```nu +cd ~ +use std/dirs +dirs +# => ╭───┬────────┬─────────────────────────────────╮ +# => │ # │ active │ path │ +# => ├───┼────────┼─────────────────────────────────┤ +# => │ 0 │ true │ /home/myuser │ +# => ╰───┴────────┴─────────────────────────────────╯ + +cd ~/src/repo/nushell +dirs +# => ╭───┬────────┬─────────────────────────────────╮ +# => │ # │ active │ path │ +# => ├───┼────────┼─────────────────────────────────┤ +# => │ 0 │ true │ /home/myuser/repo/nushell │ +# => ╰───┴────────┴─────────────────────────────────╯ +``` + +注意`cd`只会更改活动目录。 + +要*添加*当前目录到列表中,使用`dirs add`命令更改到新的活动目录: + +```nu +dirs add ../reedline +dirs +# => ╭───┬────────┬──────────────────────────────────╮ +# => │ # │ active │ path │ +# => ├───┼────────┼──────────────────────────────────┤ +# => │ 0 │ false │ /home/myuser/src/repo/nushell │ +# => │ 1 │ true │ /home/myuser/src/repo/reedline │ +# => ╰───┴────────┴──────────────────────────────────╯ +``` + +让我们继续添加几个常用目录到列表中: + +```nu +dirs add ../nu_scripts +dirs add ~ +dirs +# => ╭───┬────────┬────────────────────────────────────╮ +# => │ # │ active │ path │ +# => ├───┼────────┼────────────────────────────────────┤ +# => │ 0 │ false │ /home/myuser/src/repo/nushell │ +# => │ 1 │ false │ /home/myuser/src/repo/reedline │ +# => │ 2 │ false │ /home/myuser/src/repo/nu_scripts │ +# => │ 3 │ true │ /home/myuser │ +# => ╰───┴────────┴────────────────────────────────────╯ +``` + +现在我们可以使用`dirs next`、`dirs prev`或`dirs goto`轻松切换: + +```nu +dirs next +# Active was 3, is now 0 +pwd +# => /home/myuser/src/repo/nushell +dirs goto 2 +# => /home/myuser/src/repo/nu_scripts +``` + +当你完成某个目录的工作后,可以使用以下命令将其从列表中移除: + +```nu +dirs drop +dirs +# => ╭───┬────────┬──────────────────────────────────╮ +# => │ # │ active │ path │ +# => ├───┼────────┼──────────────────────────────────┤ +# => │ 0 │ false │ /home/myuser/src/repo/nushell │ +# => │ 1 │ true │ /home/myuser/src/repo/reedline │ +# => │ 2 │ false │ /home/myuser │ +# => ╰───┴────────┴──────────────────────────────────╯ +``` + +当我们从列表中移除`nu_scripts`时,前一个目录(`reedline`)变为活动目录。 + +## `shells`别名 + +一些用户可能更喜欢将此功能视为"Shell中的多个Shell",每个Shell都有自己的目录。 + +标准库提供了一组别名,可以替代上述`dirs`命令。 + +使用以下命令导入它们: + +```nu +use std/dirs shells-aliases * +``` + +内置别名包括: + +| 别名 | 描述 | +| -------- | ---------------------------------- | +| `shells` | 替代`dirs`列出当前"Shell"/目录 | +| `enter` | 替代`dirs add`进入新的"Shell"/目录 | +| `dexit` | 替代`dirs drop`退出"Shell"/目录 | +| `g` | `dirs goto`的别名 | +| `n` | `dirs next`的别名 | +| `p` | `dirs prev`的别名 | + +当然,你也可以根据需要定义自己的别名。 diff --git a/zh-CN/book/environment.md b/zh-CN/book/environment.md index ca41a6e6edd..b8b3c80e9b5 100644 --- a/zh-CN/book/environment.md +++ b/zh-CN/book/environment.md @@ -2,24 +2,32 @@ Shell 中的一个常见任务是控制外部应用程序将使用的环境变量。这通常是自动完成的,因为环境被打包,并在外部应用程序启动时提供给它。但有时,我们希望更精确地控制一个应用程序看到的环境变量。 -你可以使用`env`命令查看当前环境变量: +你可以通过 `$env` 变量查看当前的环境变量: -``` - # name type value raw -────────────────────────────────────────────────────────────────────────────────────────── - 16 DISPLAY string :0 :0 - 17 EDITOR string nvim nvim - 28 LANG string en_US.UTF-8 en_US.UTF-8 - 35 PATH list [list 16 items] /path1:/path2:/... - 36 PROMPT_COMMAND block -``` - -在 Nushell 中,环境变量可以是任何值,并且有任何类型(见`type`列)。 -Nushell 中使用的环境变量的实际值在`value`列下。 -你可以直接使用`$env`变量查询该值,例如,`$env.PATH | length`。 -最后的`raw`列显示了将被发送到外部应用程序的实际值(详见 [环境变量转换](environment.md#环境变量转换) )。 - -环境最初是由 Nu 的 [配置文件](configuration.md) 和 Nu 的运行环境创建的。 +```nu +$env | table -e +# => ╭──────────────────────────────────┬───────────────────────────────────────────╮ +# => │ │ ╭──────┬────────────────────────────────╮ │ +# => │ ENV_CONVERSIONS │ │ │ ╭─────────────┬──────────────╮ │ │ +# => │ │ │ PATH │ │ from_string │ │ │ │ +# => │ │ │ │ │ to_string │ │ │ │ +# => │ │ │ │ ╰─────────────┴──────────────╯ │ │ +# => │ │ │ │ ╭─────────────┬──────────────╮ │ │ +# => │ │ │ Path │ │ from_string │ │ │ │ +# => │ │ │ │ │ to_string │ │ │ │ +# => │ │ │ │ ╰─────────────┴──────────────╯ │ │ +# => │ │ ╰──────┴────────────────────────────────╯ │ +# => │ HOME │ /Users/jelle │ +# => │ LSCOLORS │ GxFxCxDxBxegedabagaced │ +# => | ... | ... | +# => ╰──────────────────────────────────┴───────────────────────────────────────────╯ +``` + +在 Nushell 中,环境变量可以是任何值,并且有任何类型。你可以使用 `describe` 命令查看环境变量的类型,例如:`$env.PROMPT_COMMAND | describe`。 + +要将环境变量发送到外部应用程序,需要将值转换为字符串。有关其工作原理,请参阅[环境变量转换](#环境变量转换)。 + +环境最初是由 Nu 的[配置文件](configuration.md)和 Nu 的运行环境创建的。 ## 设置环境变量 @@ -27,24 +35,24 @@ Nushell 中使用的环境变量的实际值在`value`列下。 ### $env.VAR 赋值 -使用`$env.VAR = "val"`命令是最直接的方法: +使用 `$env.VAR = "val"` 是最直接的方法: ```nu $env.FOO = 'BAR' ``` -因此,如果你想扩展`PATH`变量,你可以这样做: +因此,如果你想扩展 Windows 的 `Path` 变量,你可以这样做: ```nu -$env.PATH = ($env.PATH | prepend '/path/you/want/to/add') +$env.Path = ($env.Path | prepend 'C:\path\you\want\to\add') ``` -在这里,我们把指定文件夹前置添加到`PATH`中的现有路径中,所以它将有最高的优先级。 -如果你想给它最低的优先级,你可以使用`append`命令。 +在这里,我们把指定文件夹前置添加到`Path`中的现有路径中,所以它将有最高的优先级。 +如果你想给它最低的优先级,你可以使用[`append`](/commands/docs/append.md)命令。 ### [`load-env`](/commands/docs/load-env.md) -如果你有一个以上的环境变量需要设置,你可以使用`load-env`并创建一个键/值对记录(Record),以用于加载多个环境变量: +如果你有一个以上的环境变量需要设置,你可以使用[`load-env`](/commands/docs/load-env.md)并创建一个键/值对表格,以用于加载多个环境变量: ```nu load-env { "BOB": "FOO", "JAY": "BAR" } @@ -53,15 +61,15 @@ load-env { "BOB": "FOO", "JAY": "BAR" } ### 一次性环境变量 这些变量被定义为只在执行代码块的过程中临时有效。 -详情请看 [一次性环境变量](environment.md#一次性环境变量) 。 +详情请看[一次性环境变量](environment.md#一次性环境变量)。 ### 调用[`def --env`](/commands/docs/def.md)定义的命令 -详情见 [从自定义命令中定义环境](environment.md#从自定义命令中定义环境变量)。 +详情见[从自定义命令中定义环境](custom_commands.md#changing-the-environment-in-a-custom-command)。 ### 使用模块导出 -参见 [模块](modules.md#环境变量) 部分了解更多详情。 +参见[模块](modules.md)部分了解更多详情。 ## 读取环境变量 @@ -72,6 +80,45 @@ $env.FOO # => BAR ``` +有时,你可能想访问一个可能未设置的环境变量。考虑使用[问号操作符](types_of_data.md#optional-cell-paths)来避免错误: + +```nu +$env.FOO | describe +# => Error: nu::shell::column_not_found +# => +# => × Cannot find column +# => ╭─[entry #1:1:1] +# => 1 │ $env.FOO +# => · ──┬─ ─┬─ +# => · │ ╰── cannot find column 'FOO' +# => · ╰── value originates here +# => ╰──── + +$env.FOO? | describe +# => nothing + +$env.FOO? | default "BAR" +# => BAR +``` + +或者,你可以使用 `in` 来检查环境变量是否存在: + +```nu +$env.FOO +# => BAR + +if "FOO" in $env { + echo $env.FOO +} +# => BAR +``` + +### 大小写敏感性 + +无论操作系统如何,Nushell 的 `$env` 都是不区分大小写的。尽管 `$env` 的行为很像一个记录,但它的特殊之处在于它在读取或更新时会忽略大小写。这意味着,例如,你可以使用 `$env.PATH`、`$env.Path` 或 `$env.path` 中的任何一个,它们在任何操作系统上都以同样的方式工作。 + +如果你想以区分大小写的方式读取 `$env`,请使用 `$env | get --sensitive`。 + ## 作用域 当你设置环境变量时,它将只在当前作用域内可用(变量所在的块和它里面的任何块)。 @@ -89,6 +136,8 @@ $env.FOO == "BAR" # => true ``` +另请参阅:[在自定义命令中更改环境](./custom_commands.html#changing-the-environment-in-a-custom-command)。 + ## 目录切换 Shell 中常见的任务是用[`cd`](/commands/docs/cd.md)命令来改变目录。 @@ -100,14 +149,14 @@ Shell 中常见的任务是用[`cd`](/commands/docs/cd.md)命令来改变目录 在 Bash 和其他软件的启发下,有一个常用的简便方法,可以设置一次性环境变量: ```nu -FOO=BAR echo $env.FOO +FOO=BAR $env.FOO # => BAR ``` 你也可以使用[`with-env`](/commands/docs/with-env.md)来更明确地做同样的事情: ```nu -with-env { FOO: BAR } { echo $env.FOO } +with-env { FOO: BAR } { $env.FOO } # => BAR ``` @@ -124,22 +173,6 @@ with-env { FOO: BAR } { echo $env.FOO } $env.FOO = 'BAR' ``` -## 从自定义命令中定义环境变量 - -由于作用域规则,在自定义命令中定义的任何环境变量都只存在于该命令的作用域内。 -然而,用[`def --env`](/commands/docs/def.md)而不是[`def`](/commands/docs/def.md)定义的命令(它也适用于`export def`,见 [模块](modules.md))将在调用者一方保留环境设置: - -```nu -def --env foo [] { - $env.FOO = 'BAR' -} - -foo - -$env.FOO -# => BAR -``` - ## 环境变量转换 你可以通过设置`ENV_CONVERSIONS`环境变量,来在字符串和值之间转换其他环境变量。 @@ -190,20 +223,16 @@ nu -c '$env.FOO' 用`nu -c`运行命令不会加载配置文件,因此`FOO`的环境转换没有了,它被显示为一个普通的字符串 —— 这样我们可以验证转换是否成功。 你也可以通过`do $env.ENV_CONVERSIONS.FOO.to_string [a b c]`手动运行这个步骤。 -如果我们回头看一下`env`命令,`raw`列显示由`ENV_CONVERSIONS..to_string`翻译的值,`value`列显示 Nushell 中使用的值(在`FOO`的情况下是`ENV_CONVERSIONS..from_string`的结果)。 -如果这个值不是字符串,并且没有`to_string`的转换,它就不会被传递给外部(见`PROMPT_COMMAND`的`raw`列)。 -一个例外是`PATH`(Windows 上的`Path`)。默认情况下,它在启动时将字符串转换为列表,在运行外部程序时,如果没有指定手动转换,则从列表转换为字符串。 - _(重要! 环境转换字符串->值发生在 `env.nu` 和 `config.nu` 被运行**之后**。`env.nu` 和 `config.nu` 中的所有环境变量仍然是字符串,除非你手动将它们设置为一些其他的值。)_ ## 删除环境变量 -只有当一个环境变量被设置在当前作用域中时,你才能通过 [`hide`](/commands/docs/hide.md) 命令“删除”它: +只有当一个环境变量被设置在当前作用域中时,你才能通过 [`hide-env`](/commands/docs/hide-env.md) 命令“删除”它: ```nu $env.FOO = 'BAR' # => ... -hide FOO +hide-env FOO ``` 隐藏也是有作用域的,这既允许你暂时删除一个环境变量,又可以防止你从子作用域内修改父环境: @@ -211,11 +240,9 @@ hide FOO ```nu $env.FOO = 'BAR' do { - hide FOO - # $env.FOO does not exist + hide-env FOO + # $env.FOO does not exist } $env.FOO # => BAR ``` - -关于隐藏的更多细节,请参考 [模块](modules.md#隐藏) diff --git a/zh-CN/book/escaping.md b/zh-CN/book/escaping.md deleted file mode 100644 index efbe1227f91..00000000000 --- a/zh-CN/book/escaping.md +++ /dev/null @@ -1,15 +0,0 @@ -# 转移到系统 - -Nu 提供了一套你可以在不同操作系统中使用的命令(也成为"内部"命令),而且具备这种一致性是很有帮助的。但有时,你需要运行一个与 Nu 内部命令同名的外部命令。例如,要运行外部的`ls`或`date`命令,你可以使用脱字符 (^) 命令。用 `^` 前缀 可以转移调用用户 PATH 中的命令(例如:`/bin/ls`,而不是 Nu 内部的 [`ls`](/commands/docs/ls.md) 命令)。 - -Nu 的内部命令: - -```nu -ls -``` - -转移到外部命令: - -```nu -^ls -``` diff --git a/zh-CN/book/explore.md b/zh-CN/book/explore.md new file mode 100644 index 00000000000..7d54f611415 --- /dev/null +++ b/zh-CN/book/explore.md @@ -0,0 +1,65 @@ +# `explore` + +explore是一个表格分页器,类似于`less`,但用于表格结构化数据。 + +## 签名 + +`> explore --head --index --reverse --peek` + +### 参数 + +- `--head {bool}`: 显示或隐藏列标题(默认为true) +- `--index, -i`: 查看列表时显示行索引 +- `--tail, -t`: 从底部开始滚动视口 +- `--peek, -p`: 退出时输出光标所在单元格的值 + +## 快速开始 + +```nu +ls | explore -i +``` + +![explore-ls-png](https://user-images.githubusercontent.com/20165848/207849604-421312e3-537f-4b2e-b83e-f1f83f2a79d5.png) + +[`explore`](/commands/docs/explore.md)的主要功能是`:table`(你可以在上面的截图中看到)。 + +你可以通过``、``、``、``方向键与之交互。它还支持Vim风格的``、``、``、``键绑定,``和``,以及Emacs风格的``、``、``和``键绑定。 + +你可以通过进入光标模式来检查底层值。按``或``进入该模式。 +然后使用方向键选择需要的单元格。 +你将能够看到它的底层结构。 + +你可以通过`:help`获取更多相关信息。 + +## 命令 + +[`explore`](/commands/docs/explore.md)有一系列内置命令可供使用。通过按`<:>`然后输入命令名称来运行命令。 + +要查看完整的命令列表,可以输入`:help`。 + +## 配置 + +你可以通过配置自定义许多选项(包括样式和颜色)。 +你可以在[`default-config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/default_config.nu)中找到示例配置。 + +## 示例 + +### 查看值 + +```nu +$nu | explore --peek +``` + +![explore-peek-gif](https://user-images.githubusercontent.com/20165848/207854897-35cb7b1d-7f7d-4ae2-9ec8-df19ac04ac99.gif) + +### `:try`命令 + +有一个交互式环境,你可以使用`nu`命令浏览数据。 + +![explore-try-gif](https://user-images.githubusercontent.com/20165848/208159049-0954c327-9cdf-4cb3-a6e9-e3ba86fde55c.gif) + +#### 通过`$nu`保留所选值 + +记住你可以结合`--peek`参数使用。 + +![explore-try-nu-gif](https://user-images.githubusercontent.com/20165848/208161203-96b51209-726d-449a-959a-48b205c6f55a.gif) diff --git a/zh-CN/book/externs.md b/zh-CN/book/externs.md index 40d0b2b3674..aeee2141029 100644 --- a/zh-CN/book/externs.md +++ b/zh-CN/book/externs.md @@ -1,51 +1,69 @@ # 外部命令 -调用外部命令是将 Nushell 作为一个 Shell 使用的基本部分(通常也将 Nushell 作为一种语言使用)。但是有一个问题,对于 Nushell 之外的命令而言,Nushell 不能帮助寻找调用中的错误,或者自动补全,或者语法高亮。 +使用外部命令(又名二进制文件或应用程序)是任何 shell 的一个基本功能。Nushell 允许自定义命令利用其许多功能,例如: -这就是 `extern` 的作用。`extern`关键字允许你为 Nushell 之外的命令写一个完整的签名,这样你就能得到上述所有的好处。如果你看一下默认配置,你会发现其中有一些`extern`调用。下面是其中之一: +- 解析时类型检查 +- 自动补全 +- 语法高亮 + +对这些功能的支持是使用 `extern` 关键字提供的,它允许为外部命令定义一个完整的签名。 + +下面是 `ssh` 命令的一个简短例子: ```nu - export extern "git push" [ - remote?: string@"nu-complete git remotes", # the name of the remote - refspec?: string@"nu-complete git branches" # the branch / refspec - --verbose(-v) # be more verbose - --quiet(-q) # be more quiet - --repo: string # repository - --all # push all refs - --mirror # mirror all refs - --delete(-d) # delete refs - --tags # push tags (can't be used with --all or --mirror) - --dry-run(-n) # dry run - --porcelain # machine-readable output - --force(-f) # force updates - --force-with-lease: string # require old value of ref to be at this value - --recurse-submodules: string # control recursive pushing of submodules - --thin # use thin pack - --receive-pack: string # receive pack program - --exec: string # receive pack program - --set-upstream(-u) # set upstream for git pull/status - --progress # force progress reporting - --prune # prune locally removed refs - --no-verify # bypass pre-push hook - --follow-tags # push missing but relevant tags - --signed: string # GPG sign the push - --atomic # request atomic transaction on remote side - --push-option(-o): string # option to transmit - --ipv4(-4) # use IPv4 addresses only - --ipv6(-6) # use IPv6 addresses only +module "ssh extern" { + def complete_none [] { [] } + + def complete_ssh_identity [] { + ls ~/.ssh/id_* + | where {|f| + ($f.name | path parse | get extension) != "pub" + } + | get name + } + + export extern ssh [ + destination?: string@complete_none # 目标主机 + -p: int # 目标端口 + -i: string@complete_ssh_identity # 身份文件 ] +} +use "ssh extern" ssh ``` -你会注意到这给了你所有与内部命令相同的描述性语法,让你描述标志(Flags)、短标志(Short Flags)、位置参数、类型等等。 +你会注意到这里的语法与定义自定义命令时的 `def` 关键字的语法相似。你可以描述标志、位置参数、类型、补全器等等。 + +这个实现: + +- 将提供 `-p` 和 `-i`(带描述)作为 `ssh -` 的可能补全。 +- 将执行解析时类型检查。试图为端口号使用非 `int` 将导致错误(和错误条件的语法高亮)。 +- 将根据参数的形状提供解析时语法高亮。 +- 将提供 `~/.ssh` 中的任何私钥文件作为 `-i`(身份)选项的补全值。 +- 将不为目标主机提供补全。如果没有返回空列表的补全器,Nushell 将尝试使用默认的“文件”补全器。 -## 类型和自定义补全 + 有关从 SSH 配置文件中检索主机的实现,请参阅 [Nu_scripts 仓库](https://github.com/nushell/nu_scripts/blob/main/custom-completions/ssh/ssh-completions.nu)。 -在上面的例子中,你会注意到有些类型后面有`@`,接着后面是命令的名称。我们有独立的章节进一步谈论 [自定义补全](custom_completions.md)。 +::: tip 提示 +用于参数文档目的的 Nushell 注释,如果与参数在同一行,则需要在 `#` 井号之前加一个空格。 +::: -参数的类型(或形状)和自定义补全都告诉 Nushell 如何完成对该标志或位置值的补全。例如,将类型设置为`path`允许 Nushell 为你将值补全为一个文件路径。使用`@`和一个自定义的补全方式覆盖了这个默认行为,让该自定义补全方式返回给你完整的补全列表。 +## 格式说明符 + +位置参数可以用 `?` 设为可选(如上所示)。剩余的(`rest`)参数可以在参数名称前用 `...` 来匹配。例如: + +```nu +export extern "git add" [ + ...pathspecs: path + # … +] +``` ## 局限性 目前的`extern`语法有一些限制。在 Nushell 中,标志和位置参数是非常灵活的:标志可以在位置参数之前, 也可以与位置参数混合, 还可以跟随位置参数。许多外部命令没有这种灵活性。目前还没有一种方法来确保标志和位置参数的特定顺序与外部命令所要求的风格保持一致。 第二个限制是,有些外部命令要求使用`=`来传递标志和值。在 Nushell 中,`=`是一种方便的可选默认参数语法,目前还没有办法按要求使用它。 + +此外,通过脱字符号(例如 `^ssh`)调用的外部命令不会被 `extern` 识别。 + +最后,一些外部命令支持使用单个前导连字符的 `-long` 参数。Nushell `extern` 语法尚不能表示这些参数。 diff --git a/zh-CN/book/getting_started.md b/zh-CN/book/getting_started.md new file mode 100644 index 00000000000..d096e833413 --- /dev/null +++ b/zh-CN/book/getting_started.md @@ -0,0 +1,7 @@ +# 入门指南 + +让我们开始吧! :elephant: + +接下来的章节将通过示例带你[快速了解 Nushell](quick_tour.md)(包括如何在 Nushell 内部获取帮助),并向你展示如何[在文件系统中移动](moving_around.md)。 + +然后,由于 Nushell 的一些设计决策与典型的 Shell 或动态脚本语言有很大不同,请务必查看[Nu 的思维方式](thinking_in_nu.md),我们在其中解释了这些概念。 diff --git a/zh-CN/book/hooks.md b/zh-CN/book/hooks.md index 10cd48387d4..60449232b61 100644 --- a/zh-CN/book/hooks.md +++ b/zh-CN/book/hooks.md @@ -1,15 +1,15 @@ # 钩子 钩子允许你在一些预定义的情况下运行一个代码片段。 -它们只在交互式模式下可用([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)),如果你用脚本(`nu script.nu`)或命令(`nu -c "echo foo"`)参数运行 Nushell 则不起作用。 +它们只在交互式模式下可用([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)),如果你用脚本(`nu script.nu`)或命令(`nu -c "print foo"`)参数运行 Nushell 则不起作用。 目前,我们支持这些类型的钩子: - `pre_prompt` : 在命令提示显示之前被触发; - `pre_execution` : 在行输入开始执行前被触发; - `env_change` : 当环境变量发生变化时被触发; -- `display_output` : A block that the output is passed to -- `command_not_found` : Triggered when a command is not found +- `display_output` : 一个代码块,输出会被传递给它 +- `command_not_found` : 当一个命令未找到时被触发 为了更清晰地阐述,我们可以将 Nushell 的执行周期进行分解。 在 REPL 模式下,评估一行(代码)的步骤如下: @@ -19,7 +19,9 @@ 3. 显示命令提示符并等待用户输入; 4. 在用户输入东西并按下 "Enter" 健后,检查 `pre_execution` 钩子并运行它们; 5. 解析和评估用户的输入; -6. 返回到第一步; +6. 如果一个命令未找到:运行 `command_not_found` 钩子。如果它返回一个字符串,则显示它。 +7. 如果 `display_output` 被定义,则用它来打印命令输出。 +8. 返回到第一步; ## 基本钩子 @@ -82,6 +84,26 @@ $env.SPAM 钩子代码块遵循一般的作用域规则,即在块内定义的命令、别名等将在代码块结束后被丢掉。 +## `pre_execution` 钩子 + +`pre_execution` 钩子可以通过 [`commandline` 命令](/commands/docs/commandline.md) 来检查将要执行的命令。 + +例如,要打印正在执行的命令: + +```nu +$env.config = ( + $env.config + | upsert hooks.pre_execution [ {|| + $env.repl_commandline = (commandline) + print $"Command: ($env.repl_commandline)" + } ] +) + +print (1 + 3) +# => Command: print (1 + 3) +# => 4 +``` + ## 条件钩子 你可能很想做的一件事是,每当你进入一个目录时,就激活一个环境: @@ -181,9 +203,9 @@ $env.config = ($env.config | upsert hooks { ```nu $env.config = ($env.config | upsert hooks.env_change.PWD {|config| - let val = ($config | get -i hooks.env_change.PWD) + let val = ($config | get -o hooks.env_change.PWD) - if $val == $nothing { + if $val == null { $val | append {|before, after| print $"changing directory from ($before) to ($after)" } } else { [ @@ -195,7 +217,7 @@ $env.config = ($env.config | upsert hooks.env_change.PWD {|config| ### 进入目录时自动激活相应环境 -以下代码将在进入一个目录('/path/to/target/dir')后寻找 `test-env.nu` 并加载,而在离开该目录的时候移除相关定义(除了 `PWD` 环境变量): +以下代码将在进入一个目录后寻找 `test-env.nu` 并加载: ```nu $env.config = ($env.config | upsert hooks.env_change.PWD { @@ -205,16 +227,121 @@ $env.config = ($env.config | upsert hooks.env_change.PWD { ($after == '/path/to/target/dir' and ($after | path join test-env.nu | path exists)) } - code: "overlay add test-env.nu" + code: "overlay use test-env.nu" } { condition: {|before, after| ('/path/to/target/dir' not-in $after - and '/path/to/target/dir' in $before + and '/path/to/target/dir' in ($before | default "") and 'test-env' in (overlay list)) } - code: "overlay remove test-env --keep-env [ PWD ]" + code: "overlay hide test-env --keep-env [ PWD ]" } ] }) ``` + +### 过滤或转移命令输出 + +你可以使用 `display_output` 钩子来重定向命令的输出。 +你应该定义一个适用于所有值类型的代码块。 +外部命令的输出不会通过 `display_output` 进行过滤。 + +这个钩子可以在一个单独的窗口中显示输出,也许是富文本 HTML。 +下面是实现这个功能的基本思路: + +```nu +$env.config = ($env.config | upsert hooks { + display_output: { to html --partial --no-color | save --raw /tmp/nu-output.html } +}) +``` + +你可以在网页浏览器中打开 `file:///tmp/nu-output.html` 来查看结果。 +当然,这不是很方便,除非你使用一个在文件改变时能自动重新加载的浏览器。 +除了 [`save`](/commands/docs/save.md) 命令,你通常会自定义这个钩子,将 HTML 输出发送到所需窗口。 + +### 改变输出的显示方式 + +你可以通过使用 `display_output` 钩子来改变输出的默认显示行为。 +下面是一个例子,如果终端足够宽,它将默认显示行为改为显示1层深度的表格,否则就折叠起来: + +```nu +$env.config = ($env.config | upsert hooks { + display_output: {if (term size).columns >= 100 { table -ed 1 } else { table }} +}) +``` + +### _Arch Linux_ 中的 `command_not_found` 钩子 + +下面的钩子使用 `pkgfile` 命令,在 _Arch Linux_ 中查找命令属于哪个包。 + +```nu +$env.config = { + ...other config... + + hooks: { + ...other hooks... + + command_not_found: { + |cmd_name| ( + try { + let pkgs = (pkgfile --binaries --verbose $cmd_name) + if ($pkgs | is-empty) { + return null + } + ( + $"(ansi $env.config.color_config.shape_external)($cmd_name)(ansi reset) " + + $"may be found in the following packages:\n($pkgs)" + ) + } + ) + } + } +} +``` + +### _NixOS_ 中的 `command_not_found` 钩子 + +NixOS 自带 `command-not-found` 命令。我们只需要把它插入到 nushell 钩子中: + +```nu +$env.config.hooks.command_not_found = { + |command_name| + print (command-not-found $command_name | str trim) +} +``` + +### _Windows_ 中的 `command_not_found` 钩子 + +下面的钩子使用 `ftype` 命令,在 _Windows_ 中查找可能与用户 `alias` 相关的程序路径。 + +```nu +$env.config = { + ...other config... + + hooks: { + ...other hooks... + + command_not_found: { + |cmd_name| ( + try { + let attrs = ( + ftype | find $cmd_name | to text | lines | reduce -f [] { |line, acc| + $line | parse "{type}={path}" | append $acc + } | group-by path | transpose key value | each { |row| + { path: $row.key, types: ($row.value | get type | str join ", ") } + } + ) + let len = ($attrs | length) + + if $len == 0 { + return null + } else { + return ($attrs | table --collapse) + } + } + ) + } + } +} +``` diff --git a/zh-CN/book/how_nushell_code_gets_run.md b/zh-CN/book/how_nushell_code_gets_run.md new file mode 100644 index 00000000000..0213c09cfc7 --- /dev/null +++ b/zh-CN/book/how_nushell_code_gets_run.md @@ -0,0 +1,369 @@ +# Nushell代码执行原理 + +在[用Nu的方式思考](./thinking_in_nu.md#think-of-nushell-as-a-compiled-language)中,我们建议你"将Nushell视为编译型语言",这是因为Nushell代码的处理方式。我们还介绍了一些由于这种处理方式而在Nushell中无法工作的代码示例。 + +其根本原因是Nushell严格分离了 **_解析和评估_** 阶段,并 **_禁止类似`eval`的功能_** 。在本节中,我们将详细解释这意味着什么、为什么这样做以及其影响。解释尽可能简单,但如果你之前使用过其他编程语言可能会更容易理解。 + +[[toc]] + +## 解释型语言 vs 编译型语言 + +### 解释型语言 + +Nushell、Python和Bash(以及许多其他语言)都是"解释型"语言。 + +让我们从一个简单的"Hello, World!"Nushell程序开始: + +```nu +# hello.nu + +print "Hello, World!" +``` + +当然,使用`nu hello.nu`可以按预期运行。用Python或Bash编写的类似程序看起来(和行为)几乎相同。 + +在"解释型语言"中,代码通常是这样处理的: + +```text +源代码 → 解释器 → 结果 +``` + +Nushell遵循这种模式,其"解释器"分为两部分: + +1. `源代码 → 解析器 → 中间表示(IR)` +2. `IR → 评估引擎 → 结果` + +首先,源代码由解析器分析并转换为中间表示(IR),在Nushell中这只是一组数据结构。然后,这些数据结构被传递给引擎进行评估并输出结果。 + +这在解释型语言中也很常见。例如,Python源代码通常在评估前[转换为字节码](https://github.com/python/cpython/blob/main/InternalDocs/interpreter.md)。 + +### 编译型语言 + +另一方面是通常"编译"的语言,如C、C++或Rust。例如,这是一个简单的Rust"Hello, World!": + +```rust +// main.rs + +fn main() { + println!("Hello, World!"); +} +``` + +要"运行"这段代码,必须: + +1. 编译为[机器码指令](https://en.wikipedia.org/wiki/Machine_code) +2. 将编译结果作为二进制文件存储在磁盘上 + +前两步通过`rustc main.rs`完成。 + +3. 然后,要产生结果,需要运行二进制文件(`./main`),将指令传递给CPU + +所以: + +1. `源代码 ⇒ 编译器 ⇒ 机器码` +2. `机器码 ⇒ CPU ⇒ 结果` + +::: important +你可以看到编译-运行序列与解释器的解析-评估序列没有太大区别。你从源代码开始,将其解析(或编译)为某种状态(例如字节码、IR、机器码),然后评估(或运行)IR以获得结果。你可以将机器码视为另一种类型的IR,将CPU视为其解释器。 + +然而,解释型语言和编译型语言之间的一个重大区别是解释型语言通常实现 _`eval`函数_,而编译型语言则没有。这意味着什么? +::: + +## 动态语言 vs 静态语言 + +::: tip 术语说明 +一般来说,动态语言和静态语言的区别在于源代码在编译(或解析)阶段与求值/运行时阶段的处理程度: + +- _"静态"_ 语言在编译/解析阶段执行更多代码分析(如类型检查、[数据所有权](https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html)) + +- _"动态"_ 语言在求值/运行时执行更多代码分析,包括对额外代码的`求值函数`调用 + +就本文讨论而言,静态语言和动态语言的主要区别在于是否具有`求值函数`功能 +::: + +### 求值函数 + +大多数动态解释型语言都有`求值函数`。例如[Python的eval](https://docs.python.org/3/library/functions.html#eval)(以及[Python的exec](https://docs.python.org/3/library/functions.html#exec))或[Bash的eval](https://linux.die.net/man/1/bash)。 + +`求值函数`的参数是 _"源代码中的源代码"_,通常是条件计算或动态生成的。这意味着当解释型语言在解析/求值过程中遇到`求值函数`时,通常会中断正常的求值过程,对`求值函数`的源代码参数启动新的解析/求值过程。 + +以下是一个简单的Python `eval`示例来说明这个(可能令人困惑的!)概念: + +```python:line-numbers +# hello_eval.py + +print("Hello, World!") +eval("print('Hello, Eval!')") +``` + +运行该文件(`python hello_eval.py`)时,你会看到两条消息:_"Hello, World!"_ 和 _"Hello, Eval!"_。具体过程如下: + +1. 整个程序被解析 +2. (第3行) `print("Hello, World!")` 被求值 +3. (第4行) 为了求值 `eval("print('Hello, Eval!')")`: + 1. `print('Hello, Eval!')` 被解析 + 2. `print('Hello, Eval!')` 被求值 + +::: tip 更有趣的示例 +考虑 `eval("eval(\"print('Hello, Eval!')\")")` 等等! +::: + +请注意,这里使用`求值函数`在执行过程中添加了一个新的"元"步骤。`求值函数`不是单一的解析/求值过程,而是创建了额外的"递归"解析/求值步骤。这意味着Python解释器生成的字节码可以在求值过程中进一步修改。 + +Nushell不允许这样做。 + +如上所述,由于在解释过程中没有`求值函数`来修改字节码,解释型语言的解析/求值过程与C++和Rust等编译型语言的编译/运行过程(在高层面上)几乎没有区别。 + +::: tip 关键点 +这就是为什么我们建议你 _"将Nushell视为编译型语言"_。尽管它是一种解释型语言,但由于缺少`求值函数`,它具有传统静态编译型语言的一些特征性优点和限制。 +::: + +我们将在下一节深入探讨这意味着什么。 + +## 影响 + +考虑以下Python示例: + +```python:line-numbers +exec("def hello(): print('Hello eval!')") +hello() +``` + +::: note +本例中使用`exec`而非`eval`,因为它可以执行任何有效的Python代码,而不仅限于`eval`表达式。不过两者的原理相似。 +::: + +在解释过程中: + +1. 整个程序被解析 +2. 为了求值第1行: + 1. `def hello(): print('Hello eval!')` 被解析 + 2. `def hello(): print('Hello eval!')` 被求值 +3. (第2行) `hello()` 被求值 + +请注意,直到步骤2.2之前,解释器根本不知道存在`hello`函数!这使得动态语言的[静态分析](https://en.wikipedia.org/wiki/Static_program_analysis)变得困难。在这个例子中,仅通过解析(编译)源代码无法检查`hello`函数是否存在。解释器必须求值(运行)代码才能发现它。 + +- 在静态编译型语言中,缺失的函数保证会在编译时被发现 +- 而在动态解释型语言中,这可能会成为*潜在的*运行时错误。如果`求值函数`定义的函数被有条件地调用,可能直到生产环境中满足该条件时才会发现错误 + +::: important +在Nushell中,只有**两个步骤**: + +1. 解析整个源代码 +2. 求值整个源代码 + +这就是完整的解析/求值序列。 +::: + +::: tip 关键点 +通过不允许类似`求值函数`的功能,Nushell防止了这类与`求值函数`相关的错误。在Nushell中,调用不存在的定义保证会在解析时被发现。 + +此外,解析完成后,我们可以确定字节码(IR)在求值过程中不会改变。这使我们对结果字节码(IR)有深入的了解,能够进行强大可靠的静态分析和IDE集成,这在更动态的语言中可能难以实现。 + +总的来说,在扩展Nushell程序时,你可以更放心错误会被更早发现。 +::: + +## Nushell交互式解释器(REPL) + +与大多数Shell一样,Nushell有个 _"读取→求值→打印循环"_ ([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)),在你不带任何文件参数运行 `nu` 时启动。这通常被认为是 _"命令行"_,但并不完全相同。 + +::: tip 注意 +在本节中,代码块行首的`> `字符表示命令行 **_提示符_** 。例如: + +```nu +> 一些代码... +``` + +以下示例中提示符后的代码通过按Enter键执行。例如: + +```nu +> print "Hello world!" +# => Hello world! + +> ls +# => 打印文件和目录... +``` + +上述表示: + +- 在Nushell中(通过`nu`启动): + 1. 输入`print "Hello world!"` + 2. 按Enter + 3. Nushell将显示结果 + 4. 输入`ls` + 5. 按Enter + 6. Nushell将显示结果 + +::: + +当你在输入命令行后按Enter时,Nushell会: + +1. **_(读取)_**:读取命令行输入 +2. **_(求值)_**:解析命令行输入 +3. **_(求值)_**:求值命令行输入 +4. **_(求值)_**:将环境(如当前工作目录)合并到Nushell内部状态 +5. **_(打印)_**:显示结果(如果非`null`) +6. **_(循环)_**:等待下一个输入 + +换句话说,每个REPL调用都是独立的解析-求值序列。通过将环境合并回Nushell状态,我们保持了REPL调用之间的连续性。 + +比较 _"用Nu的方式思考"_ 中的简化版[`cd`示例](./thinking_in_nu.md#example-change-to-a-different-directory-cd-and-source-a-file): + +```nu +cd spam +source-env foo.nu +``` + +我们看到这不能工作(作为脚本或其他单一表达式),因为目录将在解析时[`source-env`关键字](/commands/docs/source-env.md)尝试读取文件 _之后_ 被更改。 + +然而,作为单独的REPL条目运行这些命令可以工作: + +```nu +> cd spam +> source-env foo.nu +# 成功运行! +``` + +要理解原因,让我们分解示例中发生的情况: + +1. 读取`cd spam`命令行 +2. 解析`cd spam`命令行 +3. 求值`cd spam`命令行 +4. 将环境(包括当前目录)合并到Nushell状态 +5. 读取并解析`source-env foo.nu` +6. 求值`source-env foo.nu` +7. 将环境(包括`foo.nu`的任何更改)合并到Nushell状态 + +当`source-env`在第5步解析期间尝试打开`foo.nu`时,它可以这样做,因为第3步的目录更改已在第4步合并到Nushell状态中。因此,它在后续的解析/求值周期中可见。 + +### 多行REPL命令行 + +请注意,这仅适用于 **_单独_** 的命令行。 + +在Nushell中,可以使用以下方式将多个命令组合成一个命令行: + +- 分号: + + ```nu + cd spam; source-env foo.nu + ``` + +- 换行符: + + ``` + > cd span + source-env foo.nu + ``` + + 注意第二行前没有"提示符"。这种多行命令行通常通过[键绑定](./line_editor.md#keybindings)创建,在按下Alt+EnterShift+Enter时插入换行符。 + +这两个示例在Nushell REPL中的行为完全相同。整个命令行(两个语句)作为单个读取→求值→打印循环处理。因此,它们会像前面的脚本示例一样失败。 + +::: tip +多行命令行在Nushell中非常有用,但要注意任何顺序错误的解析器关键字。 +::: + +## 解析时常量求值 + +虽然无法在求值阶段添加解析功能同时保持静态语言的优点,但我们可以在解析过程中安全地添加*少量*求值功能。 + +::: tip 术语说明 +在下文中,术语 _"常量"_ 指: + +- `const`定义 +- 当提供常量输入时输出常量值的任何命令的结果 + +::: + +本质上,**_常量_** 和常量值在解析时是已知的。这与 _变量_ 声明和值形成鲜明对比。 + +因此,我们可以将常量用作[`source`](/commands/docs/source.md)、[`use`](/commands/docs/use.md)等解析时关键字的安全已知参数。 + +考虑 _"用Nu的方式思考"_ 中的[这个示例](./thinking_in_nu.md#example-dynamically-creating-a-filename-to-be-sourced): + +```nu +let my_path = "~/nushell-files" +source $"($my_path)/common.nu" +``` + +如前所述,我们可以改为这样做: + +```nu:line-numbers +const my_path = "~/nushell-files" +source $"($my_path)/common.nu" +``` + +让我们分析这个版本的解析/求值过程: + +1. 整个程序被解析为IR: + + 1. 第1行:`const`定义被解析。由于它是常量赋值(且`const`也是解析器关键字),该赋值也可以在此阶段被求值。其名称和值由解析器存储。 + 2. 第2行:`source`命令被解析。由于`source`也是解析器关键字,它在此阶段被求值。在本例中,它可以 **_成功_** 解析,因为其参数是 **_已知的_** 并且可以在此点检索。 + 3. `~/nushell-files/common.nu`的源代码被解析。如果无效,则会生成错误,否则IR结果将包含在下一阶段的求值中。 + +2. 整个IR被求值: + 1. 第1行:`const`定义被求值。变量被添加到运行时栈。 + 2. 第2行:解析`~/nushell-files/common.nu`的IR结果被求值。 + +::: important + +- `eval`在求值期间添加额外的解析 +- 解析时常量则相反,在解析器中添加额外的求值 + +::: + +还需记住,解析期间允许的求值 **_非常有限_** 。它仅限于常规求值允许的一小部分。 + +例如,以下是不允许的: + +```nu +const foo_contents = (open foo.nu) +``` + +换句话说,只有一小部分命令和表达式可以生成常量值。对于允许的命令: + +- 它必须设计为输出常量值 +- 其所有输入也必须是常量值、字面量或字面量的复合类型(如记录、列表、表格) + +一般来说,这些命令和结果表达式相当简单且 **_没有副作用_** 。否则,解析器很容易进入不可恢复的状态。例如,想象尝试将无限流分配给常量。解析阶段将永远不会完成! + +::: tip +你可以使用以下命令查看哪些Nushell命令可以返回常量值: + +```nu +help commands | where is_const +``` + +::: + +例如,`path join`命令可以输出常量值。Nushell还在`$nu`常量记录中定义了几个有用的路径。这些可以组合起来创建有用的解析时常量求值,如: + +```nu +const my_startup_modules = $nu.default-config-dir | path join "my-mods" +use $"($my_startup_modules)/my-utils.nu" +``` + +::: note 补充说明 +编译型("静态")语言通常也有在编译时传达某些逻辑的方式。例如: + +- C的预处理器 +- Rust宏 +- [Zig的comptime](https://kristoff.it/blog/what-is-zig-comptime),这是Nushell解析时常量求值的灵感来源 + +这样做有两个原因: + +1. _提高运行时性能_:编译阶段的逻辑不需要在运行时重复。 + + 目前这不适用于Nushell,因为解析结果(IR)不会在求值后存储。不过,这已被考虑作为未来的可能功能。 + +2. 与Nushell的解析时常量求值一样,这些功能有助于(安全地)解决因缺少`求值函数`而导致的限制。 + +::: + +## 结论 + +Nushell运行在通常由 _"动态"_、*"解释型"*语言(如Python、Bash、Zsh、Fish等)主导的脚本语言领域。Nushell也是 *"解释型"*的,因为代码会立即运行(无需单独的手动编译)。 + +然而,它并不 _"动态"_,因为它没有`求值函数`构造。在这方面,它与Rust或Zig等 *"静态"*编译型语言有更多共同点。 + +缺少`求值函数`常常让许多新用户感到惊讶,这也是为什么将Nushell视为编译型静态语言会有所帮助。 diff --git a/zh-CN/book/installation.md b/zh-CN/book/installation.md index 4ea280a8795..1b616f11a54 100644 --- a/zh-CN/book/installation.md +++ b/zh-CN/book/installation.md @@ -6,6 +6,8 @@ Nushell 的主要二进制文件被命名为 `nu`(或 Windows 下的 `nu.exe` @[code](@snippets/installation/run_nu.sh) +[[toc]] + ## 预编译二进制包 Nu 二进制文件在 [GitHub 的 Release 页](https://github.com/nushell/nushell/releases) 发布,适用于 Linux、macOS 和 Windows。只需下载并解压二进制文件,然后将其复制到你的系统 `PATH` 上的某个位置即可。 @@ -18,16 +20,77 @@ Nu 可以通过几个软件包管理器获得: 对于 macOS 和 Linux,[Homebrew](https://brew.sh/) 是一个流行的选择(`brew install nushell`)。 -对于 Windows 用户: +对于 Windows: + +- [Winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/) + + - 机器范围安装: `winget install nushell --scope machine` + - 机器范围升级: `winget update nushell` + - 用户范围安装: `winget install nushell` or `winget install nushell --scope user` + - 用户范围升级: 由于 [winget-cli 问题 #3011](https://github.com/microsoft/winget-cli/issues/3011),运行 `winget update nushell` 会意外地将最新版本安装到 `C:\Program Files\nu`。要解决此问题,请再次运行 `winget install nushell` 以在用户范围内安装最新版本。 -- [Winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/) (`winget install nushell`) - [Chocolatey](https://chocolatey.org/) (`choco install nushell`) - [Scoop](https://scoop.sh/) (`scoop install nu`) +对于 Debian 和 Ubuntu: + +```sh +curl -fsSL https://apt.fury.io/nushell/gpg.key | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/fury-nushell.gpg +echo "deb https://apt.fury.io/nushell/ /" | sudo tee /etc/apt/sources.list.d/fury.list +sudo apt update +sudo apt install nushell +``` + +对于 RedHat/Fedora 和 Rocky Linux: + +```sh +echo "[gemfury-nushell] +name=Gemfury Nushell Repo +baseurl=https://yum.fury.io/nushell/ +enabled=1 +gpgcheck=0 +gpgkey=https://yum.fury.io/nushell/gpg.key" | sudo tee /etc/yum.repos.d/fury-nushell.repo +sudo dnf install -y nushell +``` + +对于 Alpine Linux: + +```sh +echo "https://alpine.fury.io/nushell/" | tee -a /etc/apk/repositories +apk update +apk add --allow-untrusted nushell +``` + 跨平台安装: - [npm](https://www.npmjs.com/) (`npm install -g nushell` 请注意,以这种方式安装,nu 插件是不包含在内的) +## Docker 容器镜像 + +Docker 镜像可从 GitHub 容器注册表获得。最新版本的镜像会定期为 Alpine 和 Debian 构建。 +你可以使用以下命令以交互模式运行镜像: + +```nu +docker run -it --rm ghcr.io/nushell/nushell:- +``` + +其中 `` 是你想要运行的 Nushell 版本,`` 是 `alpine` 或最新的受支持的 Debian 版本,例如 `bookworm`。 + +要运行特定命令,请使用: + +```nu +docker run --rm ghcr.io/nushell/nushell:latest-alpine -c "ls /usr/bin | where size > 10KiB" +``` + +要使用 Bash 从当前目录运行脚本,请使用: + +```nu +docker run --rm \ + -v $(pwd):/work \ + ghcr.io/nushell/nushell:latest-alpine \ + "/work/script.nu" +``` + ## 从源码构建 你也可以从源代码构建 `Nu`。首先,你需要设置 Rust 工具链和它的依赖项。 @@ -58,14 +121,10 @@ Nu 目前需要 **最新(1.66.1 或更高)的稳定** 版本的 Rust。最 #### Debian/Ubuntu -你将需要安装 "pkg-config" 和 "libssl-dev" 包: +你将需要安装 "pkg-config"、"build-essential" 和 "libssl-dev" 包: @[code](@snippets/installation/install_pkg_config_libssl_dev.sh) -对于希望使用 "rawkey" 或 "clipboard" 可选功能的 Linux 用户,需要安装 "libx11-dev" 和 "libxcb-composite0-dev" 软件包。 - -@[code](@snippets/installation/use_rawkey_and_clipboard.sh) - #### 基于 RHEL 的发行版 你需要安装 "libxcb"、"openssl-devel" 和 "libX11-devel": @@ -74,17 +133,31 @@ Nu 目前需要 **最新(1.66.1 或更高)的稳定** 版本的 Rust。最 #### macOS +##### Homebrew + 使用 [Homebrew](https://brew.sh/),你需要通过如下方式安装 "openssl" 和 "cmake" : @[code](@snippets/installation/macos_deps.sh) -### 使用 [crates.io](https://crates.io) 进行构建 +##### Nix + +如果在 macOS 上使用 [Nix](https://nixos.org/download/#nix-install-macos) 进行包管理,则需要 `openssl`、`cmake`、`pkg-config` 和 `curl` 包。这些可以通过以下方式安装: + +- 全局安装,使用 `nix-env --install`(以及其他方式)。 +- 本地安装,在你的 `home.nix` 配置中使用 [Home Manager](https://github.com/nix-community/home-manager)。 +- 临时安装,使用 `nix-shell`(以及其他方式)。 + +### 使用 [crates.io](https://crates.io) 和 Cargo 构建 Nu 发行版会作为源码发布到流行的 Rust 包仓库 [crates.io](https://crates.io/)。这使得使用 `cargo` 构建并安装最新的 Nu 版本变得很容易: -@[code](@snippets/installation/cargo_install_nu.sh) +```nu +cargo install nu --locked +``` -如此即可!`cargo` 工具将完成下载 Nu 及其源码依赖,构建并将其安装到 cargo bin 路径中,以便我们能够运行它。 +`cargo` 工具将完成下载 Nu 及其源码依赖,构建并将其安装到 cargo bin 路径中。 + +请注意,使用 `cargo` 时,必须单独安装默认插件。有关说明,请参阅本书的[插件安装](./plugins.html#core-plugins)部分。 ### 从 GitHub 仓库构建 @@ -101,27 +174,3 @@ Nu 发行版会作为源码发布到流行的 Rust 包仓库 [crates.io](https:/ @[code](@snippets/installation/build_nu_from_source_release.sh) 熟悉 Rust 的人可能会问,如果 `run` 默认会构建,为什么我们还要做 `build` 和 `run` 这两个步骤?这是为了解决 Cargo 中新的 `default-run` 选项的缺陷,并确保所有插件都被构建,尽管这在将来可能不再需要。 - -## 设置登录 Shell(\*nix) - -:::danger -Nu 仍在开发中,对于日常使用可能并不稳定! -::: - -要设置登录 Shell,你可以使用 [`chsh`](https://linux.die.net/man/1/chsh) 命令。一些 Linux 发行版有一个位于 `/etc/shells` 的有效 Shell 列表,在 Nu 被列入白名单之前不允许改变 Shell。如果你没有更新 `shells` 文件,你可能会看到类似于下面的错误: - -@[code](@snippets/installation/chsh_invalid_shell_error.sh) - -你可以通过在 `shells` 文件中添加你的 Nu 二进制文件来把 Nu 添加到允许的 Shells 列表中。添加的路径可以用 `which nu` 命令找到,通常是 `$HOME/.cargo/bin/nu`。 - -## 设置默认的 Shell(Windows 终端) - -如果你使用的是 [Windows Terminal](https://github.com/microsoft/terminal),你可以通过添加如下内容到你的终端设置 `"profiles"`(JSON 文件)中来设置 `nu` 作为你的默认 Shell: - -@[code](@snippets/installation/windows_terminal_default_shell.sh) - -最后需要做的是将 `"defaultProfile"` 改为: - -@[code](@snippets/installation/windows_change_default_profile.sh) - -之后,`nu` 应该会在 **Windows Terminal** 启动时被加载。 diff --git a/zh-CN/book/introduction.md b/zh-CN/book/introduction.md deleted file mode 100644 index aec0ff7cf79..00000000000 --- a/zh-CN/book/introduction.md +++ /dev/null @@ -1,180 +0,0 @@ -# 介绍 - -大家好,欢迎来到 Nushell 项目。这个项目的目标是彰显 Shell 的 Unix 哲学,就是用管道将简单的命令连接在一起,并且带来现代的开发风格。 - -Nu 从许多熟悉的领域中汲取了线索:bash 等传统 shell,PowerShell 等高级 shell,函数式编程,系统编程等。但是 Nu 并没有试图成为所有行业的杰作,而是将精力集中在做好一些事情上: - -- 创建具有现代感的灵活的跨平台 Shell -- 允许你将命令行应用程序与可理解数据结构的 Shell 进行混合和匹配 -- 具有现代命令行应用程序提供的用户体验优化 - -了解 Nu 可以做什么的最简单方式就是通过一些例子,所以让我们沉浸进去吧。 - -当你运行一个类似 `ls` 的命令时,第一个注意到的是所得到的不是一个文本块,而是一个结构化的表。 - -```nu -ls -# => ────┬────────────────────┬──────┬────────┬──────────── -# => # │ name │ type │ size │ modified -# => ────┼────────────────────┼──────┼────────┼──────────── -# => 0 │ 404.html │ File │ 429 B │ 3 days ago -# => 1 │ CONTRIBUTING.md │ File │ 955 B │ 8 mins ago -# => 2 │ Gemfile │ File │ 1.1 KB │ 3 days ago -# => 3 │ Gemfile.lock │ File │ 6.9 KB │ 3 days ago -# => 4 │ LICENSE │ File │ 1.1 KB │ 3 days ago -# => 5 │ README.md │ File │ 213 B │ 3 days ago -# => ... -``` - -这个表不仅仅是用另一种方式显示了当前目录。就像电子表格中的表一样,我们可以更加交互性地处理数据。 - -我们做的第一件事是将表格通过尺寸来排序。为了做到这点,我们将会把 `ls` 的输出喂给一个可以根据列的内容来排序表格的命令。 - -```nu -ls | sort-by size | reverse -# => ────┬────────────────────┬──────┬────────┬──────────── -# => # │ name │ type │ size │ modified -# => ────┼────────────────────┼──────┼────────┼──────────── -# => 0 │ Gemfile.lock │ File │ 6.9 KB │ 3 days ago -# => 1 │ SUMMARY.md │ File │ 3.7 KB │ 3 days ago -# => 2 │ Gemfile │ File │ 1.1 KB │ 3 days ago -# => 3 │ LICENSE │ File │ 1.1 KB │ 3 days ago -# => 4 │ CONTRIBUTING.md │ File │ 955 B │ 9 mins ago -# => 5 │ books.md │ File │ 687 B │ 3 days ago -# => ... -``` - -你可以看到我们没有给 `ls` 传递命令行参数。作为代替,我们使用了 Nu 提供的 `sort-by` 命令来排序 `ls` 的输出。为了让最大的文件位于顶部,我们还使用了 `reverse`。 - -Nu 提供了许多可在表格上工作的命令。例如,我们可以过滤 `ls` 表格,只显示大于 1 KB 的文件: - -```nu -ls | where size > 1kb -# => ───┬──────────────┬──────┬────────┬──────────── -# => # │ name │ type │ size │ modified -# => ───┼──────────────┼──────┼────────┼──────────── -# => 0 │ Gemfile │ File │ 1.1 KB │ 3 days ago -# => 1 │ Gemfile.lock │ File │ 6.9 KB │ 3 days ago -# => 2 │ LICENSE │ File │ 1.1 KB │ 3 days ago -# => 3 │ SUMMARY.md │ File │ 3.7 KB │ 3 days ago -# => ───┴──────────────┴──────┴────────┴──────────── -``` - -就像 Unix 哲学所体现的那样,作为可以相互交流的命令,为我们提供了许多种不同的配对。让我们看一个不同的命令: - -```nu -ps -# => ─────┬───────┬──────────────────┬─────────┬─────────┬──────────┬───────── -# => # │ pid │ name │ status │ cpu │ mem │ virtual -# => ─────┼───────┼──────────────────┼─────────┼─────────┼──────────┼───────── -# => 0 │ 33155 │ nu_plugin_core_p │ Running │ 3.8496 │ 1.8 MB │ 4.4 GB -# => 1 │ 32841 │ mdworker_shared │ Running │ 0.0000 │ 19.3 MB │ 4.4 GB -# => 2 │ 32829 │ CoreServicesUIAg │ Running │ 0.0000 │ 16.1 MB │ 4.5 GB -# => 3 │ 32828 │ mdworker_shared │ Running │ 0.0000 │ 23.0 MB │ 4.4 GB -``` - -如果你使用过 Linux,可能会对 `ps` 命令相当熟悉。这个命令为我们展示了系统所有正在运行的进程和它们的状态与名称。我们同样可以查看到它们的 CPU 占用。 - -如果我们想显示正在使用 CPU 的进程怎么办? 就像我们之前使用 `ls` 命令一样,我们也可以使用 `ps` 命令返回的表: - -```nu -ps | where cpu > 10 -# => ───┬──────┬──────────────────┬─────────┬────────┬──────────┬───────── -# => # │ pid │ name │ status │ cpu │ mem │ virtual -# => ───┼──────┼──────────────────┼─────────┼────────┼──────────┼───────── -# => 0 │ 3971 │ Google Chrome He │ Running │ 5.1263 │ 99.4 MB │ 5.5 GB -# => 1 │ 360 │ iTerm2 │ Running │ 6.6635 │ 218.6 MB │ 6.0 GB -# => ───┴──────┴──────────────────┴─────────┴────────┴──────────┴───────── -``` - -到目前为止,我们已经看到使用 `ls` 和 `ps` 列出文件和进程。 Nu 还提供了其他可以创建有用信息表的命令。 接下来,让我们探索`date` 和 `sys`。 - -运行 `date` 将给出当前日期与时间的信息: - -```nu -date -# => ──────────┬──────── -# => year │ 2020 -# => month │ 5 -# => day │ 5 -# => hour │ 21 -# => minute │ 13 -# => second │ 17 -# => timezone │ +02:00 -# => ──────────┴──────── -``` - -运行 `sys` 将给出 Nu 所运行的系统的信息: - -```nu -sys -# => ─────────┬───────────────────────────────────────── -# => host │ [row 7 columns] -# => cpu │ [row cores current ghz max ghz min ghz] -# => disks │ [table 2 rows] -# => mem │ [row free swap free swap total total] -# => net │ [table 11 rows] -# => battery │ [table 1 rows] -# => ─────────┴───────────────────────────────────────── -``` - -和之前的表有些不一样, `sys` 命令给出的表在单元格内包含了另一些结构化的表格,而非简单的数值。为了查看到这些数据,我们需要选择要看的列: - -```nu -sys | get host -# => ──────────┬───────────────────────────────────────────── -# => name │ Linux -# => release │ 5.3.0-1019-azure -# => version │ #20-Ubuntu SMP Fri Mar 27 23:54:23 UTC 2020 -# => hostname │ lifeless -# => arch │ x86_64 -# => uptime │ 8:03:47:32 -# => sessions │ [table 2 rows] -# => ──────────┴───────────────────────────────────────────── -``` - -`get` 命令让我们跳入表格中一列的内容。这样,我们正在观看 "host" 列,它包含了关于 Nu 所运行的主机的信息。操作系统的名字、主机名、CPU 架构等等。让我们看看系统上的用户名: - -```nu -sys | get host.sessions -# => ───┬───────── -# => # │ -# => ───┼───────── -# => 0 │ sophia -# => ───┴───────── -``` - -目前,系统上只有一个用户,名为 `sophia`。 你会注意到,我们可以传递路径( `host.sessions` 部分),而不仅仅是传递列名。 Nu 将采用该路径并转到表中的相应数据位置。 - -您可能已经注意到其他的不同之处。 我们没有一个数据表,而是只有一个元素:字符串 "sophia"。 Nu 使用数据表和字符串。 字符串是在 Nu 之外使用命令的重要组成部分。 - -让我们看看字符串如何在 Nu 之外起作用。 我们将从前面的示例开始,并运行外部的 `echo` 命令( `^` 告诉 nu 不要使用内置的 `echo` 命令): - -```nu -sys | get host.sessions | ^echo $it -# => sophia -``` - -如果这看起来与我们以前的传统 Shell 非常相似,那么就说明你拥有敏锐的眼睛! 相似,但是有一个重要的区别:我们对前面看到的值调用了 `^echo` 。 这允许我们将数据从 Nu 传递到 `echo`(或 Nu 外部的任何命令,例如 `git`)。 - -_注意:Nu 的内建命令的帮助文本可以用 `help` 来浏览_: - -```nu -help config -# => Configuration management. -# => -# => Usage: -# => > config {flags} -# => -# => Subcommands: -# => config get - Gets a value from the config -# => config set - Sets a value in the config -# => config set_into - Sets a value in the config -# => config clear - clear the config -# => config load - Loads the config from the path given -# => config remove - Removes a value from the config -# => config path - return the path to the config file -# => -# => Flags: -# => -h, --help: Display this help message -``` diff --git a/zh-CN/book/line_editor.md b/zh-CN/book/line_editor.md index eaac5b43471..18604215b9d 100644 --- a/zh-CN/book/line_editor.md +++ b/zh-CN/book/line_editor.md @@ -1,170 +1,209 @@ # Reedline,Nu 的行编辑器 -Nushell 的行编辑器 [Reedline](https://github.com/nushell/reedline) 是一个跨平台的行读取器,它被设计为模块化且颇具灵活性的。该引擎的作用是负责控制命令历史,验证,自动补全,提示以及屏幕绘制。 +Nushell 的行编辑器 [Reedline](https://github.com/nushell/reedline) 是一个跨平台的行读取器,它被设计为模块化且颇具灵活性的。该行编辑器负责控制命令历史、验证、自动补全、提示、屏幕绘制等。 -## 配置 +[[toc]] -### 编辑模式 +## 多行编辑 -Reedline 允许你使用两种模式来编辑文本:vi 和 emacs。如果没有指定,默认的编辑模式是 emacs 模式。若要自行设置喜欢的模式,你可以修改配置文件为相应模式。比如: +Reedline 允许 Nushell 命令行跨越多行。这可以通过几种方法实现: -```nu - $env.config = { - ... - edit_mode: emacs - ... - } -``` +1. 当括号表达式打开时按 Enter。 -#### 默认键盘绑定 - -每种编辑模式都有相应的 vi 或 emacs 文本编辑的常用快捷键设置。 - -Emacs 和 Vi 快捷键绑定 - -| 快捷键 | 事件 | -| ----------- | --------------------- | -| Esc | Esc | -| Backspace | 退格 | -| End | 移至行尾 | -| End | 补全历史提示 | -| Home | 移至行首 | -| Ctr + c | 取消当前行 | -| Ctr + l | 清除屏幕 | -| Ctr + r | 搜索历史 | -| Ctr + Right | Complete history word | -| Ctr + Right | 右移一个词 | -| Ctr + Left | 左移一个词 | -| Up | 菜单上移 | -| Up | 上移 | -| Down | 菜单下移 | -| Down | 下移 | -| Left | 菜单左移 | -| Left | 左移 | -| Right | 完成历史提示 | -| Right | 菜单右移 | -| Right | 右移 | -| Ctr + b | 菜单左移 | -| Ctr + b | 左移 | -| Ctr + f | 完成历史提示 | -| Ctr + f | 菜单右移 | -| Ctr + f | 右移 | -| Ctr + p | 菜单上移 | -| Ctr + p | 上移 | -| Ctr + n | 菜单下移 | -| Ctr + n | 下移 | - -Vi 普通键绑定 - -| 快捷键 | 事件 | -| ------- | ---------- | -| Ctr + c | 取消当前行 | -| Ctr + l | 清除屏幕 | -| Up | 菜单上移 | -| Up | 上移 | -| Down | 菜单下移 | -| Down | 下移 | -| Left | 菜单左移 | -| Left | 左移 | -| Right | 菜单右移 | -| Right | 右移 | - -除了之前的键盘绑定,在正常 Vi 模式下,你可以使用经典的 Vi 快捷键来进行移动或者执行相应的动作。可用的组合的选项是: - -Vi 正常移动快捷键 - -| 快捷键 | 移动 | -| ------ | -------------------- | -| w | 前移一个单词 | -| d | 移动到行尾 | -| 0 | 移动到行首 | -| $ | 移动到行尾 | -| f | 行内向右查找字符 | -| t | 行内右移到指定字符前 | -| F | 行内向左查找字符 | -| T | 行内左移到指定字符前 | - -Vi 正常操作快捷键 - -| 快捷键 | 操作 | -| ------ | ------------------ | -| d | 删除 | -| p | 在光标之后粘贴 | -| P | 在光标之前粘贴 | -| h | 左移 | -| l | 右移 | -| j | 下移 | -| k | 上移 | -| w | 右移一个单词 | -| b | 左移一个单词 | -| i | 在光标前插入 | -| a | 在光标后插入 | -| 0 | 移到行首 | -| ^ | 移到行首 | -| $ | 移到行尾 | -| u | 撤销 | -| c | 修改 | -| x | 删除字符 | -| s | 搜索历史 | -| D | 删除当前位置到行尾 | -| A | 在当前行最后插入 | - -### 命令历史 + 例如: -如前所述,Reedline 管理并存储所有被编辑并发送给 Nushell 的命令。要配置 Reedline 可以存储的最大记录数,你需要在配置文件中调整这个值: + ```nu + def my-command [] { + ``` -```nu - $env.config = { - ... - history: { - ... - max_size: 1000 - ... + 在开括号后按 Enter 将插入一个换行符。这同样适用于打开的(且有效的)`(` 和 `[` 表达式。 + + 这通常用于创建代码块和闭包(如上所述),也用于列表、记录和表格字面量: + + ```nu + let file = { + name: 'repos.sqlite' + hash: 'b939a3fa4ca011ca1aa3548420e78cee' + version: '1.4.2' } - ... - } -``` + ``` -### 定制你的提示 + 它甚至可以用来将单个命令延续到多行: -Reedline 的提示语也是高度可定制的。为了构建你的完美提示符,你可以在配置文件中定义下面的环境变量: + ::: details 示例 -```nu -# Use nushell functions to define your right and left prompt -def create_left_prompt [] { - let path_segment = ($env.PWD) + ```nu + ( + tar + -cvz + -f archive.tgz + --exclude='*.temp' + --directory=../project/ + ./ + ) + ``` - $path_segment -} + ::: -def create_right_prompt [] { - let time_segment = ([ - (date now | format date '%m/%d/%Y %r') - ] | str join) +2. 在行尾有管道符(`|`)时按 Enter。 - $time_segment -} + ```nu + ls | + where name =~ '^[0-9]' | # 尾随管道符后的注释是可以的 + get name | + mv ...$in ./backups/ + ``` + +3. 使用 Alt+EnterShift+Enter 手动插入换行符。 + + 这可以用来创建前一个命令行的更易读的版本: + + ```nu + ls + | where name =~ '^[0-9]' # 以数字开头的文件 + | get name + | mv ...$in ./backups/ + ``` -$env.PROMPT_COMMAND = { create_left_prompt } -$env.PROMPT_COMMAND_RIGHT = { create_right_prompt } + ::: tip 提示 + 这些快捷键中的一个或两个可能会被终端应用程序或窗口管理器拦截。例如,Windows Terminal(以及 Windows 上的大多数其他终端应用程序)将 Alt+Enter 分配给将终端扩展到全屏。如果你的终端中这两个快捷键都不起作用,你可以为以下事件分配一个不同的快捷键: + + ```nu + event: { edit: insertnewline } + ``` + + 有关更多详细信息,请参阅下面的[按键绑定](#按键绑定)。 + + ::: + +4. 按 Ctrl+O 会在你的编辑器中打开当前的命令行。保存生成的文件并退出编辑器将用结果更新命令行。 + +## 设置编辑模式 + +Reedline 允许你使用两种模式来编辑文本:Vi 和 Emacs。如果没有指定,默认的编辑模式是 Emacs。若要自行设置喜欢的模式,你可以使用 `edit_mode` 设置。 + +```nu +$env.config.edit_mode = 'vi' ``` -::: tip -你并非必须要用 Nushell 的函数来定义环境变量,也可以使用简单的字符串来定义它们。 +这可以在命令行中更改,也可以保存在 `config.nu` 中。 + +::: note 提示 +Vi 是一个具有“普通”模式和“插入”模式的“模态”编辑器。我们建议在使用 Nushell 中的 Vi 模式之前,通过使用 Vim 或 Neovim 编辑器来熟悉这些模式。每个编辑器都有一个内置的教程,涵盖了模态编辑的基础知识(及更多)。 ::: -你也可以通过修改以下环境变量来定制行编辑器的提示符: +## 默认键盘绑定 + +每种编辑模式都有相应的 Vi 或 Emacs 文本编辑的常用快捷键设置。 + +### Emacs 和 Vi 插入模式快捷键 + +这些快捷键事件适用于 Emacs 和 Vi 插入模式: + +| 快捷键 | 事件 | +| ------------------------------------- | ------------------ | +| Shift+Enter | 插入换行符 | +| Alt+Enter | 插入换行符 | +| Backspace | 退格 | +| End | 移至行尾 | +| End | 补全历史提示 | +| Home | 移至行首 | +| Ctrl+C | 取消当前行 | +| Ctrl+L | 清除屏幕 | +| Ctrl+R | 搜索历史 | +| Ctrl+ (右箭头) | 补全历史单词 | +| Ctrl+ (右箭头) | 右移一个词 | +| Ctrl+ (左箭头) | 左移一个词 | +| (上箭头) | 上移 | +| (下箭头) | 下移 | +| (左箭头) | 左移 | +| (右箭头) | 右移 | +| Ctrl+P | 上移 | +| Ctrl+N | 下移 | +| Ctrl+B | 左移 | +| Ctrl+F | 右移 | +| (右箭头) | 完成历史提示 | +| Ctrl+F | 完成历史提示 | +| Alt+F | 完成历史提示一个词 | +| Alt+ (左箭头) | 减少一个历史提示词 | + +### Vi 插入模式快捷键 + +这些快捷键事件仅适用于 Vi 插入模式: + +| 快捷键 | 事件 | +| -------------- | ------------------ | +| Esc | 切换到 Vi 普通模式 | + +### Vi 普通模式快捷键 + +这些快捷键事件仅适用于 Vi 普通模式: + +| 快捷键 | 事件 | +| ------------------------------------- | ---------- | +| Ctrl+C | 取消当前行 | +| Ctrl+L | 清除屏幕 | +| (上箭头) | 上移 | +| (下箭头) | 下移 | +| (左箭头) | 左移 | +| (右箭头) | 右移 | +| Ctrl+ (右箭头) | 右移一个词 | +| Ctrl+ (左箭头) | 左移一个词 | + +### Vi 普通模式移动 + +与 Vi 一样,许多移动和操作可以在普通模式下与可选的计数结合使用。例如,3dw 删除接下来的三个单词。 + +| 快捷键 | 移动 | +| -------------------------------------- | ---------------------------- | +| w | 移动到下一个单词的开头 | +| e | 移动到当前或下一个单词的结尾 | +| b | 移动到当前或上一个单词的开头 | +| 0 | 移动到行首 | +| $ | 移动到行尾 | +| h | 左移 | +| l | 右移 | +| j | 下移 | +| k | 上移 | +| f+\ | 向右移动到 \ | +| t+\ | 向右移动到 \ 之前 | +| Shift+F+\ | 向左移动到 \ | +| Shift+T+\ | 向左移动到 \ 之后 | + +### Vi 普通模式操作 + +这些操作可以与上面的许多[移动](#vi-普通模式移动)结合使用。 + +| 快捷键 | 操作 | +| ----------------------------- | ------------------------------------ | +| d | 删除 | +| Shift+D | 删除到行尾 | +| p | 在当前字符后粘贴 | +| Shift+P | 在当前字符前粘贴 | +| i | 在当前字符处进入 Vi 插入模式(追加) | +| Shift+I | 在行首进入插入模式 | +| a | 在当前字符后追加 | +| Shift+A | 追加到行尾 | +| 0 | 移动到行首 | +| ^ | 移动到行首 | +| $ | 移动到行尾 | +| c | 修改 | +| r | 替换 | +| s | 替换字符 | +| x | 删除字符 | +| u | 撤销 | + +## 命令历史 + +如前所述,Reedline 管理并存储所有被编辑并发送给 Nushell 的命令。要配置 Reedline 可以存储的最大记录数,你需要在配置文件中调整这个值: ```nu -$env.PROMPT_INDICATOR = "〉" -$env.PROMPT_INDICATOR_VI_INSERT = ": " -$env.PROMPT_INDICATOR_VI_NORMAL = "〉" -$env.PROMPT_MULTILINE_INDICATOR = "::: " +$env.config.history.max_size = 1000 ``` -::: tip -提示符是环境变量,它代表了提示的状态 -::: +## 定制提示 + +Reedline 提示符是使用多个环境变量配置的。有关详细信息,请参阅[提示符配置](./configuration.md#prompt-configuration)。 ## 按键绑定 @@ -173,21 +212,13 @@ Reedline 按键绑定是一个强大的结构,它允许你建立一连串的 例如,我们假设你想把补全菜单绑定到 `Ctrl + t` 这组快捷键上(默认是`tab`)。你可以添加下面的条目到你的配置文件: ```nu - $env.config = { - ... - - keybindings: [ - { - name: completion_menu - modifier: control - keycode: char_t - mode: emacs - event: { send: menu name: completion_menu } - } - ] - - ... - } +$env.config.keybindings ++= [{ + name: completion_menu + modifier: control + keycode: char_t + mode: emacs + event: { send: menu name: completion_menu } +}] ``` 在加载这个新的 `config.nu` 之后,你的新键盘绑定(`Ctrl + t`)将打开命令自动补全。 @@ -200,8 +231,14 @@ Reedline 按键绑定是一个强大的结构,它允许你建立一连串的 - control - alt - shift - - control | alt - - control | alt | shift + - shift_alt + - alt_shift + - control_alt + - alt_control + - control_shift + - shift_control + - control_alt_shift + - control_shift_alt - keycode: 这代表要按下的键 - mode: emacs, vi_insert, vi_normal (一个单独的字符串或一个列表,例如: [`vi_insert` `vi_normal`]) - event: 键盘绑定要发送的事件的类型。选项包括: @@ -209,11 +246,11 @@ Reedline 按键绑定是一个强大的结构,它允许你建立一连串的 - edit - until -::: tip +::: tip 提示 所有可用的修饰键、键码和事件都可以通过`keybindings list`命令找到。 ::: -::: tip +::: tip 提示 添加到 `vi_insert` 模式中的按键绑定将在行编辑器处于插入模式(可以写入文本)时可用,而标有 `vi_normal` 模式的按键绑定将在正常模式下(当光标使用 h、j、k 或 l 移动时)可用。 ::: @@ -236,55 +273,40 @@ Reedline 按键绑定是一个强大的结构,它允许你建立一连串的 ... ``` -上述第一个按键绑定例子遵循第一种情况,只有一个事件被发送到引擎。 +本页中显示的第一个按键绑定示例遵循第一种情况;一个单一的事件被发送到引擎。 -后一个按键绑定的例子是向引擎发送一系列的事件。它首先清除提示,插入一个字符串,然后输入该值。 +下一个按键绑定的例子是向引擎发送一系列的事件。它首先清除提示,插入一个字符串,然后输入该值: ```nu - $env.config = { - ... - - keybindings: [ - { - name: change_dir_with_fzf - modifier: CONTROL - keycode: Char_t - mode: emacs - event:[ - { edit: Clear } - { edit: InsertString, - value: "cd (ls | where type == dir | each { |row| $row.name} | str join (char nl) | fzf | decode utf-8 | str trim)" - - } - { send: Enter } - ] - } - - ... - } +$env.config.keybindings ++= [{ + name: change_dir_with_fzf + modifier: CONTROL + keycode: Char_t + mode: emacs + event: [ + { edit: Clear } + { + edit: InsertString, + value: "cd (ls | where type == dir | each { |row| $row.name} | str join (char nl) | fzf | decode utf-8 | str trim)" + } + { send: Enter } + ] +}] ``` -上面按键绑定的缺点是,插入的文本将被验证处理并保存在历史记录中,这使得按键绑定的执行速度有点慢,而且会用相同的命令来填充命令历史。出于这个原因,可以采用 `ExecuteHostCommand` 类型的事件。下一个例子以更简单的方式做了与前一个相同的事情,发送了一个单一的事件给引擎: +上面按键绑定的缺点是,插入的文本将被验证处理并保存在历史记录中,这使得按键绑定的执行速度有点慢,而且会用相同的命令来填充命令历史。出于这个原因,可以采用 `executehostcommand` 类型的事件。下一个例子以更简单的方式做了与前一个相同的事情,发送了一个单一的事件给引擎: ```nu - $env.config = { - ... - - keybindings: [ - { - name: change_dir_with_fzf - modifier: CONTROL - keycode: Char_y - mode: emacs - event: { - send: ExecuteHostCommand, +$env.config.keybindings ++= [{ + name: change_dir_with_fzf + modifier: CONTROL + keycode: Char_y + mode: emacs + event: { + send: executehostcommand, cmd: "cd (ls | where type == dir | each { |row| $row.name} | str join (char nl) | fzf | decode utf-8 | str trim)" - } } - ] - - ... - } +}] ``` 在我们继续之前,你一定已经注意到,编辑和发送的语法发生了变化,因此有必要对它们进行更多的解释。 `send` 是所有可以被引擎处理的 `Reedline` 事件,而 `edit` 是所有可以被引擎处理的 `EditCommands`。 @@ -305,7 +327,7 @@ keybindings list | where type == events ... ``` -::: tip +::: tip 提示 你可以用大写字母来命名事件的名称,键盘绑定解析器是不区分大小写的。 ::: @@ -325,7 +347,7 @@ keybindings list | where type == events ```nu ... event: { - send: ExecuteHostCommand + send: executehostcommand cmd: "cd ~" } ... @@ -354,7 +376,7 @@ keybindings list | where type == edits ```nu ... event: { - edit: InsertString + edit: insertstring value: "MY NEW STRING" } ... @@ -365,7 +387,7 @@ keybindings list | where type == edits ```nu ... event: { - edit: MoveRightUntil + edit: moverightuntil value: "S" } ... @@ -384,26 +406,18 @@ keybindings list | where type == edits 下一个键盘绑定就是这种情况: ```nu - $env.config = { - ... - - keybindings: [ - { - name: completion_menu - modifier: control - keycode: char_t - mode: emacs - event: { - until: [ - { send: menu name: completion_menu } - { send: MenuNext } - ] - } - } - ] - - ... - } +$env.config.keybindings ++= [{ + name: completion_menu + modifier: control + keycode: char_t + mode: emacs + event: { + until: [ + { send: menu name: completion_menu } + { send: menunext } + ] + } +}] ``` 上面的按键绑定将首先尝试打开一个补全菜单。如果菜单没有激活,它将激活它并发送一个成功信号。如果再次按下按键绑定,因为已经有一个激活的菜单,那么它将发送的下一个事件是`MenuNext`,这意味着它将把选择器移动到菜单的下一个元素。 @@ -413,27 +427,19 @@ keybindings list | where type == edits 例如,下一个按键绑定将总是发送一个`down`,因为该事件总是成功的。 ```nu - $env.config = { - ... - - keybindings: [ - { - name: completion_menu - modifier: control - keycode: char_t - mode: emacs - event: { - until: [ +$env.config.keybindings ++= [{ + name: completion_menu + modifier: control + keycode: char_t + mode: emacs + event: { + until: [ { send: down } { send: menu name: completion_menu } { send: menunext } - ] - } - } - ] - - ... - } + ] + } +}] ``` ### 移除一个默认的按键绑定 @@ -443,20 +449,12 @@ keybindings list | where type == edits 例如,在所有的编辑模式下,禁用 `Ctrl + l` 清除屏幕: ```nu - $env.config = { - ... - - keybindings: [ - { - modifier: control - keycode: char_l - mode: [emacs, vi_normal, vi_insert] - event: null - } - ] - - ... - } +$env.config.keybindings ++= [{ + modifier: control + keycode: char_l + mode: [emacs, vi_normal, vi_insert] + event: null +}] ``` ### 排查键盘绑定问题 @@ -468,41 +466,55 @@ keybindings list | where type == edits 感谢 Reedline,Nushell 的菜单可以帮助你完成日常的 Shell 脚本操作。接下来我们介绍一下在使用 Nushell 时一直可用的默认菜单。 +### 菜单快捷键 + +当菜单激活时,一些快捷键会根据上面讨论的[快捷键 `until` 说明符](#until类型)而改变。菜单的通用快捷键是: + +| 快捷键 | 事件 | +| ------------------------------- | -------------- | +| Tab | 选择下一个项目 | +| Shift+Tab | 选择上一个项目 | +| Enter | 接受选择 | +| (上箭头) | 菜单上移 | +| (下箭头) | 菜单下移 | +| (左箭头) | 菜单左移 | +| (右箭头) | 菜单右移 | +| Ctrl+P | 菜单上移 | +| Ctrl+N | 菜单下移 | +| Ctrl+B | 菜单左移 | +| Ctrl+F | 菜单右移 | + +::: note 提示 +菜单方向行为因菜单类型而异(见下文)。例如,在 `description` 菜单中,“上”和“下”适用于“额外”列表,但在 `list` 菜单中,方向适用于选择。 +::: + ### 帮助菜单 -帮助菜单的存在是为了方便你过渡到 Nushell。假设你正在组建一个惊人的管道,然后你忘记了反转一个字符串的内部命令。你可以用`ctr+q`来激活帮助菜单,而不是删除你的管道。一旦激活,只需输入你要找的命令的关键词,菜单就会显示与你的输入相匹配的命令,而匹配的依据就是命令的名称或描述。 +帮助菜单的存在是为了方便你过渡到 Nushell。假设你正在组建一个惊人的管道,然后你忘记了反转一个字符串的内部命令。你可以用`F1`来激活帮助菜单,而不是删除你的管道。一旦激活,只需输入你要找的命令的关键词,菜单就会显示与你的输入相匹配的命令,而匹配的依据就是命令的名称或描述。 要浏览菜单,你可以用`tab`选择下一个元素,你可以按左键或右键滚动描述,你甚至可以在行中粘贴可用的命令例子。 帮助菜单可以通过修改以下参数进行配置: ```nu - $env.config = { - ... - - menus = [ - ... - { - name: help_menu - only_buffer_difference: true # Search is done on the text written after activating the menu - marker: "? " # Indicator that appears with the menu is active - type: { - layout: description # Type of menu - columns: 4 # Number of columns where the options are displayed - col_width: 20 # Optional value. If missing all the screen width is used to calculate column width - col_padding: 2 # Padding between columns - selection_rows: 4 # Number of rows allowed to display found options - description_rows: 10 # Number of rows allowed to display command description - } - style: { - text: green # Text style - selected_text: green_reverse # Text style for selected option - description_text: yellow # Text style for description - } - } - ... - ] - ... +$env.config.menus ++= [{ + name: help_menu + only_buffer_difference: true # 搜索是在激活菜单后输入的文本上进行的 + marker: "? " # 菜单激活时出现的指示符 + type: { + layout: description # 菜单类型 + columns: 4 # 显示选项的列数 + col_width: 20 # 可选值。如果缺少,则使用所有屏幕宽度来计算列宽 + col_padding: 2 # 列之间的填充 + selection_rows: 4 # 允许显示找到的选项的行数 + description_rows: 10 # 允许显示命令描述的行数 + } + style: { + text: green # 文本样式 + selected_text: green_reverse # 选定选项的文本样式 + description_text: yellow # 描述的文本样式 + } +}] ``` ### 补全菜单 @@ -512,63 +524,47 @@ keybindings list | where type == edits 默认情况下,补全菜单是通过按`tab`访问的,它可以通过修改配置对象中的这些值来进行配置: ```nu - $env.config = { - ... - - menus = [ - ... - { - name: completion_menu - only_buffer_difference: false # Search is done on the text written after activating the menu - marker: "| " # Indicator that appears with the menu is active - type: { - layout: columnar # Type of menu - columns: 4 # Number of columns where the options are displayed - col_width: 20 # Optional value. If missing all the screen width is used to calculate column width - col_padding: 2 # Padding between columns - } - style: { - text: green # Text style - selected_text: green_reverse # Text style for selected option - description_text: yellow # Text style for description - } - } - ... - ] - ... +$env.config.menus ++= [{ + name: completion_menu + only_buffer_difference: false # 搜索是在激活菜单后输入的文本上进行的 + marker: "| " # 菜单激活时出现的指示符 + type: { + layout: columnar # 菜单类型 + columns: 4 # 显示选项的列数 + col_width: 20 # 可选值。如果缺少,则使用所有屏幕宽度来计算列宽 + col_padding: 2 # 列之间的填充 + } + style: { + text: green # 文本样式 + selected_text: green_reverse # 选定选项的文本样式 + description_text: yellow # 描述的文本样式 + } +}] ``` 通过修改这些参数,你可以根据自己的喜好定制你的菜单布局。 ### 历史菜单 -历史菜单是访问编辑器命令历史的一个便捷方法。当激活菜单时(默认为`Ctrl+x`),命令的历史会以时间倒序显示,这使得选择前一个命令变得非常容易。 +历史菜单是访问编辑器命令历史的一个便捷方法。当激活菜单时(默认为`Ctrl+r`),命令的历史会以时间倒序显示,这使得选择前一个命令变得非常容易。 历史菜单可以通过修改配置对象中的这些值进行配置: ```nu - $env.config = { - ... - - menus = [ - ... - { - name: help_menu - only_buffer_difference: true # Search is done on the text written after activating the menu - marker: "? " # Indicator that appears with the menu is active - type: { - layout: list # Type of menu - page_size: 10 # Number of entries that will presented when activating the menu - } - style: { - text: green # Text style - selected_text: green_reverse # Text style for selected option - description_text: yellow # Text style for description - } - } - ... - ] - ... +$env.config.menus ++= [{ + name: history_menu + only_buffer_difference: true # 搜索是在激活菜单后输入的文本上进行的 + marker: "? " # 菜单激活时出现的指示符 + type: { + layout: list # 菜单类型 + page_size: 10 # 激活菜单时将呈现的条目数 + } + style: { + text: green # 文本样式 + selected_text: green_reverse # 选定选项的文本样式 + description_text: yellow # 描述的文本样式 + } +}] ``` 当历史菜单被激活时,它从历史中拉出`page_size`个记录并在菜单中呈现。如果终端还有空间,当你再次按`Ctrl+x`时,菜单将拉出相同数量的记录,并将它们追加到当前页。如果不可能呈现所有拉出的记录,菜单将创建一个新的页面。可以通过按`Ctrl+z`转到上一页或`Ctrl+x`转到下一页来浏览这些页面。 @@ -620,34 +616,26 @@ let a = (ls | where size > 10MiB) 满足这些所需的菜单将看起来像这样: ```nu - $env.config = { - ... - - menus = [ - ... - { - name: vars_menu - only_buffer_difference: true - marker: "# " - type: { - layout: list - page_size: 10 - } - style: { - text: green - selected_text: green_reverse - description_text: yellow - } - source: { |buffer, position| - scope variables - | where name =~ $buffer - | sort-by name - | each { |row| {value: $row.name description: $row.type} } - } - } - ... - ] - ... +$env.config.menus ++= [{ + name: vars_menu + only_buffer_difference: true + marker: "# " + type: { + layout: list + page_size: 10 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + source: { |buffer, position| + scope variables + | where name =~ $buffer + | sort-by name + | each { |row| {value: $row.name description: $row.type} } + } +}] ``` 正如你所看到的,新的菜单与之前描述的`history_menu`是相同的,唯一的区别是新的字段叫`source`。`source`字段是 Nushell 所定义的,它包含了你想在菜单中显示的值。对于这个菜单,我们从`scope variables`中提取数据,然后用它来创建记录并填充菜单。 @@ -656,13 +644,13 @@ let a = (ls | where size > 10MiB) ```nu { - value: # The value that will be inserted in the buffer - description: # Optional. Description that will be display with the selected value - span: { # Optional. Span indicating what section of the string will be replaced by the value + value: # 将插入缓冲区的值 + description: # 可选。将与所选值一起显示的描述 + span: { # 可选。指示将被值替换的字符串部分的范围 start: end: } - extra: [string] # Optional. A list of strings that will be displayed with the selected value. Only works with a description menu + extra: [string] # 可选。将与所选值一起显示的字符串列表。仅适用于描述菜单 } ``` @@ -677,36 +665,30 @@ let a = (ls | where size > 10MiB) 如果你想改变两个菜单的默认激活方式,可以通过定义新的按键绑定来实现。例如,接下来的两个按键绑定设置分别将`Ctrl+t`和`Ctrl+y`定义为触发自动补全和历史菜单: ```nu - $env.config = { - ... - - keybindings: [ - { +$env.config.keybindings ++= [ + { name: completion_menu modifier: control keycode: char_t mode: [vi_insert vi_normal] event: { - until: [ - { send: menu name: completion_menu } - { send: menupagenext } - ] + until: [ + { send: menu name: completion_menu } + { send: menupagenext } + ] } - } - { + } + { name: history_menu modifier: control keycode: char_y mode: [vi_insert vi_normal] event: { - until: [ - { send: menu name: history_menu } - { send: menupagenext } - ] + until: [ + { send: menu name: history_menu } + { send: menupagenext } + ] } - } - ] - - ... - } + } +] ``` diff --git a/zh-CN/book/loading_data.md b/zh-CN/book/loading_data.md index 2d32dca5d70..127d57d8d2b 100644 --- a/zh-CN/book/loading_data.md +++ b/zh-CN/book/loading_data.md @@ -24,8 +24,9 @@ Nu 目前支持直接从以下格式加载表数据: - ics - ini - json -- nuon +- [nuon](#nuon) - ods +- [SQLite 数据库](#sqlite) - ssv - toml - tsv @@ -35,6 +36,11 @@ Nu 目前支持直接从以下格式加载表数据: - xml - yaml / yml +::: tip 你知道吗? +在底层,`open` 会在你的作用域中查找与文件扩展名匹配的 `from ...` 子命令。 +因此,你可以通过创建自己的 `from ...` 子命令来简单地扩展 `open` 支持的文件类型集。 +::: + 但是,当你加载其他的文本文件时会发生什么呢?让我们试一试: ```nu @@ -45,6 +51,46 @@ open README.md 本质上,这些文本文件对 Nu 来说就是一个大字符串。接下来,我们将讨论如何处理这些字符串,以便从中获得我们需要的数据。 +## NUON + +Nushell 对象表示法(Nushell Object Notation,NUON)旨在成为 Nushell 的 JavaScript 对象表示法(JavaScript Object Notation,JSON)。 +也就是说,NUON 代码是描述某种数据结构的有效 Nushell 代码。 +例如,这是一个有效的 NUON(示例来自[默认配置文件](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/default_config.nu)): + +```nu +{ + menus: [ + # 默认 nushell 菜单的配置 + # 注意缺少 source 参数 + { + name: completion_menu + only_buffer_difference: false + marker: "| " + type: { + layout: columnar + columns: 4 + col_width: 20 # 可选值。如果缺少,则使用所有屏幕宽度来计算列宽 + col_padding: 2 + } + style: { + text: green + selected_text: green_reverse + description_text: yellow + } + } + ] +} +``` + +你可能会注意到它与 JSON 非常相似,你是对的。 +**NUON 是 JSON 的超集!** +也就是说,任何 JSON 代码都是有效的 NUON 代码,因此也是有效的 Nushell 代码。 +与 JSON 相比,NUON 更“人性化”。 +例如,允许注释,并且不需要逗号。 + +目前 NUON 的一个限制是它不能表示所有的 Nushell [数据类型](types_of_data.md)。 +最值得注意的是,NUON 不允许序列化代码块。 + ## 处理字符串 处理来自 Nu 外部数据时一个普遍情况是,它并不总是以 Nu 理解的格式出现。通常这些数据是以字符串的形式提供给我们的。 @@ -136,13 +182,12 @@ open people.txt | lines | split column "|" first_name last_name job | str trim | 其他可用于字符串的命令有: -- `str` +- [`str`](/commands/docs/str.md) - [`lines`](/commands/docs/lines.md) -- ~~`size`~~ 如果我们已经知道待处理的数据具有 Nu 能够理解的格式,则可以使用一些辅助命令,例如,我们打开一个 Rust 的 Cargo.lock 文件: -```toml +```nu open Cargo.lock # => # This file is automatically @generated by Cargo. # => # It is not intended for manual editing. @@ -151,7 +196,7 @@ open Cargo.lock # => version = "0.1.2" ``` -"Cargo.lock" 实际上是一个 .toml 文件,但是文件扩展名不是 .toml。没关系,我们可以使用 `from toml` 命令: +"Cargo.lock" 实际上是一个 .toml 文件,但是文件扩展名不是 .toml。没关系,我们可以使用 `from` 命令和 `toml` 子命令: @[code](@snippets/loading_data/cargo-toml.sh) @@ -161,18 +206,39 @@ open Cargo.lock 虽然能够打开一个文件并立即使用其数据表很有帮助,但这并不总是我们想要的。为了获得原始文本,[`open`](/commands/docs/open.md)命令可以接受一个可选的`--raw`标志: -```toml +```nu open Cargo.toml --raw -# => [package] -# => name = "nu" +# => [package] name = "nu" # => version = "0.1.3" # => authors = ["Yehuda Katz ", "Sophia Turner <547158+sophiajt@users.noreply.github.com>"] # => description = "A shell for the GitHub era" # => license = "MIT" ``` +## SQLite + +SQLite 数据库会被 [`open`](/commands/docs/open.md) 自动检测,无论其文件扩展名是什么。你可以打开整个数据库: + +```nu +open foo.db +``` + +或者 [`get`](/commands/docs/get.md) 一个特定的表: + +```nu +open foo.db | get some_table +``` + +或者运行任何你喜欢的 SQL 查询: + +```nu +open foo.db | query db "select * from some_table" +``` + +(注意:一些旧版本的 Nu 使用 `into db | query` 而不是 `query db`) + ## 获取 URLs -除了从文件系统中加载文件,你还可以通过使用[`http get`](/commands/docs/http_get.md)命令来加载 URLs。这将从互联网上获取 URL 的内容并返回: +除了从文件系统中加载文件,你还可以通过使用[`http get`](/commands/docs/http.md)命令来加载 URLs。这将从互联网上获取 URL 的内容并返回: @[code](@snippets/loading_data/rust-lang-feed.sh) diff --git a/zh-CN/book/metadata.md b/zh-CN/book/metadata.md index 6ee28efcb4c..086b217399b 100644 --- a/zh-CN/book/metadata.md +++ b/zh-CN/book/metadata.md @@ -13,7 +13,7 @@ open Cargo.toml | from toml # => | ---------- object originates from here ``` -这个错误信息不仅告诉我们,我们给 `from toml` 的不是一个字符串,还告诉我们这个值最初来自哪里。那么它是如何知道的呢? +这个错误信息不仅告诉我们,我们给 [`from toml`](/commands/docs/from_toml.md) 的不是一个字符串,还告诉我们这个值最初来自哪里。那么它是如何知道的呢? 在 Nu 中流经管道的值通常有一些额外信息,或元数据,附加在它们身上。这些通常被称为标签,就像商店里商品上的标签一样。这些标签并不影响数据,但它们给了 Nu 一种方法来改善使用这些数据的体验。 diff --git a/zh-CN/book/modules.md b/zh-CN/book/modules.md index d34ced5280c..9824663131d 100644 --- a/zh-CN/book/modules.md +++ b/zh-CN/book/modules.md @@ -1,292 +1,15 @@ # 模块 -与其他许多编程语言类似,Nushell 也有模块,可以让你将自定义的命令导入到当前作用域中。 -然而,由于 Nushell 也是一个 Shell,模块还允许你导入环境变量,可以用来方便地激活/停用各种环境配置。 +与其他许多编程语言类似,Nushell 支持编写和使用*模块*,以此来更有效地组织代码。模块可以被认为是容纳各种定义的“容器”,包括: -_注意! 目前对模块的实现是相当基本的,并将在未来进一步扩展。例如,目前还不能从一个模块中导入另一个模块。_ +- 额外的自定义命令 +- 别名 +- 常量 +- 外部命令 +- 环境变量 +- 甚至其他模块(即子模块) -## 基础知识 +许多用户会从使用他人编写的模块开始,然后逐步编写自己的模块。本章涵盖了这两个主题: -一个简单的模块可以像这样定义: - -```nu -module greetings { - export def hello [name: string] { - $"hello ($name)!" - } - - export def hi [where: string] { - $"hi ($where)!" - } -} -``` - -或者在一个与你要创建的模块名相同的文件中: - -```nu -# greetings.nu - -export def hello [name: string] { - $"hello ($name)!" -} - -export def hi [where: string] { - $"hi ($where)!" -} -``` - -我们在 `greetings` 模块中定义了 `hello` 和 `hi` 两个自定义命令。`export`关键字使得以后可以从模块中导入该命令。 - -与[`def`](/commands/docs/def.md)类似,也可以用`export`关键字标记[`def --env`](/commands/docs/def.md)(你可以在[环境](environment.md)章节中了解更多关于[`def --env`](/commands/docs/def.md)的信息)。 - -## 使用模块 - -模块本身并不做任何事情,要使用模块导出的定义,我们需要[`use`](/commands/docs/use.md)它: - -```nu -use greetings - -# => greetings hello "world" -# => hello world! - -greetings hi "there" -# => hi there! -``` - -`hello`和`hi`命令现在可以通过`greetings`前缀被调用。 - -## 导入符号 - -一般来说,[`use`](/commands/docs/use.md)关键词后面的任何内容都会形成一个**导入模式**,它控制着符号的导入方式。 -导入模式可以是以下的一种: - -`use greetings` - -导入所有以模块名称为前缀的符号(我们在前面的例子中看到了这个)。 - -`use greetings hello` - -`hello`符号将被直接导入,没有任何前缀。 - -`use greetings [ hello, hi ] ` - -直接导入多个符号,没有任何前缀。 - -`use greetings *` - -你也可以使用模块名称和`*` glob 来直接导入所有的名称,且不需要任何前缀。 - -## 模块文件 - -Nushell 让你隐含地把一个源文件当作一个模块。 -让我们先把模块定义的主体保存到一个文件中: - -```nu -# greetings.nu - -export def hello [name: string] { - $"hello ($name)!" -} - -export def hi [where: string] { - $"hi ($where)!" -} -``` - -现在,你可以直接在文件上调用[`use`](/commands/docs/use.md): - -```nu -use greetings.nu - -# => greetings hello "world" -# => hello world! - -greetings hi "there" -# => hi there! -``` - -Nushell 会自动从文件名("greetings",没有".nu"扩展名)推断出模块的名称。所以你可以通过文件名而不是模块名配合使用上述任何导入模式来完成导入。 - -## 本地自定义命令 - -任何在模块中定义的自定义命令,如果没有`export`关键字,将只在该模块的作用域内工作: - -```nu -# greetings.nu - -export def hello [name: string] { - greetings-helper "hello" "world" -} - -export def hi [where: string] { - greetings-helper "hi" "there" -} - -def greetings-helper [greeting: string, subject: string] { - $"($greeting) ($subject)!" -} -``` - -然后,在 Nushell 里我们可以从 "greetings.nu" 中导入所有定义: - -```nu -use greetings.nu * - -# => hello "world" -# => hello world! - -# => hi "there" -# => hi there! - -greetings-helper "foo" "bar" # fails because 'greetings-helper' is not exported -``` - -## 环境变量 - -到目前为止,我们只是用模块来导入自定义命令,用同样的方法导出环境变量也是可能的。 -其语法与你可能习惯的直接修改 `$env` 或 [`load-env`](/commands/docs/load-env.md)等命令略有不同: - -```nu -# greetings.nu - -export env MYNAME { "Arthur, King of the Britons" } - -export def hello [name: string] { - $"hello ($name)" -} -``` - -`use` 的工作方式与自定义命令相同: - -```nu -use greetings.nu - -# => $env."greetings MYNAME" -# => Arthur, King of the Britons - -greetings hello $env."greetings MYNAME" -# => hello Arthur, King of the Britons! -``` - -你可能注意到我们没有直接给`MYNAME`赋值,相反,我们给了它一个代码块(`{ ...}`),它在我们每次调用[`use`](/commands/docs/use.md)时都会被执行。例如,我们可以用[`random`](/commands/docs/random.md)命令来演示这一点: - -```nu -module roll { export env ROLL { random dice | into string } } - -use roll ROLL - -# => $env.ROLL -# => 4 - -# => $env.ROLL -# => 4 - -use roll ROLL - -# => $env.ROLL -# => 6 - -$env.ROLL -# => 6 -``` - -## 导出符号 - -如上所述,你可以从模块中导出定义和环境变量。这可以让你更容易地将相关的定义分组,并导出你想公开的定义。 - -你还可以导出别名和外部声明(extern),并在需要时才使用这些功能。导出外部声明也让你有能力隐藏模块中的自定义自动补全命令,这样它们就不必成为全局命名空间的一部分。 - -下面是所有你可以导出的列表: - -- `export def` - 导出一个自定义命令 -- `export def --env` - 导出一个自定义环境命令 -- `export env` - 导出一个环境变量 -- `export alias` - 导出一个别名 -- `export extern` - 导出一个已知外部命令的定义 - -## 隐藏 - -任何自定义命令、别名或环境变量, 无论是否从模块中导入, 都可以被 "隐藏", 以恢复之前的定义。 -(注意,现在还不能从模块中导出别名,但它们仍然可以被隐藏。) -我们用[`hide`](/commands/docs/hide.md)命令来实现隐藏: - -```nu -def foo [] { "foo" } - -# => foo -# => foo - -hide foo - -foo # error! command not found! -``` - -[`hide`](/commands/docs/hide.md)命令也接受导入模式,就像[`use`](/commands/docs/use.md)那样。不过,导入模式的解释略有不同。它可以是下面中的一种: - -`hide foo` 或者 `hide greetings` - -- 如果该名称是一个自定义的命令或环境变量,则直接隐藏它。否则: -- 如果名字是一个模块的名称,则隐藏所有以模块名称为前缀的导出。 - -`hide greetings hello` - -- 隐藏带前缀的命令或环境变量 - -`hide greetings [hello, hi]` - -- 隐藏带前缀的若干个命令或环境变量 - -`hide greetings *` - -- 隐藏模块的所有的导出,不含前缀 - -让我们看几个例子。前面已经看到了直接隐藏一个自定义命令的例子,现在让我们试试环境变量: - -```nu -$env.FOO = "FOO" - -# => $env.FOO -# => FOO - -hide FOO - -$env.FOO # error! environment variable not found! -``` - -第一种情况也适用于从一个模块导入的命令/环境变量(使用上面定义的 "greetings.nu" 文件): - -```nu -use greetings.nu * - -# => $env.MYNAME -# => Arthur, King of the Britons - -# => hello "world" -# => hello world! - -hide MYNAME - -$env.MYNAME # error! environment variable not found! - -hide hello - -hello "world" # error! command not found! -``` - -最后,当名称为模块名时(假设是之前的`greetings`模块): - -```nu -use greetings.nu - -# => $env."greetings MYNAME" -# => Arthur, King of the Britons - -# => greetings hello "world" -# => hello world! - -hide greetings - -$env."greetings MYNAME" # error! environment variable not found! - -greetings hello "world" # error! command not found! -``` +- [使用模块](./modules/using_modules.md) +- [创建模块](./modules/creating_modules.md) diff --git a/zh-CN/book/modules/creating_modules.md b/zh-CN/book/modules/creating_modules.md new file mode 100644 index 00000000000..73ad1bd7b1d --- /dev/null +++ b/zh-CN/book/modules/creating_modules.md @@ -0,0 +1,553 @@ +# 创建模块 + +[[toc]] + +::: important +在下面的示例中工作时,建议在导入每个模块或命令的更新版本之前启动一个新的 shell。这将有助于减少由先前导入的定义引起的任何混淆。 +::: + +## 概述 + +模块(以及将在下面介绍的子模块)可以通过两种方式创建: + +- 最常见的是,通过创建一个包含一系列 `export` 语句的文件来导出要从模块导出的定义。 +- 对于模块内的子模块,使用 `module` 命令 + +::: tip +虽然可以使用 `module` 命令直接在命令行中创建模块,但将模块定义存储在文件中以供重用更有用且更常见。 +::: + +模块文件可以是: + +- 名为 `mod.nu` 的文件,在这种情况下,其*目录*成为模块名 +- 任何其他 `.nu` 文件,在这种情况下,文件名成为模块名 + +### 简单模块示例 + +创建一个名为 `inc.nu` 的文件,内容如下: + +```nu +export def increment []: int -> int { + $in + 1 +} +``` + +哈!这是一个模块!我们现在可以导入它并使用 `increment` 命令: + +```nu +use inc.nu * +5 | increment +# => 6 +``` + +当然,你可以轻松地分发这样的文件,以便其他人也可以使用该模块。 + +## 导出 + +我们在上面的模块概述中简要介绍了模块中可用的定义类型。虽然这对于最终用户来说可能已经足够了,但模块作者需要知道*如何*为以下内容创建导出定义: + +- 命令 ([`export def`](/commands/docs/export_def.md)) +- 别名 ([`export alias`](/commands/docs/export_alias.md)) +- 常量 ([`export const`](/commands/docs/export_const.md)) +- 已知外部命令 ([`export extern`](/commands/docs/export_extern.md)) +- 子模块 ([`export module`](/commands/docs/export_module.md)) +- 从其他模块导入的符号 ([`export use`](/commands/docs/export_use.md)) +- 环境设置 ([`export-env`](/commands/docs/export-env.md)) + +::: tip +只有标记为 `export`(或环境变量的 `export-env`)的定义在导入模块时才可访问。未标记为 `export` 的定义仅在模块内部可见。在某些语言中,这些被称为"私有"或"本地"定义。下面的[附加示例](#local-definitions)中有一个示例。 +::: + +### `main` 导出 + +::: important +导出不能与模块本身的名称相同。 +::: + +在上面的[基本示例](#basic-module-example)中,我们有一个名为 `inc` 的模块,其中有一个名为 `increment` 的命令。但是,如果我们将该文件重命名为 `increment.nu`,它将无法导入。 + +```nu +mv inc.nu increment.nu +use increment.nu * +# => Error: nu::parser::named_as_module +# => ... +# => help: Module increment can't export command named +# => the same as the module. Either change the module +# => name, or export `main` command. +``` + +就像错误消息中提到的那样,你可以简单地将导出重命名为 `main`,在这种情况下,导入时将采用模块的名称。编辑 `increment.nu` 文件: + +```nu +export def main []: int -> int { + $in + 1 +} +``` + +现在它按预期工作: + +```nu +use ./increment.nu +2024 | increment +# => 2025 +``` + +::: note +`main` 可以用于 `export def` 和 `export extern` 定义。 +::: + +::: tip +在以下情况下导入 `main` 定义: + +- 使用 `use ` 或 `use ` 导入整个模块 +- 使用 `*` 通配符导入模块的所有定义(例如,`use *` 等) +- 使用 `use main`、`use [main]` 等显式导入 `main` 定义 + +相反,以下形式*不*导入 `main` 定义: + +```nu +use +# or +use [ ] +``` + +::: + +::: note +此外,无论是否导出,`main` 在脚本文件中都有特殊行为。有关更多详细信息,请参见[脚本](../scripts.html#parameterizing-scripts)章节。 +::: + +## 模块文件 + +如上概述中简要提到的,模块可以通过以下两种方式创建: + +1. `.nu`:"文件形式" - 适用于简单模块 +2. `/mod.nu`:"目录形式" - 适用于组织更大的模块项目,其中子模块可以轻松映射到主模块的子目录 + +上面的 `increment.nu` 示例显然是(1)文件形式的示例。让我们尝试将其转换为目录形式: + +```nu +mkdir increment +mv increment.nu increment/mod.nu + +use increment * +41 | increment +# => 42 +``` + +请注意,一旦导入,无论使用文件形式还是目录形式,模块的行为都是相同的;只有其路径发生了变化。 + +::: note +从技术上讲,你可以使用目录形式或显式使用 `use increment/mod.nu *` 来导入它,但在使用 `mod.nu` 时首选目录简写。 +::: + +## 子命令 + +如[自定义命令](../custom_commands.md)中所述,子命令允许我们逻辑地分组命令。使用模块,可以通过两种方式完成此操作: + +1. 与任何自定义命令一样,命令可以定义为 `" "`,在引号内使用空格。让我们在上面的 `increment` 模块中添加一个 `increment by` 子命令: + +```nu +export def main []: int -> int { + $in + 1 +} + +export def "increment by" [amount: int]: int -> int { + $in + $amount +} +``` + +然后可以使用 `use increment *` 导入它来加载 `increment` 命令和 `increment by` 子命令。 + +2. 或者,我们可以简单地使用名称 `by` 来定义子命令,因为导入整个 `increment` 模块将导致相同的命令: + +```nu +export def main []: int -> int { + $in + 1 +} + +export def by [amount: int]: int -> int { + $in + $amount +} +``` + +此模块使用 `use increment`(不带通配符 `*`)导入,并产生相同的 `increment` 命令和 `increment by` 子命令。 + +::: note +我们将在下面的示例中继续使用此版本,因此请注意导入模式已更改为 `use increment`(而不是 `use increment *`)。 +::: + +## 子模块 + +子模块是从另一个模块导出的模块。有两种方法可以将子模块添加到模块中: + +1. 使用 `export module`:导出(a)子模块和(b)其子模块的成员 +2. 使用 `export use`:导出(a)子模块和(b)其父模块的成员 + +为了演示差异,让我们创建一个新的 `my-utils` 模块,并将我们的 `increment` 示例作为子模块。此外,我们将在其自己的子模块中创建一个新的 `range-into-list` 命令。 + +1. 为新 `my-utils` 创建目录,并将 `increment.nu` 移入其中 + + ```nu + mkdir my-utils + # 根据需要调整以下内容 + mv increment/mod.nu my-utils/increment.nu + rm increment + cd my-utils + ``` + +2. 在 `my-utils` 目录中,创建一个包含以下内容的 `range-into-list.nu` 文件: + + ```nu + export def main []: range -> list { + # 看起来有点奇怪,是的,但以下内容只是 + # 将范围转换为列表的简单方法 + each {||} + } + ``` + +3. 测试它: + +```nu +use range-into-list.nu +1..5 | range-into-list | describe +# => list (stream) +``` + +4. 我们现在应该有一个包含以下内容的 `my-utils` 目录: + + - `increment.nu` 模块 + - `range-into-list.nu` 模块 + +以下示例展示了如何创建带有子模块的模块。 + +### 示例:使用 `export module` 的子模块 + +子模块定义的最常见形式是使用 `export module`。 + +1. 创建一个名为 `my-utils` 的新模块。由于我们在 `my-utils` 目录中,我们将创建一个 `mod.nu` 来定义它。此版本的 `my-utils/mod.nu` 将包含: + + ```nu + export module ./increment.nu + export module ./range-into-list.nu + ``` + +2. 我们现在有一个带有两个子模块的 `my-utils` 模块。试试看: + +```nu +# 转到 my-utils 的父目录 +cd .. +use my-utils * +5 | increment by 4 +# => 9 + +let file_indices = 0..2..<10 | range-into-list +ls | select ...$file_indices +# => Returns the 1st, 3rd, 5th, 7th, and 9th file in the directory +``` + +在继续下一节之前,运行 `scope modules` 并查找 `my-utils` 模块。请注意,它本身没有命令;只有两个子模块。 + +### 示例:使用 `export use` 的子模块 + +或者,我们可以(重新)导出其他模块的*定义*。这与第一种形式略有不同,因为 `increment` 和 `range-into-list` 的命令(以及其他定义,如果存在)成为 `my-utils` 模块本身的成员。我们将能够在 `scope modules` 命令的输出中看到差异。 + +让我们将 `my-utils/mod.nu` 更改为: + +```nu +export use ./increment.nu +export use ./range-into-list.nu +``` + +使用与上面相同的命令进行尝试: + +```nu +# 转到 my-utils 的父目录 +cd .. +use my-utils * +5 | increment by 4 +# => 9 + +let file_indices = 0..2..<10 | range-into-list +ls / | sort-by modified | select ...$file_indices +# => Returns the 1st, 3rd, 5th, 7th, and 9th file in the directory, oldest-to-newest +``` + +再次运行 `scope modules`,请注意子模块的所有命令都重新导出到 `my-utils` 模块中。 + +::: tip +虽然 `export module` 是推荐且最常见的形式,但在一种模块设计场景中需要 `export use` - `export use` 可用于*选择性导出*子模块中的定义,而 `export module` 无法做到这一点。有关示例,请参见[附加示例 - 子模块的选择性导出](#selective-export-from-a-submodule)。 +::: + +::: note +没有 `export` 的 `module` 只定义本地模块;它不导出子模块。 +::: + +## 记录模块文档 + +与[自定义命令](../custom_commands.md#documenting-your-command)一样,模块可以包含文档,可以使用 `help ` 查看。文档只是模块文件开头的一系列注释行。让我们记录 `my-utils` 模块: + +```nu +# 一组有用的实用函数 + +export use ./increment.nu +export use ./range-into-list.nu +``` + +现在查看帮助: + +```nu +use my-utils * +help my-utils + +# => A collection of helpful utility functions +``` + +另请注意,由于 `increment` 和 `range-into-list` 的命令使用 `export use ...` 重新导出,这些命令也显示在主模块的帮助中。 + +## 环境变量 + +模块可以使用 [`export-env`](/commands/docs/export-env.md) 定义环境。让我们用上面定义的 `$env.NU_MODULES_DIR` 的常见目录扩展我们的 `my-utils` 模块,我们将在未来放置我们的模块。此目录默认位于[使用模块 - 模块路径](./using_modules.md#module-path)中讨论的 `$env.NU_LIB_DIRS` 搜索路径中。 + +```nu +# 一组有用的实用函数 + +export use ./increment.nu +export use ./range-into-list.nu + +export-env { + $env.NU_MODULES_DIR = ($nu.default-config-dir | path join "scripts") +} +``` + +当使用 `use` 导入此模块时,[`export-env`](/commands/docs/export-env.md) 块内的代码将运行,其环境将合并到当前作用域中: + +```nu +use my-utils +$env.NU_MODULES_DIR +# => Returns the directory name +cd $env.NU_MODULES_DIR +``` + +::: tip +与任何没有 `--env` 定义的命令一样,模块中的命令和其他定义使用它们自己的环境作用域。这允许在模块内部进行更改,而不会泄漏到用户的作用域中。在 `my-utils/mod.nu` 的底部添加以下内容: + +```nu +export def examine-config-dir [] { + # 更改 PWD 环境变量 + cd $nu.default-config-dir + ls +} +``` + +运行此命令会在模块中*本地*更改目录,但更改不会传播到父作用域。 + +::: + +## 注意事项 + +### `export-env` 仅在 `use` 调用被*求值*时运行 + +::: note +这种情况通常在创建使用 `std/log` 的模块时遇到。 +::: + +在另一个环境中导入模块的环境可能无法按预期工作。让我们创建一个名为 `go.nu` 的新模块,该模块创建到常见目录的"快捷方式"。其中之一将是上面在 `my-utils` 中定义的 `$env.NU_MODULES_DIR`。 + +我们可能会尝试: + +```nu +# go.nu,在 my-utils 的父目录中 +use my-utils + +export def --env home [] { + cd ~ +} + +export def --env modules [] { + cd $env.NU_MODULES_DIR +} +``` + +然后导入它: + +```nu +use go.nu +go home +# => Works +go modules +# => Error: $env.NU_MODULES_DIR is not found +``` + +这不起作用,因为在这种情况下的 `my-utils` 没有被*求值*;它仅在导入 `go.nu` 模块时被*解析*。虽然这会将所有其他导出带入作用域,但它不会*运行* `export-env` 块。 + +::: important +如本章开头所提到的,在 `my-utils`(及其 `$env.NU_MODULES_DIR`)仍然从先前的导入中在作用域内时尝试此操作将不会像预期的那样失败。在新的 shell 会话中进行测试以查看"正常"失败。 +::: + +为了将 `my-utils` 导出的环境带入 `go.nu` 模块的作用域,有两个选项: + +1. 在每个需要它的命令中导入模块 + + 通过将 `use my-utils` 放在 `go home` 命令本身中,当命令运行时,其 `export-env` 将被*求值*。例如: + + ```nu + # go.nu + export def --env home [] { + cd ~ + } + + export def --env modules [] { + use my-utils + cd $env.NU_MODULES_DIR + } + ``` + +2. 在 `go.nu` 模块的 `export-env` 块中导入 `my-utils` 环境 + + ```nu + use my-utils + export-env { + use my-utils [] + } + + export def --env home [] { + cd ~ + } + + export def --env modules [] { + cd $env.NU_MODULES_DIR + } + ``` + + 在上面的示例中,`go.nu` 两次导入 `my-utils`: + + 1. 第一个 `use my-utils` 将模块及其定义(环境除外)导入模块作用域。 + 2. 第二个 `use my-utils []` 除了环境外不导入任何内容到 `go.nu` 的导出环境块中。由于 `go.nu` 的 `export-env` 在首次导入模块时执行,因此 `use my-utils []` 也被求值。 + +请注意,第一种方法将 `my-utils` 环境保留在 `go.nu` 模块的作用域内。另一方面,第二种方法将 `my-utils` 环境重新导出到用户作用域中。 + +### 模块文件和命令不能以父模块命名 + +`.nu` 文件不能与其模块目录同名(例如,`spam/spam.nu`),因为这会创建名称被定义两次的歧义条件。这与上面描述的命令不能与其父级同名的情况类似。 + +## Windows 路径语法 + +::: important +Windows 上的 Nushell 支持正斜杠和反斜杠作为路径分隔符。但是,为了确保它们在所有平台上都能工作,强烈建议仅在模块中使用正斜杠 `/`。 +::: + +## 附加示例 + +### 本地定义 + +如上所述,没有 [`export`](/commands/docs/export.md) 关键字的模块中的定义仅在模块的作用域内可访问。 + +为了演示,创建一个新的模块 `is-alphanumeric.nu`。在此模块中,我们将创建一个 `str is-alphanumeric` 命令。如果字符串中的任何字符不是字母数字,则返回 `false`: + +```nu +# is-alphanumeric.nu +def alpha-num-range [] { + [ + ...(seq char 'a' 'z') + ...(seq char 'A' 'Z') + ...(seq 0 9 | each { into string }) + ] +} + +export def "str is-alphanumeric" []: string -> bool { + if ($in == '') { + false + } else { + let chars = (split chars) + $chars | all {|char| $char in (alpha-num-range)} + } +} +``` + +请注意,此模块中有两个定义 -- `alpha-num-range` 和 `str is-alphanumeric`,但只有第二个被导出。 + +```nu +use is-alphanumeric.nu * +'Word' | str is-alphanumeric +# => true +'Some punctuation?!' | str is-alphanumeric +# => false +'a' in (alpha-num-range) +# => Error: +# => help: `alpha-num-range` is neither a Nushell built-in or a known external command +``` + +### 子模块的选择性导出 + +::: note +虽然以下是一个罕见的用例,但此技术被标准库用于 +分别使 `dirs` 命令及其别名可用。 +::: + +如上[子模块](#submodules)部分所述,只有 `export use` 可以从子模块中选择性导出定义。 + +为了演示,让我们将上面[注意事项](#caveats)中的 `go.nu` 模块示例的修改形式添加到 `my-utils`: + +```nu +# go.nu,在 my-utils 目录中 +export def --env home [] { + cd ~ +} + +export def --env modules [] { + cd ($nu.default-config-dir | path join "scripts") +} + +export alias h = home +export alias m = modules +``` + +此 `go.nu` 包括以下来自原始版本的更改: + +- 它不依赖于 `my-utils` 模块,因为它现在将是 `my-utils` 的子模块 +- 它添加了"快捷方式"别名: + `h`:转到主目录(`go home` 的别名) + `m`:转到模块目录(`go modules` 的别名) + +用户可以使用以下方式*仅*导入别名: + +```nu +use my-utils/go.nu [h, m] +``` + +但是,假设我们希望 `go.nu` 成为 `my-utils` 的子模块。当用户导入 `my-utils` 时,他们应该*仅*获得命令,而不是别名。编辑 `my-utils/mod.nu` 并添加: + +```nu +export use ./go.nu [home, modules] +``` + +这*几乎*有效 - 它选择性地导出 `home` 和 `modules`,但不导出别名。但是,它这样做时没有 `go` 前缀。例如: + +```nu +use my-utils * +home +# => works +go home +# => Error: command not found +``` + +要将它们导出为 `go home` 和 `go modules`,请对 `my-utils/mod.nu` 进行以下更改: + +```nu +# 将现有的 `export use` 替换为... +export module go { + export use ./go.nu [home, modules] +} +``` + +这在 `my-utils` 中创建了一个新的导出子模块 `go`,其中包含 `go home` 和 `go modules` 的选择性(重新)导出定义。 + +```nu +use my-utils * +# => 如预期: +go home +# => works +home +# => Error: command not found +``` diff --git a/zh-CN/book/modules/using_modules.md b/zh-CN/book/modules/using_modules.md new file mode 100644 index 00000000000..3375b8554b1 --- /dev/null +++ b/zh-CN/book/modules/using_modules.md @@ -0,0 +1,248 @@ +# 使用模块 + +[[toc]] + +## 概述 + +终端用户可以通过("导入")他人编写的模块来为Nushell添加新功能。 + +要导入模块及其定义,我们使用[`use`](/commands/docs/use.md)命令: + +```nu +use <路径/到/模块> <成员...> +``` + +例如: + +```nu +use std/log +log info "你好哇,模块" +``` + +::: tip +上面的例子使用了[标准库](../standard_library.md),这是Nushell内置的模块集合。因为它对所有Nushell用户都可用,我们也会在下面的几个例子中使用它。 +::: + +## 安装模块 + +安装模块只需将其文件放在目录中即可。可以通过`git clone`(或其他版本控制系统)、像`nupm`这样的包管理器或手动完成。模块的文档应提供相关建议。 + +## 导入模块 + +[`use`](/commands/docs/use.md)关键字之后的任何内容形成一个**导入模式**,控制如何导入定义。 + +注意上面的`use`有两个参数: + +- 模块的路径 +- (可选)要导入的定义 + +模块文档通常会告诉你推荐的导入方式。不过,了解可用的选项仍然很有用: + +### 模块路径 + +模块路径可以是: + +- 包含`mod.nu`文件的绝对路径: + + ::: details 示例 + + ```nu + use ~/nushell/modules/nupm + ``` + + 注意模块名(即其目录)可以以`/`结尾(Windows上是`\`),但像大多数接受路径的命令(如`cd`)一样,这完全是可选的。 + + ::: + +- 包含`mod.nu`文件的相对路径: + + ::: details 示例 + + ```nu + # cd然后使用相对nupm目录中的mod.nu + cd ~/nushell/modules + use nupm + # 或 + use nupm/ + ``` + + 注意模块名(其目录)可以以`/`结尾(Windows上是`\`),但像大多数接受路径的命令(如`cd`)一样,这完全是可选的。 + ::: + + ::: important 重要提示!从`$env.NU_LIB_DIRS`导入模块 + 通过相对路径导入模块时,Nushell首先从当前目录搜索。如果在该位置找不到匹配的模块,Nushell会搜索`$env.NU_LIB_DIRS`列表中的每个目录。 + + 这允许你将模块安装到一个位置,无论当前目录如何,都可以通过相对路径轻松访问。 + ::: + +- Nushell模块文件的绝对或相对路径。如上所述,Nushell会在`$env.NU_LIB_DIRS`中搜索匹配的相对路径。 + + ::: details 示例 + + ```nu + use ~/nushell/modules/std-rfc/bulk-rename.nu + # 或 + cd ~/nushell/modules + use std-rfc/bulk-rename.nu + ``` + + ::: + +- 虚拟目录: + + ::: details 示例 + 上面提到的标准库模块存储在一个带有`std`目录的虚拟文件系统中。可以将其视为上面"绝对路径"示例的替代形式。 + + ```nu + use std/assert + assert equal 'string1' "string1" + ``` + + ::: + +- 不太常见的是,使用[`module`](/commands/docs/module.md)命令创建的模块名称。虽然可以在命令行使用此命令创建模块,但这并不常见或有用。相反,这种形式主要由模块作者用来定义子模块。参见[创建模块 - 子模块](./creating_modules.md#子模块)。 + +### 模块定义 + +`use`命令的第二个参数是要导入的定义的可选列表。同样,模块文档应提供建议。例如,[标准库章节](../standard_library.md#导入子模块)涵盖了每个子模块的推荐导入。 + +当然,你总是可以选择最适合你使用场景的形式。 + +- **将整个模块/子模块作为带有子命令的命令导入** + + 在上面的一个例子中,我们在没有指定定义的情况下导入了`std/log`模块: + + ```nu + use std/log + log info "Hello, std/log Module" + ``` + + 注意这将导入`log`子模块及其所有*子命令*(如`log info`、`log error`等)到当前作用域。 + + 将上面的例子与下一个版本比较,命令变成了`std log info`: + + ```nu + use std + std log info "Hello, std Module" + ``` + +- **从模块导入所有定义** + + 或者,你可以将定义本身导入当前作用域。例如: + + ```nu + use std/formats * + ls | to jsonl + ``` + + 注意`to jsonl`命令如何直接放在当前作用域中,而不是作为`formats`的子命令。 + +- **从模块导入一个或多个定义** + + Nushell还可以选择性地导入模块定义的子集。例如: + + ```nu + use std/math PI + let circle = 2 * $PI * $radius + ``` + + 记住定义可以是: + + - 命令 + - 别名 + - 常量 + - 外部命令 + - 其他模块(作为子模块) + - 环境变量(总是导入) + + 不太常见的是,也可以使用导入列表: + + ```nu + use std/formats [ 'from ndjson' 'to ndjson' ] + ``` + + ::: note 导入子模块 + 虽然你可以使用`use <模块> `(例如`use std help`)单独导入子模块,但使用这种形式时,整个父模块及其*所有*定义(以及子模块)都将被*解析*。如果可能,将子模块作为*模块*加载会得到更快的代码。例如: + + ```nu + # 更快 + use std/help + ``` + + ::: + +## 导入常量 + +如上面`std/math`示例所示,一些模块可能导出常量定义。导入整个模块时,可以通过与模块同名的记录访问常量: + +```nu +# 导入整个模块 - 记录访问 +use std/math +$math.PI +# => 3.141592653589793 + +$math +# => ╭───────┬──────╮ +# => │ GAMMA │ 0.58 │ +# => │ E │ 2.72 │ +# => │ PI │ 3.14 │ +# => │ TAU │ 6.28 │ +# => │ PHI │ 1.62 │ +# => ╰───────┴──────╯ + +# 或导入模块的所有成员 +use std/math * +$PI +# => 3.141592653589793 +``` + +## 隐藏 + +任何自定义命令或别名,无论是从模块导入的还是不是,都可以使用[`hide`](/commands/docs/hide.md)命令"隐藏"以恢复之前的定义。 + +`hide`命令也接受导入模式,类似于[`use`](/commands/docs/use.md),但解释略有不同。这些模式可以是以下之一: + +- 如果名称是自定义命令,`hide`命令直接隐藏它。 +- 如果名称是模块名,它会隐藏所有以模块名作为前缀的导出 + +例如,使用`std/assert`: + +```nu +use std/assert +assert equal 1 2 +# => Assertion failed +assert true +# => Assertion passes + +hide assert +assert equal 1 1 +# => Error: +# => help: A command with that name exists in module `assert`. Try importing it with `use` + +assert true +# => Error: +# => help: A command with that name exists in module `assert`. Try importing it with `use` +``` + +就像你可以`use`模块定义的一个子集一样,你也可以选择性地`hide`它们: + +```nu +use std/assert +hide assert main +assert equal 1 1 +# => assertion passes + +assert true +# => Error: +# => help: A command with that name exists in module `assert`. Try importing it with `use` +``` + +::: tip +`main`在[创建模块](./creating_modules.md#main-导出)中有更详细的介绍,但对于终端用户来说,`main`只是表示"与模块同名的命令"。在这种情况下,`assert`模块导出一个`main`命令,"伪装"成`assert`命令。隐藏`main`的效果是隐藏`assert`命令,但不隐藏其子命令。 +::: + +## 另请参阅 + +- 要使模块始终可用而不必在每个Nushell会话中`use`它,只需将其导入(`use`)添加到启动配置中。参见[配置](../configuration.md)章节了解如何操作。 + +- 模块也可以作为[覆盖层](../overlays.md)的一部分使用。 diff --git a/zh-CN/book/moving_around.md b/zh-CN/book/moving_around.md index 8e840e2ba90..6ef0a658c82 100644 --- a/zh-CN/book/moving_around.md +++ b/zh-CN/book/moving_around.md @@ -1,65 +1,213 @@ # 在系统中四处移动 -早期的 Shell 允许你在文件系统中进行目录跳转并运行命令,而现代的 Shell 如 Nu 也允许你这样做。让我们来看看你在与系统交互时可能会用到的一些常用命令。 +Shell 的一个决定性特征是能够在文件系统中导航和交互。Nushell 当然也不例外。以下是你在与文件系统交互时可能会用到的一些常用命令。 ## 查看目录内容 -@[code](@snippets/moving_around/ls_example.sh) +```nu +ls +``` -正如我们在其他章节中所看到的,[`ls`](/commands/docs/ls.md) 是一个用于查看路径内容的命令。Nu 将以表格的形式返回内容并供我们使用。 +正如在快速入门中看到的,[`ls`](/commands/docs/ls.md) 命令返回目录的内容。Nushell 的 `ls` 将以[表格](types_of_data.html#tables)的形式返回内容。 [`ls`](/commands/docs/ls.md) 命令还需要一个可选的参数,以改变你想查看的内容。例如,我们可以列出以 `.md` 结尾的文件: -@[code](@snippets/moving_around/ls_shallow_glob_example.sh) +```nu +ls *.md +# => ╭───┬────────────────────┬──────┬──────────┬──────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├───┼────────────────────┼──────┼──────────┼──────────────┤ +# => │ 0 │ CODE_OF_CONDUCT.md │ file │ 3.4 KiB │ 9 months ago │ +# => │ 1 │ CONTRIBUTING.md │ file │ 11.0 KiB │ 5 months ago │ +# => │ 2 │ README.md │ file │ 12.0 KiB │ 6 days ago │ +# => │ 3 │ SECURITY.md │ file │ 2.6 KiB │ 2 months ago │ +# => ╰───┴────────────────────┴──────┴──────────┴──────────────╯ +``` -## 通配符 +## 通配符模式 (wildcards) 上述可选参数 `*.md` 中的星号(\*)有时被称为通配符(wildcards)或 Glob,它让我们可以匹配任何东西。你可以把 glob `*.md` 理解为“匹配以 `.md` 结尾的任何文件名”。 最通用的通配符是 `*`,能够匹配所有路径。它经常和其他模式(pattern)组合使用,比如 `*.bak` 和 `temp*`。 -Nu 也使用现代 Globs,它允许你访问更深的目录。比如,`ls **/*.md` 将递归地罗列当前目录下、所有后缀为 `.md` 的非隐藏文件: +Nu 也使用双星号 `**`,它允许你访问更深的目录。比如,`ls **/*` 将递归地罗列当前目录下、所有的非隐藏路径。 -@[code](@snippets/moving_around/ls_deep_glob_example.sh) +```nu +ls **/*.md +# => ╭───┬───────────────────────────────┬──────┬──────────┬──────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├───┼───────────────────────────────┼──────┼──────────┼──────────────┤ +# => │ 0 │ CODE_OF_CONDUCT.md │ file │ 3.4 KiB │ 5 months ago │ +# => │ 1 │ CONTRIBUTING.md │ file │ 11.0 KiB │ a month ago │ +# => │ 2 │ README.md │ file │ 12.0 KiB │ a month ago │ +# => │ 3 │ SECURITY.md │ file │ 2.6 KiB │ 5 hours ago │ +# => │ 4 │ benches/README.md │ file │ 249 B │ 2 months ago │ +# => │ 5 │ crates/README.md │ file │ 795 B │ 5 months ago │ +# => │ 6 │ crates/nu-cli/README.md │ file │ 388 B │ 5 hours ago │ +# => │ 7 │ crates/nu-cmd-base/README.md │ file │ 262 B │ 5 hours ago │ +# => │ 8 │ crates/nu-cmd-extra/README.md │ file │ 669 B │ 2 months ago │ +# => │ 9 │ crates/nu-cmd-lang/README.md │ file │ 1.5 KiB │ a month ago │ +# => ╰───┴───────────────────────────────┴──────┴──────────┴──────────────╯ +``` -- `**` 表示“从这里开始的任何目录中”; -- `*.md` 表示“任意后缀为 `.md` 的文件名”(不包括隐藏文件,要额外添加 `--all, -a` 选项); -- 除了 `*`,还有 `?` 用来匹配单个字符。比如,可以使用 `p???` 模式匹配 `port` 字符串。 +这里,我们正在寻找任何以 ".md" 结尾的文件。双星号进一步指定了“从这里开始的任何目录中”。 -结合 [字符串的处理](/zh-CN/book/working_with_strings.md) 能够写出更强大的模式。但是,请牢记 Nu 类似一种 [编译型语言](/zh-CN/book/thinking_in_nu.md#把-nushell-想象成一种编译型语言)。 +Nushell 的通配符语法不仅支持 `*`,还支持用 `?` 匹配[单个字符和用 `[...]` 匹配字符组](https://docs.rs/nu-glob/latest/nu_glob/struct.Pattern.html)。 -## 改变当前目录 +通过将 `*`、`?` 和 `[]` 模式包含在单引号、双引号或[原始字符串](working_with_strings.md#raw-strings)中,可以对它们进行转义。例如,要显示名为 `[slug]` 的目录的内容,请使用 `ls "[slug]"` 或 `ls '[slug]'`。 -@[code](@snippets/book/moving_around/cd_example.nu) +然而,*反引号*引用的字符串不会转义通配符。例如,比较以下场景: -要从当前目录换到一个新目录,我们使用 [`cd`](/commands/docs/cd.md) 命令。就像在其他 Shells 中一样,我们可以使用目录的名称,或者如果我们想进入父目录,我们可以使用 `..` 的快捷方式。 +1. 未引用:通配符模式 -如果 [`cd`](/commands/docs/cd.md) 被省略,只给出一个路径本身,也可以改变当前工作目录: + 一个带有通配符字符的未引用的[裸词字符串](working_with_strings.html#bare-word-strings)被解释为一个通配符模式,所以下面的命令将删除当前目录中所有文件名中包含 `myfile` 的文件: + + ```nu + rm *myfile* + ``` + +2. 引用:带星号的字符串字面量 + + 当使用单引号或双引号引用,或使用[原始字符串](working_with_strings.html#raw-strings)时,一个带有字面量、转义的星号(或其他通配符字符)的*字符串*被传递给命令。结果不是一个通配符。以下命令将只删除一个字面量名为 `*myfile*`(包括星号)的文件。名称中包含 `myfile` 的其他文件不受影响: -@[code](@snippets/book/moving_around/cd_without_command_example.nu) + ```nu + rm "*myfile*" + ``` -:::warning -用 [`cd`](/commands/docs/cd.md) 改变目录会改变 `PWD` 环境变量。这意味着目录的改变会保留到当前代码块中,一旦你退出这个代码块,你就会返回到以前的目录。你可以在 [环境篇](environment.md) 中了解更多关于这方面的信息。 +3. 反引号引用:通配符模式 + + [反引号引用的字符串](working_with_strings.html#backtick-quoted-strings)中的星号(和其他通配符模式)被解释为一个通配符模式。请注意,这与上面 #1 中的裸词字符串示例的行为相同。 + + 以下命令与第一个示例一样,删除当前目录中所有文件名中包含 `myfile` 的文件: + + ```nu + rm `*myfile*` + ``` + +::: tip 提示 +Nushell 还包括一个专门的 [`glob` 命令](https://www.nushell.sh/commands/docs/glob.html),支持更复杂的通配符场景。 ::: -## 文件系统命令 +### 将字符串转换为通配符 + +上面的引用技术在构造通配符字面量时很有用,但你可能需要以编程方式构造通配符。有几种技术可用于此目的: + +1. `into glob` + + [`into glob` 命令](/commands/docs/into_glob.html)可用于将字符串(和其他类型)转换为通配符。例如: + + ```nu + # 查找文件名包含当前月份(格式为 YYYY-mm)的文件 + let current_month = (date now | format date '%Y-%m') + let glob_pattern = ($"*($current_month)*" | into glob) + ls $glob_pattern + ``` + +2. 扩展操作符与 [`glob` 命令](/commands/docs/glob.html)结合使用: + + [`glob` 命令](/commands/docs/glob.html)(注意:与 `into glob` 不同)生成一个与通配符模式匹配的[`列表`](types_of_data.html#lists)文件名。这个列表可以使用[扩展操作符](operators.html#spread-operator)展开并传递给文件系统命令: + + ```nu + # 查找文件名包含当前月份(格式为 YYYY-mm)的文件 + let current_month = (date now | format date '%Y-%m') + ls ...(glob $"*($current_month)*") + ``` + +3. 通过注解强制 `glob` 类型: + + ```nu + # 查找文件名包含当前月份(格式为 YYYY-mm)的文件 + let current_month = (date now | format date '%Y-%m') + let glob_pattern: glob = ($"*($current_month)*") + ls $glob_pattern + ``` -Nu 还提供了一些基本的文件系统命令,并且可以跨平台工作。 +## 创建目录 -我们可以使用 [`mv`](/commands/docs/mv.md) 命令将一个目录或文件从一个地方移动到另一个地方: +与大多数其他 shell 一样,[`mkdir` 命令](/commands/docs/mkdir.md)用于创建新目录。一个细微的区别是,Nushell 的内部 `mkdir` 命令默认情况下像 Unix/Linux 的 `mkdir -p` 一样操作,因为它: -@[code](@snippets/moving_around/mv_example.sh) +- 会自动创建多个目录级别。例如: -我们可以通过 [`cp`](/commands/docs/cp.md) 命令把一个目录或文件从一个地方复制到另一个地方: + ```nu + mkdir modules/my/new_module + ``` -@[code](@snippets/moving_around/cp_example.sh) + 即使这些目录都不存在,这也会创建所有三个目录。在 Linux/Unix 上,这需要 `mkdir -p`。 -我们也可以通过 [`rm`](/commands/docs/rm.md) 命令删除一个目录或文件: +- 如果目录已经存在,则不会出错。例如: -@[code](@snippets/moving_around/rm_example.sh) + ```nu + mkdir modules/my/new_module + mkdir modules/my/new_module + # => 没有错误 + ``` -这三个命令也可以使用我们先前看到的 [`ls`](/commands/docs/ls.md) 的 Glob 功能。 + ::: tip 提示 + 一个常见的错误是,刚接触 Nushell 的人会尝试使用 `mkdir -p `,就像在原生的 Linux/Unix 版本中一样。然而,这会在 Nushell 中产生一个 `Unknown Flag` 错误。 -最后,我们可以使用 [`mkdir`](/commands/docs/mkdir.md) 命令创建一个新目录: + 只需重复不带 `-p` 的命令即可达到相同的效果。 + ::: -@[code](@snippets/moving_around/mkdir_example.sh) +## 改变当前目录 + +```nu +cd cookbook +``` + +要从当前目录换到一个新目录,我们使用 [`cd`](/commands/docs/cd.md) 命令。 + +如果 [`cd`](/commands/docs/cd.md) 被省略,只给出一个路径本身,也可以改变当前工作目录: + +```nu +cookbook/ +``` + +就像在其他 Shells 中一样,我们可以使用目录的名称,或者如果我们想进入父目录,我们可以使用 `..` 的快捷方式。 + +你还可以添加额外的点来进入更上层的目录: + +```nu +# 切换到父目录 +cd .. +# 或者 +.. +# 上移两层(父目录的父目录) +cd ... +# 或者 +... +# 上移三层(父目录的父目录的父目录) +cd .... +# 等等 +``` + +::: tip 提示 +多点快捷方式可用于内部 Nushell [文件系统命令](/commands/categories/filesystem.html)以及外部命令。例如,在 Linux/Unix 系统上运行 `^stat ....` 将显示路径被扩展为 `../../..` +::: + +你也可以将相对目录级别与目录名结合起来: + +```nu +cd ../sibling +``` + +::: tip 重要提示 +用 [`cd`](/commands/docs/cd.md) 改变目录会改变 `PWD` 环境变量。这意味着目录的改变会保留到当前代码块(例如,块或闭包)中。一旦你退出这个代码块,你就会返回到以前的目录。你可以在[环境篇](environment.md)中了解更多关于这方面的信息。 +::: + +## 文件系统命令 + +Nu 还提供了一些可以跨平台工作的基本[文件系统命令](/commands/categories/filesystem.html),例如: + +- [`mv`](/commands/docs/mv.md) 用于重命名或将文件或目录移动到新位置 +- [`cp`](/commands/docs/cp.md) 用于将项目复制到新位置 +- [`rm`](/commands/docs/rm.md) 用于从文件系统中删除项目 + +::: tip 提示 +在 Bash 和许多其他 shell 中,大多数文件系统命令(`cd` 除外)实际上是系统中的独立二进制文件。例如,在 Linux 系统上,`cp` 是 `/usr/bin/cp` 二进制文件。在 Nushell 中,这些命令是内置的。这有几个优点: + +- 它们在可能没有二进制版本的平台(例如 Windows)上工作一致。这允许创建跨平台的脚本、模块和自定义命令。 +- 它们与 Nushell 更紧密地集成,允许它们理解 Nushell 类型和其他构造。 +- 如[快速入门](quick_tour.html)中所述,它们在 Nushell 帮助系统中有文档记录。运行 `help ` 或 ` --help` 将显示该命令的 Nushell 文档。 + +虽然通常建议使用 Nushell 内置版本,但也可以访问 Linux 二进制文件。有关详细信息,请参阅[运行外部命令](./running_externals.md)。 +::: diff --git a/zh-CN/book/navigating_structured_data.md b/zh-CN/book/navigating_structured_data.md new file mode 100644 index 00000000000..1d959d347ef --- /dev/null +++ b/zh-CN/book/navigating_structured_data.md @@ -0,0 +1,347 @@ +# 导航和访问结构化数据 + +鉴于Nushell对结构化数据的强大支持,一些常见任务涉及导航和访问这些数据。 + +## 本节索引 + +- [背景和定义](#背景) +- [单元格路径](#单元格路径) + - [记录](#记录) + - [列表](#列表) + - [表格](#表格) + - 示例数据 + - 示例 - 访问表格行 + - 示例 - 访问表格列 + - [嵌套数据](#嵌套数据) +- [使用`get`和`select`](#使用get和select) + - 示例 - `get`与`select`对比(表格行) + - 示例 - `select`多行多列 +- [使用可选操作符处理缺失数据](#可选操作符) +- [带空格的键/列名](#带空格的键列名) +- [其他访问结构化数据的命令](#其他访问结构化数据的命令) + +## 背景 + +对于下面的示例和描述,请记住关于结构化数据的几个定义: + +- **_列表:_** 列表包含零个或多个任意类型的值。零值的列表称为"空列表" +- **_记录:_** 记录包含零个或多个命名键及其对应值。记录值的数据也可以是任何类型。零键值对的记录称为"空记录" +- **_嵌套数据:_** 列表、记录或表格中包含的值可以是基本类型或结构化数据本身。这意味着数据可以多层嵌套且形式多样: + - 列表值可以包含表格、记录甚至其他列表 + - **_表格:_** 表格是记录的列表 + - 记录值可以包含表格、列表和其他记录 + - 这意味着表格的记录也可以包含嵌套的表格、列表和其他记录 + +::: tip +因为表格是记录的列表,任何适用于列表的命令或语法也适用于表格。反之则不一定成立;有些命令和语法适用于表格但不适用于列表。 +::: + +## 单元格路径 + +单元格路径是访问结构化数据内部值的主要方式。这种路径基于类似于电子表格的概念,其中列有名称,行有编号。单元格路径的名称和索引用点分隔。 + +### 记录 + +对于记录,单元格路径指定键的名称,这是一个`string`。 + +#### 示例 - 访问记录值: + +```nu +let my_record = { + a: 5 + b: 42 + } +$my_record.b + 5 +# => 47 +``` + +### 列表 + +对于列表,单元格路径指定列表中值的位置(索引)。这是一个`int`: + +#### 示例 - 访问列表值: + +记住,列表索引从0开始。 + +```nu +let scoobies_list = [ Velma Fred Daphne Shaggy Scooby ] +$scoobies_list.2 +# => Daphne +``` + +### 表格 + +- 要访问列,单元格路径使用列名,这是一个`string` +- 要访问行,它使用行的索引号,这是一个`int` +- 要访问单个单元格,它使用列名和行索引的组合 + +接下来的几个示例将使用以下表格: + +```nu +let data = [ + [date temps condition ]; + [2022-02-01T14:30:00+05:00, [38.24, 38.50, 37.99, 37.98, 39.10], 'sunny' ], + [2022-02-02T14:30:00+05:00, [35.24, 35.94, 34.91, 35.24, 36.65], 'sunny' ], + [2022-02-03T14:30:00+05:00, [35.17, 36.67, 34.42, 35.76, 36.52], 'cloudy' ], + [2022-02-04T14:30:00+05:00, [39.24, 40.94, 39.21, 38.99, 38.80], 'rain' ] +] +``` + +::: details 展开查看数据可视化表示 + +```nu +╭───┬─────────────┬───────────────┬───────────╮ +│ # │ date │ temps │ condition │ +├───┼─────────────┼───────────────┼───────────┤ +│ 0 │ 2 years ago │ ╭───┬───────╮ │ sunny │ +│ │ │ │ 0 │ 38.24 │ │ │ +│ │ │ │ 1 │ 38.50 │ │ │ +│ │ │ │ 2 │ 37.99 │ │ │ +│ │ │ │ 3 │ 37.98 │ │ │ +│ │ │ │ 4 │ 39.10 │ │ │ +│ │ │ ╰───┴───────╯ │ │ +│ 1 │ 2 years ago │ ╭───┬───────╮ │ sunny │ +│ │ │ │ 0 │ 35.24 │ │ │ +│ │ │ │ 1 │ 35.94 │ │ │ +│ │ │ │ 2 │ 34.91 │ │ │ +│ │ │ │ 3 │ 35.24 │ │ │ +│ │ │ │ 4 │ 36.65 │ │ │ +│ │ │ ╰───┴───────╯ │ │ +│ 2 │ 2 years ago │ ╭───┬───────╮ │ cloudy │ +│ │ │ │ 0 │ 35.17 │ │ │ +│ │ │ │ 1 │ 36.67 │ │ │ +│ │ │ │ 2 │ 34.42 │ │ │ +│ │ │ │ 3 │ 35.76 │ │ │ +│ │ │ │ 4 │ 36.52 │ │ │ +│ │ │ ╰───┴───────╯ │ │ +│ 3 │ 2 years ago │ ╭───┬───────╮ │ rain │ +│ │ │ │ 0 │ 39.24 │ │ │ +│ │ │ │ 1 │ 40.94 │ │ │ +│ │ │ │ 2 │ 39.21 │ │ │ +│ │ │ │ 3 │ 38.99 │ │ │ +│ │ │ │ 4 │ 38.80 │ │ │ +│ │ │ ╰───┴───────╯ │ │ +╰───┴─────────────┴───────────────┴───────────╯ +``` + +::: + +这表示天气数据形式的表格,有三列: + +1. **_date_**: 每天的Nushell `date` +2. **_temps_**: Nushell `list`,包含5个`float`值,表示该地区不同气象站的温度读数 +3. **_conditions_**: 每天的Nushell `string`,表示该地区的天气状况 + +#### 示例 - 访问表格行(记录) + +访问第二天的数据作为记录: + +```nu +$data.1 +# => ╭───────────┬───────────────╮ +# => │ date │ 2 years ago │ +# => │ │ ╭───┬───────╮ │ +# => │ temps │ │ 0 │ 35.24 │ │ +# => │ │ │ 1 │ 35.94 │ │ +# => │ │ │ 2 │ 34.91 │ │ +# => │ │ │ 3 │ 35.24 │ │ +# => │ │ │ 4 │ 36.65 │ │ +# => │ │ ╰───┴───────╯ │ +# => │ condition │ sunny │ +# => ╰───────────┴───────────────╯ +``` + +#### 示例 - 访问表格列(列表) + +```nu +$data.condition +# => ╭───┬────────╮ +# => │ 0 │ sunny │ +# => │ 1 │ sunny │ +# => │ 2 │ cloudy │ +# => │ 3 │ rain │ +# => ╰───┴────────╯ +``` + +#### 示例 - 访问表格单元格(值) + +第四天的天气状况: + +```nu +$data.condition.3 +# => rain +``` + +### 嵌套数据 + +由于数据可以嵌套,单元格路径可以包含对多个名称或索引的引用。 + +#### 示例 - 访问嵌套表格数据 + +获取第三天第二个气象站的温度: + +```nu +$data.temps.2.1 +# => 36.67 +``` + +第一个索引`2`访问第三天,然后下一个索引`1`访问第二个气象站的温度读数。 + +## 使用`get`和`select` + +除了上面使用的单元格路径字面量语法外,Nushell还提供了几个使用单元格路径的命令。其中最重要的是: + +- `get`等同于使用单元格路径字面量,但支持变量名和表达式。`get`与上面的单元格路径示例一样,返回单元格路径指示的**值**。 +- `select`有微妙但关键的区别。它返回指定的**数据结构**本身,而不仅仅是它的值。 + - 在表格上使用`select`将返回一个大小相等或更小的表格 + - 在列表上使用`select`将返回一个大小相等或更小的列表 + - 在记录上使用`select`将返回一个大小相等或更小的记录 + +继续使用上面的示例表格: + +### 示例 - `get`与`select`对比(表格行) + +```nu +$data | get 1 +# => ╭───────────┬───────────────╮ +# => │ date │ 2 years ago │ +# => │ │ ╭───┬───────╮ │ +# => │ temps │ │ 0 │ 35.24 │ │ +# => │ │ │ 1 │ 35.94 │ │ +# => │ │ │ 2 │ 34.91 │ │ +# => │ │ │ 3 │ 35.24 │ │ +# => │ │ │ 4 │ 36.65 │ │ +# => │ │ ╰───┴───────╯ │ +# => │ condition │ sunny │ +# => ╰───────────┴───────────────╯ + +$data | select 1 +# => ╭───┬─────────────┬───────────────┬───────────╮ +# => │ # │ date │ temps │ condition │ +# => ├───┼─────────────┼───────────────┼───────────┤ +# => │ 0 │ 2 years ago │ ╭───┬───────╮ │ sunny │ +# => │ │ │ │ 0 │ 35.24 │ │ │ +# => │ │ │ │ 1 │ 35.94 │ │ │ +# => │ │ │ │ 2 │ 34.91 │ │ │ +# => │ │ │ │ 3 │ 35.24 │ │ │ +# => │ │ │ │ 4 │ 36.65 │ │ │ +# => │ │ │ ╰───┴───────╯ │ │ +# => ╰───┴─────────────┴───────────────┴───────────╯ +``` + +注意: + +- [`get`](/commands/docs/get.md)返回与上面`$data.1`示例相同的记录 +- [`select`](/commands/docs/select.md)返回一个新的单行表格,包括列名和行索引 + +::: tip +`select`结果表格的行索引与原表格不同。新表格有自己的从0开始的索引。 + +要获取原始索引,可以使用[`enumerate`](/commands/docs/enumerate.md)命令。例如: + +```nu +$data | enumerate | select 1 +``` + +::: + +### 示例 - `select`多行多列 + +因为`select`结果是新表格,所以可以指定多个列名、行索引,甚至两者都指定。此示例创建一个新表格,包含第一行和第二行的date和condition列: + +```nu +$data | select date condition 0 1 +# => ╭───┬─────────────┬───────────╮ +# => │ # │ date │ condition │ +# => ├───┼─────────────┼───────────┤ +# => │ 0 │ 2 years ago │ sunny │ +# => │ 1 │ 2 years ago │ sunny │ +# => ╰───┴─────────────┴───────────╯ +``` + +## 带空格的键/列名 + +如果键名或列名包含空格或其他阻止其作为裸词字符串访问的字符,则可以引用键名。 + +示例: + +```nu +let record_example = { + "key x":12 + "key y":4 + } +$record_example."key x" +# => 12 + +# 或 +$record_example | get "key x" +# => 12 +``` + +当键名可能与数值混淆时,也需要引号。 + +示例: + +```nu +let record_example = { + "1": foo + "2": baz + "3": far +} + +$record_example."1" +# => foo +``` + +不要将键名与此情况下的行索引混淆。这里,第一项被*分配*了键名`1`(字符串)。如果使用`transpose`命令转换为表格,键`1`(`string`)将位于行索引`0`(整数)。 + +## 处理缺失数据 + +### 可选操作符 + +默认情况下,如果无法访问请求的行或列,单元格路径访问将失败。要抑制这些错误,可以在单元格路径成员后添加`?`将其标记为可选: + +#### 示例 - 可选操作符 + +使用上面的温度数据: + +```nu +let cp: cell-path = $.temps?.1 # 仅获取temps列中第二个位置的数据 + +# 哎呀,我们删除了temps列 +$data | reject temps | get $cp +``` + +默认情况下,通过可选操作符访问时,缺失的单元格将被替换为`null`。 + +### 为缺失或`null`数据设置默认值 + +[`default`命令](/commands/docs/default.html)可用于为缺失或null的列结果应用默认值。 + +```nu +let missing_value = [{a:1 b:2} {b:1}] +$missing_value +# => ╭───┬────┬───╮ +# => │ # │ a │ b │ +# => ├───┼────┼───┤ +# => │ 0 │ 1 │ 2 │ +# => │ 1 │ ❎ │ 1 │ +# => ╰───┴────┴───╯ + +let with_default_value = ($missing_value | default 'n/a' a) +$with_default_value +# => ╭───┬─────┬───╮ +# => │ # │ a │ b │ +# => ├───┼─────┼───┤ +# => │ 0 │ 1 │ 2 │ +# => │ 1 │ n/a │ 1 │ +# => ╰───┴─────┴───╯ + +$with_default_value.1.a +# => n/a +``` + +## 其他访问结构化数据的命令 + +- [`reject`](/commands/docs/reject.md) 与`select`相反,移除指定的行和列 +- [`range`](/commands/docs/range.md) 使用[`range`](./types_of_data.md#ranges)类型指定要选择的列表或表的行范围 diff --git a/zh-CN/book/nu_as_a_shell.md b/zh-CN/book/nu_as_a_shell.md new file mode 100644 index 00000000000..189c0d06217 --- /dev/null +++ b/zh-CN/book/nu_as_a_shell.md @@ -0,0 +1,21 @@ +# 作为Shell的Nu + +[Nu基础](nu_fundamentals.md)和[在Nu中编程](programming_in_nu.md)章节主要关注Nushell的语言特性。本章将重点介绍与Nushell解释器(Nushell [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop))相关的部分。有些概念直接是Nushell编程语言的一部分(如环境变量),而其他一些则是纯粹为了增强交互体验而实现的(如钩子),因此在运行脚本等场景下不会出现。 + +Nushell的许多参数都可以[配置](configuration.md)。配置本身作为环境变量存储。此外,Nushell有几个不同的配置文件,在启动时运行,你可以在其中放置自定义命令、别名等。 + +任何shell的一个重要特性都是[环境变量](environment.md)。在Nushell中,环境变量有作用域,并且可以具有Nushell支持的任何类型。这带来了一些额外的设计考虑,请参阅链接部分了解更多详情。 + +其他章节解释了如何处理[标准输出、标准错误和退出码](stdout_stderr_exit_codes.md),如何[当存在同名内置命令时运行外部命令](./running_externals.md),以及如何[配置第三方提示符](3rdpartyprompts.md)以与Nushell配合使用。 + +Nushell的一个有趣特性是[目录栈](directory_stack.md),它允许你同时在多个目录中工作。 + +Nushell还有自己的行编辑器[Reedline](line_editor.md)。通过Nushell的配置,可以配置Reedline的一些功能,如提示符、键绑定、历史记录或菜单。 + +还可以为[外部命令定义自定义签名](externs.md),这使你可以为它们定义[自定义补全](custom_completions.md)(自定义补全也适用于Nushell自定义命令)。 + +[Nu中的颜色和主题](coloring_and_theming.md)更详细地介绍了如何配置Nushell的外观。 + +如果你想在后台运行一些命令,[后台作业](background_jobs.md)提供了简单的指南供你遵循。 + +最后,[钩子](hooks.md)允许你在特定事件时插入Nushell代码片段运行。 diff --git a/zh-CN/book/nu_fundamentals.md b/zh-CN/book/nu_fundamentals.md new file mode 100644 index 00000000000..11eafe5d1ed --- /dev/null +++ b/zh-CN/book/nu_fundamentals.md @@ -0,0 +1,20 @@ +# Nu 基础 + +本章解释了 Nushell 编程语言的一些基础知识。 +阅读完本章后,你应该对如何编写简单的 Nushell 程序有所了解。 + +Nushell 有一个丰富的类型系统。 +你会发现像字符串或整数这样的典型数据类型,以及像单元格路径这样不太典型的数据类型。 +此外,Nushell 的一个决定性特征是 _结构化数据_ 的概念,这意味着你可以将类型组织成集合:列表、记录或表格。 +与传统的 Unix 方法中命令通过纯文本进行通信不同,Nushell 命令通过这些数据类型进行通信。 +以上所有内容都在[数据类型](types_of_data.md)中进行了解释。 + +[加载数据](loading_data.md)解释了如何将常见的数据格式(如 JSON)读入 _结构化数据_ 。这包括我们自己的 "NUON" 数据格式。 + +就像 Unix shell 一样,Nushell 命令可以组合成[管道](pipelines.md)来传递和修改数据流。 + +一些数据类型具有有趣的特性,值得单独介绍:[字符串](working_with_strings.md)、[列表](working_with_lists.md)和[表格](working_with_tables.md)。 +除了解释这些特性外,这些部分还展示了如何进行一些常见的操作,例如组合字符串或更新列表中的值。 + +最后,[命令参考](/commands/)列出了所有内置命令及其简要说明。 +请注意,你也可以在 Nushell 中使用 [`help`](/commands/docs/help.md) 命令访问此信息。 diff --git a/zh-CN/book/nushell_map.md b/zh-CN/book/nushell_map.md index 64b5e0608aa..fc4ec7b1ff0 100644 --- a/zh-CN/book/nushell_map.md +++ b/zh-CN/book/nushell_map.md @@ -2,100 +2,72 @@ 这个表格背后的想法是帮助你了解 Nu 的内置和插件所提供命令与其他已知的 Shell 和特定领域语言(DSL)的关系。我们试图制作一张所有 Nu 命令和它们在其他语言中的对应关系的表。欢迎大家参与贡献。 -注意:此表针对 Nu 0.43 或更高版本。 - - -| Nushell | SQL | .Net LINQ (C#) | PowerShell (without external modules) | Bash | -| ---------------------- | ----------------------------- | ---------------------------------------------------- | ------------------------------------------ | ----------------------------------------------- | -| alias | - | - | alias | alias | -| append | - | Append | -Append | | -| args | - | - | | | -| autoview | - | - | | | -| math avg | avg | Average | Measure-Object, measure | | -| calc, `` | math operators | Aggregate, Average, Count, Max, Min, Sum | | bc | -| cd | - | - | Set-Location, cd | cd | -| clear | - | - | Clear-Host | clear | -| compact | | | | | -| config | - | - | $Profile | vi .bashrc, .profile | -| cp | - | - | Copy-Item, cp, copy | cp | -| date | NOW() / getdate() | DateTime class | Get-Date | date | -| debug | | | | | -| default | | | | | -| drop | | | | | -| du | - | - | | du | -| each | cursor | | ForEach-Object, foreach, for | | -| echo | print, union all | - | Write-Output, write | echo | -| enter | - | - | | | -| exit | - | | exit | exit | -| first | top, limit | First, FirstOrDefault | Select-Object -First | head | -| format | | String.Format | String.Format | | -| from | import flatfile, openjson, cast(variable as xml) | - | Import/ConvertFrom-{Csv,Xml,Html,Json} | | -| get | | Select | (cmd).column | | -| group-by | group by | GroupBy, group | Group-Object, group | | -| headers | | | | | -| help | sp_help | - | Get-Help, help, man | man | -| histogram | - | - | | | -| history | - | - | Get-History, history | history | -| http(`*`) | - | HttpClient,WebClient, HttpWebRequest/Response | Invoke-WebRequest | wget | -| inc(`*`) | - | | - | - | -| is_empty | is null | String.InNullOrEmpty | String.InNullOrEmpty | | -| keep, =take | top, limit | Take | Select-Object -First | head | -| keep-until | | | | | -| keep-while | | TakeWhile | | | -| kill | - | - | Stop-Process, kill | kill | -| last | | Last, LastOrDefault | Select-Object -Last | tail | -| length | count | Count | Measure-Object, measure | wc | -| lines | - | - | File.ReadAllLines | | -| ls | - | - | Get-ChildItem, dir, ls | ls | -| match(`*`) | case when | Regex.IsMatch | [regex] | | -| merge | | | | | -| mkdir | - | - | mkdir, md | mkdir | -| mv | - | - | Move-Item, mv, move, mi | mv | -| next | | | | | -| nth | limit x offset y, rownumber = | ElementAt | [x], indexing operator, ElementAt | | -| open | | | Get-Content, gc, cat, type | cat | -| parse | | | | | -| transpose | pivot | - | | | -| post(`*`) | - | HttpClient,WebClient, HttpWebRequest/Response | Invoke-WebRequest | | -| prepend | | | | | -| prev | | | | | -| ps(`*`) | - | - | Get-Process, ps, gps | ps | -| pwd | - | - | Get-Location, pwd | pwd | -| range | | Range | 1..10, 'a'..'f' | | -| reduce | | Aggregate | | | -| reject | | | | | -| rename | - | - | Rename-Item, ren, rni | mv | -| reverse | | Reverse | [Array]::Reverse($var) | | -| rm | - | - | Remove-Item, del, erase, rd, ri, rm, rmdir | rm | -| save | - | - | Write-Output, Out-File | > foo.txt | -| select | select | Select | Select-Object, select | | -| shells | - | - | - | | -| shuffle | | Random | Sort-Object {Get-Random} | - | -| size | | | Measure-Object, measure | wc | -| skip | where row_number() | Skip | Select-Object -Skip | | -| skip until | | | | | -| skip while | | SkipWhile | | | -| sort-by | order by | OrderBy, OrderByDescending, ThenBy, ThenByDescending | Sort-Object, sort | | -| split-by | | Split | Split | | -| split column | | - | | | -| split row | | - | | | -| str(`*`) | string functions | String class | String class | | -| str join | concat_ws | Join | Join-String | | -| str trim | rtrim, ltrim | Trim, TrimStart, TrimEnd | Trim | | -| sum | sum | Sum | Measure-Object, measure | | -| sys(`*`) | - | - | Get-ComputerInfo | uname, lshw, lsblk, lscpu, lsusb, hdparam, free | -| table | | | Format-Table, ft, Format-List, fl | | -| tags | - | - | - | | -| textview(`*`) | - | - | Get-Content, cat | | -| tree(`*`) | - | - | tree | | -| to | - | - | Export/ConvertTo-{Csv,Xml,Html,Json} | | -| touch | - | - | Set-Content | touch | -| uniq | distinct | Distinct | Get-Unique, gu | uniq | -| upsert | As | - | | | -| version | select @@version | - | $PSVersionTable | | -| with_env | - | - | $env:FOO = 'bar' | export foo = "bar" | -| where | where | Where | Where-Object, where, "?" operator | | -| which | - | - | - | which | -| wrap | | | | | - -* `*` - 这些命令是标准插件的一部分 +| Nushell | SQL | .Net LINQ (C#) | PowerShell (without external modules) | Bash | +| ---------------------------------------------------------- | ----------------------------- | ---------------------------------------------------- | ------------------------------------------ | ----------------------------------------------- | +| [`alias`](/commands/docs/alias.md) | | | `alias` | `alias` | +| [`append`](/commands/docs/append.md) | | `Append` | `-Append` | | +| [`math avg`](/commands/docs/math_avg.md) | `avg` | `Average` | `Measure-Object`, `measure` | | +| [Operators](operators.md) and [`math`](/commands/docs/math.md) | Math operators | `Aggregate`, `Average`, `Count`, `Max`, `Min`, `Sum` | | `bc` | +| [`cd`](/commands/docs/cd.md) | | | `Set-Location`, `cd` | `cd` | +| [`clear`](/commands/docs/clear.md)
Ctrl/⌘+L | | | `Clear-Host`
Ctrl/⌘+L | `clear`
Ctrl/⌘+L | +| [`config`](/commands/docs/config.md)
`$nu.default-config-dir` | | | `$Profile` | `~/.bashrc`, `~/.profile` | +| [`cp`](/commands/docs/cp.md) | | | `Copy-Item`, `cp`, `copy` | `cp` | +| [`date`](/commands/docs/date.md) | `NOW()`, `getdate()` | `DateTime` class | `Get-Date` | `date` | +| [`du`](/commands/docs/du.md)
[`ls --du`](/commands/docs/ls.md) | | | | `du` | +| [`each`](/commands/docs/each.md) | Cursors | | `ForEach-Object`, `foreach`, `for` | `for` | +| [`exit`](/commands/docs/exit.md)
Ctrl/⌘+D | | | `exit`
Ctrl/⌘+D | `exit`
Ctrl/⌘+D | +| [`http`](/commands/docs/http.md) | | `HttpClient`, `WebClient`, `HttpWebRequest/Response` | `Invoke-WebRequest` | `wget`, `curl` | +| [`first`](/commands/docs/first.md) | `top`, `limit` | `First`, `FirstOrDefault` | `Select-Object -First` | `head` | +| [`format`](/commands/docs/format.md), [`str`](/commands/docs/str.md) | | `String.Format` | `String.Format` | `printf` | +| [`from`](/commands/docs/from.md) | `import flatfile,` `openjson`, `cast(variable as xml) `| | `Import/ConvertFrom-{Csv,Xml,Html,Json}` | | +| [`get`](/commands/docs/get.md) | | `Select` | `(cmd).column` | | +| [`group-by`](/commands/docs/group-by.md) | `group by` | `GroupBy`, `group` | `Group-Object`, `group` | | +| [`help`](/commands/docs/help.md) | `sp_help` | | `Get-Help`, `help`, `man` | `man` | +| [`history`](/commands/docs/history.md) | | | `Get-History`, `history` | `history` | +| [`is-empty`](/commands/docs/is-empty.md) | `is null` | `String.IsNullOrEmpty` | `String.IsNullOrEmpty` | | +| [`kill`](/commands/docs/kill.md) | | | `Stop-Process`, `kill` | `kill` | +| [`last`](/commands/docs/last.md) | | `Last`, `LastOrDefault` | `Select-Object -Last` | `tail` | +| [`str stats`](/commands/docs/str_stats.md)
[`length`](/commands/docs/length.md)
[`str length`](/commands/docs/str_length.md) | `count` | `Count` | `Measure-Object`, `measure` | `wc` | +| [`lines`](/commands/docs/lines.md) | | | `File.ReadAllLines` | | +| [`ls`](/commands/docs/ls.md) | | | `Get-ChildItem`, `dir`, `ls` | `ls` | +| [`mkdir`](/commands/docs/mkdir.md) | | | `mkdir`, `md`, `New-Item -ItemType Directory` | `mkdir` | +| [`mv`](/commands/docs/mv.md) | | | `Move-Item`, `mv`, `move`, `mi` | `mv` | +| [`open`](/commands/docs/open.md) | | | `Get-Content`, `gc`, `cat`, `type` | `cat` | +| [`print`](/commands/docs/print.md) | `print`, `union all` | | `Write-Output`, `write` | `echo`, `print` | +| [`transpose`](/commands/docs/transpose.md) | `pivot` | | | | +| [`ps`](/commands/docs/ps.md) | | | `Get-Process`, `ps`, `gps` | `ps` | +| [`pwd`](/commands/docs/pwd.md) | | | `Get-Location`, `pwd` | `pwd` | +| [`range` (command)](/commands/docs/range.md) | `limit x offset y`, `rownumber` | `ElementAt` | `[x]`, indexing operator, `ElementAt` | | +| [`range` (type)](types_of_data.html#ranges) | | `Range` | `1..10`, `'a'..'f'` | | +| [`reduce`](/commands/docs/reduce.md) | | `Aggregate` | | | +| [`rename`](/commands/docs/rename.md) | | | `Rename-Item`, `ren`, `rni` | `mv` | +| [`reverse`](/commands/docs/reverse.md) | | `Reverse` | `[Array]::Reverse($var)` | | +| [`rm`](/commands/docs/rm.md) | | | `Remove-Item`, `del`, `erase`, `rd`, `ri`, `rm`, `rmdir` | `rm` | +| [`save`](/commands/docs/save.md) | | | `Write-Output`, `Out-File` | `> foo.txt` redirection | +| [`select`](/commands/docs/select.md) | `select` | `Select` | `Select-Object`, `select` | | +| [`shuffle`](/commands/docs/shuffle.md) | | `Random` | `Sort-Object {Get-Random}` | | +| [`skip`](/commands/docs/skip.md) | `where row_number()` | `Skip` | `Select-Object -Skip` | | +| [`skip until`](/commands/docs/skip_until.md) | | `SkipWhile` | | | +| [`skip while`](/commands/docs/skip_while.md) | | `SkipWhile` | | | +| [`sort-by`](/commands/docs/sort-by.md) | `order by` | `OrderBy`, `OrderByDescending`, `ThenBy`, `ThenByDescending` | `Sort-Object`, `sort` | `sort` | +| [`str`](/commands/docs/str.md) | String functions | `String` class | `String` class | | +| [`str join`](/commands/docs/str_join.md) | `concat_ws` | `Join` | `Join-String` | | +| [`str trim`](/commands/docs/str_trim.md) | `rtrim`, `ltrim` | `Trim`, `TrimStart`, `TrimEnd` | `Trim` | | +| [`math sum`](/commands/docs/math_sum.md) | ``sum` | `Sum` | `Measure-Object`, `measure` | | +| [`uname`](/commands/docs/uname.md)
[`sys host`](/commands/docs/sys_host.md) | | | `Get-ComputerInfo` | `uname` | +| [`sys disks`](/commands/docs/sys_disks.md) | | | `Get-ComputerInfo` | `lsblk` | +| [`sys mem`](/commands/docs/sys_mem.md) | | | `Get-ComputerInfo` | `free` | +| [`table`](/commands/docs/table.md) | | | `Format-Table`, `ft`, `Format-List`, `fl` | | +| [`take`](/commands/docs/take.md) | `top`, `limit` | `Take` | `Select-Object -First` | `head` | +| [`take until`](/commands/docs/take_until.md) | | `TakeWhile` | | | +| [`take while`](/commands/docs/take_while.md) | | `TakeWhile` | | | +| [`timeit`](/commands/docs/timeit.md) | | | `Measure-Command` | `time` | +| [`to`](/commands/docs/to.md) | | | `Export`/`ConvertTo-{Csv,Xml,Html,Json}` | | +| [`touch`](/commands/docs/touch.md) | | | `Set-Content` | `touch` | +| [`uniq`](/commands/docs/uniq.md) | `distinct` | `Distinct` | `Get-Unique`, `gu` | `uniq` | +| [`update`](/commands/docs/update.md) | | | `ForEach-Object` | | +| [`upsert`](/commands/docs/upsert.md) | `As` | | `ForEach-Object` | | +| [`version`](/commands/docs/version.md) | `select @@version` | | `$PSVersionTable` | | +| `$env.FOO = "bar"`
[`with-env`](/commands/docs/with-env.md) | | | `$env:FOO = 'bar'` | `export FOO "bar"` | +| [`where`](/commands/docs/where.md) | `where` | `Where` | `Where-Object`, `where`, `?` operator | | +| [`which`](/commands/docs/which.md) | | | `Get-Command` | `which` | diff --git a/zh-CN/book/nushell_map_functional.md b/zh-CN/book/nushell_map_functional.md index 6c8c2c3d4a8..ce5057de2cc 100644 --- a/zh-CN/book/nushell_map_functional.md +++ b/zh-CN/book/nushell_map_functional.md @@ -4,100 +4,44 @@ 注意:此表针对 Nu 0.43 或更高版本。 -| Nushell | Clojure | Tablecloth (Ocaml / Elm) | Haskell | | -| ------------------------- | ---------------------------- | ------------------------------- | ------------------------ | --- | -| alias | | | | | -| append | conj, into, concat | append, (++), concat, concatMap | (++) | | -| args | | | | | -| autoview | | | | | -| math avg | | | | | -| into binary | Integer/toHexString | | showHex | | -| calc, `` | math operators | | | | -| cd | | | | | -| clear | | | | | -| clip | | | | | -| compact | | | | | -| config | | | | | -| count | count | length, size | length, size | | -| cp | | | | | -| date | java.time.LocalDate/now | | | | -| debug | | | | | -| default | | | | | -| drop | | | | | -| du | | | | | -| each | map, mapv, iterate | map, forEach | map, mapM | | -| echo | println | | putStrLn, print | | -| enter | | | | | -| exit | System/exit | | | | -| first | first | head | head | | -| format | format | | Text.Printf.printf | | -| from | | | | | -| get | | | | | -| group-by | group-by | | group, groupBy | | -| headers | | | | | -| help | doc | | | | -| histogram | | | | | -| history | | | | | -| http(`*`) | | | | | -| inc(`*`) | inc | | succ | | -| insert | | | | | -| is-empty | empty? | isEmpty | | | -| keep | take, drop-last, pop | take, init | take, init | | -| keep-until | | | | | -| keep-while | take-while | takeWhile | takeWhile | | -| kill | | | | | -| last | last, peek, take-last | last | last | | -| lines | | | lines, words, split-with | | -| ls | | | | | -| match(`*`) | re-matches, re-seq, re-find | | | | -| merge | | | | | -| mkdir | | | | | -| mv | | | | | -| next | | | | | -| nth | nth | Array.get | lookup | | -| open | with-open | | | | -| parse | | | | | -| transpose | (apply mapv vector matrix) | | transpose | | -| post(`*`) | | | | | -| prepend | cons | cons, :: | :: | | -| prev | | | | | -| ps | | | | | -| pwd | | | | | -| range, 1..10 | range | range | 1..10, 'a'..'f' | | -| reduce | reduce, reduce-kv | foldr | foldr | | -| reject | | | | | -| rename | | | | | -| reverse | reverse, rseq | reverse, reverseInPlace | reverse | | -| rm | | | | | -| save | | | | | -| select | select-keys | | | | -| shells | | | | | -| shuffle | shuffle | | | | -| size | count | | size, length | | -| skip | rest | tail | tail | | -| skip until | | | | | -| skip while | drop-while | dropWhile | dropWhile, dropWhileEnd | | -| sort-by | sort, sort-by, sorted-set-by | sort, sortBy, sortWith | sort, sortBy | | -| split-by | split, split-{at,with,lines} | split, words, lines | split, words, lines | | -| split column | | | | | -| split row | | | | | -| str(`*`) | clojure.string functions | String functions | | | -| str join | join | concat | intercalate | | -| str trim | trim, triml, trimr | trim, trimLeft, trimRight | strip | | -| sum | apply + | sum | sum | | -| sys | | | | | -| table | | | | | -| tags | | | | | -| tree(`*`) | | | | | -| to | | | | | -| touch | | | | | -| uniq | set | Set.empty | Data.Set | | -| upsert | | | | | -| version | | | | | -| with_env | | | | | -| what | | | | | -| where | filter, filterv, select | filter, filterMap | filter | | -| which | | | | | -| wrap | | | | | - -- `*` - 这些命令是标准插件的一部分 +| Nushell | Clojure | Tablecloth (Ocaml / Elm) | Haskell | +| ------------ | ---------------------------- | ------------------------------- | ------------------------ | +| append | conj, into, concat | append, (++), concat, concatMap | (++) | +| into binary | Integer/toHexString | | showHex | +| count | count | length, size | length, size | +| date | java.time.LocalDate/now | | | +| each | map, mapv, iterate | map, forEach | map, mapM | +| exit | System/exit | | | +| first | first | head | head | +| format | format | | Text.Printf.printf | +| group-by | group-by | | group, groupBy | +| help | doc | | | +| is-empty | empty? | isEmpty | | +| last | last, peek, take-last | last | last | +| lines | | | lines, words, split-with | +| match | | match (Ocaml), case (Elm) | case | +| nth | nth | Array.get | lookup | +| open | with-open | | | +| transpose | (apply mapv vector matrix) | | transpose | +| prepend | cons | cons, :: | :: | +| print | println | | putStrLn, print | +| range, 1..10 | range | range | 1..10, 'a'..'f' | +| reduce | reduce, reduce-kv | foldr | foldr | +| reverse | reverse, rseq | reverse, reverseInPlace | reverse | +| select | select-keys | | | +| shuffle | shuffle | | | +| size | count | | size, length | +| skip | rest | tail | tail | +| skip until | drop-while | | | +| skip while | drop-while | dropWhile | dropWhile, dropWhileEnd | +| sort-by | sort, sort-by, sorted-set-by | sort, sortBy, sortWith | sort, sortBy | +| split row | split, split-{at,with,lines} | split, words, lines | split, words, lines | +| str | clojure.string functions | String functions | | +| str join | join | concat | intercalate | +| str trim | trim, triml, trimr | trim, trimLeft, trimRight | strip | +| sum | apply + | sum | sum | +| take | take, drop-last, pop | take, init | take, init | +| take until | take-while | takeWhile | takeWhile | +| take while | take-while | takeWhile | takeWhile | +| uniq | set | Set.empty | Data.Set | +| where | filter, filterv, select | filter, filterMap | filter | diff --git a/zh-CN/book/nushell_map_imperative.md b/zh-CN/book/nushell_map_imperative.md index 7341115634c..2681e03a15a 100644 --- a/zh-CN/book/nushell_map_imperative.md +++ b/zh-CN/book/nushell_map_imperative.md @@ -2,100 +2,74 @@ 这个表格的目的是帮助你了解 Nu 的内置和插件所提供的命令与命令式语言的关系。我们试图制作一张所有 Nu 命令和它们在其他语言中的对应关系的表。欢迎大家参与贡献。 -注意:此表假设 Nu 0.43 或更高版本。 +注意:此表假设 Nu 0.94 或更高版本。 -| Nushell | Python | Kotlin (Java) | C++ | Rust | -| ------------ | ----------------------------- | --------------------------------------------------- | ----------------------- | --------------------------------------------- | -| alias | | | | | -| append | list.append, set.add | add | push_back, emplace_back | push, push_back | -| args | | | | | -| autoview | | | | | -| math avg | statistics.mean | | | | -| calc, = math | math operators | math operators | math operators | math operators | -| cd | | | | | -| clear | | | | | -| clip | | | | | -| compact | | | | | -| config | | | | | -| count | len | size, length | length | len | -| cp | shutil.copy | | | | -| date | datetime.date.today | java.time.LocalDate.now | | | -| debug | | | | | -| default | | | | | -| drop | | | | | -| du | shutil.disk_usage | | | | -| each | for | for | for | for | -| echo | print | println | printf | println! | -| enter | | | | | -| exit | exit | System.exit, kotlin.system.exitProcess | exit | exit | -| fetch | urllib.request.urlopen | | | | -| first | list[0] | List[0], peek | vector[0], top | Vec[0] | -| format | format | format | format | format! | -| from | csv, json, sqlite3 | | | | -| get | dict[\"key\"] | Map[\"key\"] | map[\"key\"] | HashMap["key"], get, entry | -| group-by | itertools.groupby | groupBy | | group_by | -| headers | | | | | -| help | help | | | | -| histogram | | | | | -| history | | | | | -| inc(`*`) | x += 1 | x++ | x++ | x += 1 | -| insert | list.insert | | | | -| is-empty | is None | isEmpty | empty | is_empty | -| keep | list[:x] | | | &Vec[..x] | -| keep until | | | | | -| keep while | itertools.takewhile | | | | -| kill | os.kill | | | | -| last | list[-1] | | | &Vec[Vec.len()-1] | -| lines | split, splitlines | split | views::split | split, split_whitespace, rsplit, lines | -| ls | os.listdir | | | | -| match(`*`) | re.findall | Regex.matches | regex_match | | -| merge | | | | | -| mkdir | os.mkdir | | | | -| mv | shutil.move | | | | -| next | | | | | -| nth | list[x] | List[x] | vector[x] | Vec[x] | -| open | open | | | | -| parse | | | | | -| transpose | zip(\*matrix) | | | | -| post(`*`) | urllib.request.urlopen | | | | -| prepend | deque.appendleft | | | | -| prev | | | | | -| ps(`*`) | os.listdir('/proc') | | | | -| pwd | os.getcwd | | | | -| range | range | .., until, downTo, step | iota | .. | -| reduce | functools.reduce | reduce | reduce | fold, rfold, scan | -| reject | | | | | -| rename | shutil.move | | | | -| reverse | reversed, list.reverse | reverse, reversed, asReversed | reverse | rev | -| rm | os.remove | | | | -| save | io.TextIOWrapper.write | | | | -| select | {k:dict[k] for k in keylist} | | | | -| shells | | | | | -| shuffle | random.shuffle | | | | -| size | len | | | | -| skip | list[x:] | | | &Vec[x..],skip | -| skip until | | | | | -| skip while | itertools.dropwhile | | | skip_while | -| sort-by | sorted, list.sort | sortedBy, sortedWith, Arrays.sort, Collections.sort | sort | sort | -| split-by | str.split{,lines}, re.split | split | views::split | split | -| split column | | | | | -| split row | | | | | -| str(`*`) | str functions | String functions | string functions | &str, String functions | -| str join | str.join | joinToString | | join | -| str trim | strip, rstrip, lstrip | trim, trimStart, trimEnd | regex | trim, trim*{start,end}, strip*{suffix,prefix} | -| sum | sum | sum | reduce | sum | -| sys(`*`) | sys | | | | -| table | | | | | -| tags | | | | | -| tree(`*`) | | | | | -| to | csv, json, sqlite3 | | | | -| touch | open(path, 'a').close() | | | | -| uniq | set | Set | set | HashSet | -| upsert | | | | | -| version | sys.version, sys.version_info | | | | -| with-env | os.environ | | | | -| where | filter | filter | filter | filter | -| which | shutil.which | | | | -| wrap | | | | | - -- `*` - 这些命令是标准插件的一部分 +| Nushell | Python | Kotlin (Java) | C++ | Rust | +| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | ----------------------------------------------------------- | --------------------------- | ---------------------------------------------------- | +| [`append`](/commands/docs/append.md) | `list.append`, `set.add` | `add` | `push_back`, `emplace_back` | `push`, `push_back` | +| [`math avg`](/commands/docs/math_avg.md) | `statistics.mean` | | | | +| [`math`](/commands/docs/math.md), [Math Operators](nushell_operator_map.md) | Math operators | Math operators | Math operators | Math operators | +| [`cp`](/commands/docs/cp.md) | `shutil.copy` | | | `fs::copy` | +| [`date`](/commands/docs/date.md) | `datetime.date.today` | `java.time.LocalDate.now` | | | +| [`drop`](/commands/docs/drop.md) | `list[:-3]` | | | | +| [`du`](/commands/docs/du.md), [`ls --du`](/commands/docs/ls.md) | `shutil.disk_usage` | | | | +| [`each`](/commands/docs/each.md)
[`for`](/commands/docs/for.md) | `for` | `for` | `for` | `for` | +| [`exit`](/commands/docs/exit.md) | `exit()` | `System.exit`, `kotlin.system.exitProcess` | `exit` | `exit` | +| [`http get`](/commands/docs/http_get.md) | `urllib.request.urlopen` | | | | +| [`first`](/commands/docs/first.md) | `list[:x]` | `List[0]`, `peek` | `vector[0]`, `top` | `Vec[0]` | +| [`format`](/commands/docs/format.md) | `format` | `format` | `format` | `format!` | +| [`from`](/commands/docs/from.md) | `csv`, `json`, `sqlite3` | | | | +| [`get`](/commands/docs/get.md) | `dict[\"key\"]` | `Map[\"key\"]` | `map[\"key\"]` | `HashMap["key"]`, `get`, `entry` | +| [`group-by`](/commands/docs/group-by.md) | `itertools.groupby` | `groupBy` | | `group_by` | +| [`headers`](/commands/docs/headers.md) | `keys` | | | | +| [`help`](/commands/docs/help.md) | `help()` | | | | +| [`insert`](/commands/docs/insert.md) | `dict[\"key\"] = val` | | `map.insert({ 20, 130 })` | `map.insert(\"key\", val)` | +| [`is-empty`](/commands/docs/is-empty.md) | `is None`, `is []` | `isEmpty` | `empty` | `is_empty` | +| [`take`](/commands/docs/take.md) | `list[:x]` | | | `&Vec[..x]` | +| [`take until`](/commands/docs/take_until.md) | `itertools.takewhile` | | | | +| [`take while`](/commands/docs/take_while.md) | `itertools.takewhile` | | | | +| [`kill`](/commands/docs/kill.md) | `os.kill` | | | | +| [`last`](/commands/docs/last.md) | `list[-x:]` | | | `&Vec[Vec.len()-1]` | +| [`length`](/commands/docs/length.md) | `len` | `size`, `length` | `length` | `len` | +| [`lines`](/commands/docs/lines.md) | `split`, `splitlines` | `split` | `views::split` | `split`, `split_whitespace`, `rsplit`, `lines` | +| [`ls`](/commands/docs/ls.md) | `os.listdir` | | | `fs::read_dir` | +| [`match`](/commands/docs/match.md) | `match` | `when` | | `match` | +| [`merge`](/commands/docs/merge.md) | `dict.append` | | | `map.extend` | +| [`mkdir`](/commands/docs/mkdir.md) | `os.mkdir` | | | `fs::create_dir` | +| [`mv`](/commands/docs/mv.md) | `shutil.move` | | | `fs::rename` | +| [`get`](/commands/docs/get.md) | `list[x]` | `List[x]` | `vector[x]` | `Vec[x]` | +| [`open`](/commands/docs/open.md) | `open` | | | | +| [`transpose`](/commands/docs/transpose.md) | `zip(\*matrix)` | | | | +| [`http post`](/commands/docs/http_post.md) | `urllib.request.urlopen` | | | | +| [`prepend`](/commands/docs/prepend.md) | `deque.appendleft` | | | | +| [`print`](/commands/docs/print.md) | `print` | `println` | `printf` | `println!` | +| [`ps`](/commands/docs/ps.md) | `os.listdir('/proc')` | | | | +| [`pwd`](/commands/docs/pwd.md) | `os.getcwd` | | | `env::current_dir` | +| [`range`](types_of_data.html#ranges) type | `range` | `..`, `until`, `downTo`, `step` | `iota` | `..` | +| [`reduce`](/commands/docs/reduce.md) | `functools.reduce` | `reduce` | `reduce` | `fold`, `rfold`, `scan` | +| [`reject`](/commands/docs/reject.md) | `del` | | | | +| [`rename`](/commands/docs/rename.md) | `dict[\"key2\"] = dict.pop(\"key\")` | | | `map.insert(\"key2\", map.remove(\"key\").unwrap())` | +| [`reverse`](/commands/docs/reverse.md) | `reversed`, `list.reverse` | `reverse`, `reversed`, `asReversed` | `reverse` | `rev` | +| [`rm`](/commands/docs/rm.md) | `os.remove` | | | | +| [`save`](/commands/docs/save.md) | `io.TextIOWrapper.write` | | | | +| [`select`](/commands/docs/select.md) | `{k:dict[k] for k in keys}` | | | | +| [`shuffle`](/commands/docs/shuffle.md) | `random.shuffle` | | | | +| [`str stats`](/commands/docs/str_stats.md)
[`str length`](/commands/docs/str_length.md)
[`length`](/commands/docs/length.md) | `len` | | | `len` | +| [`skip`](/commands/docs/skip.md) | `list[x:]` | | | `&Vec[x..]`, `skip` | +| [`skip until`](/commands/docs/skip_until.md) | `itertools.dropwhile` | | | | +| [`skip while`](/commands/docs/skip_while.md) | `itertools.dropwhile` | | | `skip_while` | +| [`sort-by`](/commands/docs/sort-by.md) | `sorted`, `list.sort` | `sortedBy`, `sortedWith`, `Arrays.sort`, `Collections.sort` | `sort` | `sort` | +| [`split row`](/commands/docs/split_row.md) | `str.split{,lines}`, `re.split` | `split` | `views::split` | `split` | +| [`str`](/commands/docs/str.md) | `str` functions | String functions | String functions | `&str`, String functions | +| [`str join`](/commands/docs/str_join.md) | `str.join` | `joinToString` | | `join` | +| [`str trim`](/commands/docs/str_trim.md) | `strip`, `rstrip`, `lstrip` | `trim`, `trimStart`, `trimEnd` | Regex | `trim`, `trim*{start,end}`, `strip*{suffix,prefix}` | +| [`math sum`](/commands/docs/math_sum.md) | `sum` | `sum` | `reduce` | `sum` | +| [`to`](/commands/docs/to.md) | `import csv`, `json`, `sqlite3` | | | | +| [`touch`](/commands/docs/touch.md) | `open(path, 'a').close()` | | | | +| [`uniq`](/commands/docs/uniq.md) | `set` | Set | `set` | `HashSet` | +| [`upsert`](/commands/docs/upsert.md) | `dict[\"key\"] = val` | | | | +| [`version`](/commands/docs/version.md) | `sys.version`, `sys.version_info` | | | | +| [`with-env`](/commands/docs/with-env.md)
`$env.FOO = "bar"` | `os.environ` | | | | +| [`where`](/commands/docs/where.md) | `filter` | `filter` | `filter` | `filter` | +| [`which`](/commands/docs/which.md) | `shutil.which` | | | | +| [`wrap`](/commands/docs/wrap.md) | `{ "key" : val }` | | | | diff --git a/zh-CN/book/operators.md b/zh-CN/book/operators.md index 95cbfb9eee6..8bbcb343c82 100644 --- a/zh-CN/book/operators.md +++ b/zh-CN/book/operators.md @@ -2,52 +2,89 @@ Nushell 支持以下常见的数学、逻辑和字符串操作的运算符: -| 运算符 | 描述 | -| ------------- | --------------------------------- | -| `+` | 加 | -| `-` | 减 | -| `*` | 乘 | -| `/` | 除 | -| `**` | 指数 (幂) | -| `mod` | 取模 | -| `==` | 等于 | -| `!=` | 不等于 | -| `<` | 小于 | -| `<=` | 小于等于 | -| `>` | 大于 | -| `>=` | 大于等于 | -| `=~` | 正则匹配 / 字符串包含另一个字符串 | -| `!~` | 正则不匹配 / 字符串*不*包含另一个 | -| `in` | 列表包含值 | -| `not-in` | 列表不包含值 | -| `not` | 逻辑取反 | -| `and` | 两个布尔值与运算 | -| `or` | 两个布尔值或运算 | -| `//` | 整除并向下取整 | -| `**` | 幂运算 | -| `bit-or` | 按位或 | -| `bit-xor` | 按位异或 | -| `bit-and` | 按位与 | -| `bit-shl` | 按位左移 | -| `bit-shr` | 按位右移 | -| `starts-with` | 字符串开始检测 | -| `ends-with` | 字符串结尾检测 | +| 运算符 | 描述 | +| --- | --- | +| `+` | 加 | +| `-` | 减 | +| `*` | 乘 | +| `/` | 除 | +| `//` | 整除 | +| `mod` | 取模 | +| `**` | 幂运算 | +| `==` | 等于 | +| `!=` | 不等于 | +| `<` | 小于 | +| `<=` | 小于等于 | +| `>` | 大于 | +| `>=` | 大于等于 | +| `=~` 或 `like` | 正则匹配 / 字符串包含另一个 | +| `!~` 或 `not-like` | 正则不匹配 / 字符串*不*包含另一个 | +| `in` | 值在列表中 | +| `not-in` | 值不在列表中 | +| `has` | 列表包含值 | +| `not-has` | 列表不包含值 | +| `not` | 逻辑取反 | +| `and` | 两个布尔值与运算 (短路) | +| `or` | 两个布尔值或运算 (短路) | +| `xor` | 两个布尔值异或运算 | +| `bit-or` | 按位或 | +| `bit-xor` | 按位异或 | +| `bit-and` | 按位与 | +| `bit-shl` | 按位左移 | +| `bit-shr` | 按位右移 | +| `starts-with` | 字符串开始检测 | +| `ends-with` | 字符串结尾检测 | +| `++` | 追加列表 | 圆括号可用于分组以指定求值顺序,或用于调用命令并在表达式中使用结果。 ## 运算符结合顺序 -数学运算的结合顺序如下(从最高优先级到最低): +要了解操作的优先级,可以运行命令:`help operators | sort-by precedence -r`。 + +按优先级降序排列,本文详细介绍了以下操作: - 圆括号 (`()`) -- 乘 (`*`) 、 除 (`/`) 和幂 (`**`) +- 幂 (`**`) +- 乘 (`*`) 、 除 (`/`) 、整除 (`//`) 和取模 (`mod`) - 加 (`+`) 和减 (`-`) +- 位移 (`bit-shl`, `bit-shr`) +- 比较操作 (`==`, `!=`, `<`, `>`, `<=`, `>=`)、成员测试 (`in`, `not-in`, `starts-with`, `ends-with`)、正则匹配 (`=~`, `!~`) 和列表追加 (`++`) +- 按位与 (`bit-and`) +- 按位异或 (`bit-xor`) +- 按位或 (`bit-or`) +- 逻辑与 (`and`) +- 逻辑异或 (`xor`) +- 逻辑或 (`or`) +- 赋值操作 +- 逻辑非 (`not`) ```nu 3 * (1 + 2) # => 9 ``` +## 类型 + +并非所有操作都对所有数据类型有意义。 +如果你试图对不兼容的数据类型执行操作,你将收到一条错误消息,该消息应解释出了什么问题: +```nu +"spam" - 1 +# => Error: nu::parser::unsupported_operation (link) +# => +# => × Types mismatched for operation. +# => ╭─[entry #49:1:1] +# => 1 │ "spam" - 1 +# => · ───┬── ┬ ┬ +# => · │ │ ╰── int +# => · │ ╰── doesn't support these values. +# => · ╰── string +# => ╰──── +# => help: Change string or int to be the right types and try again. +``` + +规则有时可能感觉有点严格,但另一方面,应该会有更少的意外副作用。 + ## 正则表达式 / 字符串包含运算符 `=~`和`!~`运算符提供了一种更方便的方法来评估 [正则表达式](https://cheatography.com/davechild/cheat-sheets/regular-expressions/)。你不需要知道正则表达式就可以使用它们 —— 它们也是检查一个字符串是否包含另一个的简单方法: @@ -87,3 +124,153 @@ ls | where name =~ ^nu # returns all files whose names start with "nu" ```nu ("FOO" | str downcase) == ("Foo" | str downcase) ``` + +## 扩展运算符 + +Nushell 有一个用于解包列表和记录的扩展运算符 (`...`)。如果你以前使用过 JavaScript,你可能对它很熟悉。有些语言使用 `*` 作为它们的扩展/散开运算符。它可以在需要多个值或键值对的地方扩展列表或记录。 + +你可以在三个地方使用扩展运算符: + +- [在列表字面量中](#in-list-literals) +- [在记录字面量中](#in-record-literals) +- [在命令调用中](#in-command-calls) + +### 在列表字面量中 + +假设你有多个列表想要连接在一起,但你也想在其中穿插一些单个值。这可以用 `append` 和 `prepend` 来完成,但扩展运算符可以让你更轻松地做到这一点。 + +```nu +let dogs = [Spot, Teddy, Tommy] +let cats = ["Mr. Humphrey Montgomery", Kitten] +[ + ...$dogs + Polly + ...($cats | each { |elt| $"($elt) \(cat\)" }) + ...[Porky Bessie] + ...Nemo +] +# => ╭───┬───────────────────────────────╮ +# => │ 0 │ Spot │ +# => │ 1 │ Teddy │ +# => │ 2 │ Tommy │ +# => │ 3 │ Polly │ +# => │ 4 │ Mr. Humphrey Montgomery (cat) │ +# => │ 5 │ Kitten (cat) │ +# => │ 6 │ Porky │ +# => │ 7 │ Bessie │ +# => │ 8 │ ...Nemo │ +# => ╰───┴───────────────────────────────╯ +``` + +以下是使用 `append` 的等效版本: +```nu +$dogs | + append Polly | + append ($cats | each { |elt| $"($elt) \(cat\)" }) | + append [Porky Bessie] | + append ...Nemo +``` + +请注意,每次调用 `append` 都会创建一个新列表,这意味着在第二个示例中,创建了 3 个不必要的中间列表。扩展运算符不是这种情况,因此如果你要一遍又一遍地连接大量的大型列表,使用 `...` 可能会有(非常微小的)性能优势。 + +你可能已经注意到,上面结果列表的最后一项是 `"...Nemo"`。这是因为在列表字面量中,它只能用于扩展列表,而不能用于扩展字符串。因此,在列表字面量中,它只能在变量 (`...$foo`)、子表达式 (`...(foo)`) 和列表字面量 (`...[foo]`) 之前使用。 + +如果 `...` 和下一个表达式之间有任何空格,`...` 也不会被识别为扩展运算符: + +```nu +[ ... [] ] +# => ╭───┬────────────────╮ +# => │ 0 │ ... │ +# => │ 1 │ [list 0 items] │ +# => ╰───┴────────────────╯ +``` + +这主要是为了避免 `...` 在诸如 `mv ... $dir` 之类的命令中被误认为是扩展运算符。 + +### 在记录字面量中 + +假设你有一个包含一些配置信息的记录,并且你想向该记录添加更多字段: + +```nu +let config = { path: /tmp, limit: 5 } +``` + +你可以使用扩展运算符创建一个包含 `$config` 所有字段和一些新字段的新记录。你可以在单个记录字面量中使用多个记录的扩展。 + +```nu +{ + ...$config, + users: [alice bob], + ...{ url: example.com }, + ...(sys mem) +} +# => ╭────────────┬───────────────╮ +# => │ path │ /tmp │ +# => │ limit │ 5 │ +# => │ │ ╭───┬───────╮ │ +# => │ users │ │ 0 │ alice │ │ +# => │ │ │ 1 │ bob │ │ +# => │ │ ╰───┴───────╯ │ +# => │ url │ example.com │ +# => │ total │ 8.3 GB │ +# => │ free │ 2.6 GB │ +# => │ used │ 5.7 GB │ +# => │ available │ 2.6 GB │ +# => │ swap total │ 2.1 GB │ +# => │ swap free │ 18.0 MB │ +# => │ swap used │ 2.1 GB │ +# => ╰────────────┴───────────────╯ +``` + +与列表类似,在记录字面量中,扩展运算符只能在变量 (`...$foo`)、子表达式 (`...(foo)`) 和记录字面量 (`...{foo:bar}`) 之前使用。同样,`...` 和下一个表达式之间不能有空格,才能被识别为扩展运算符。 + +### 在命令调用中 + +你还可以将参数扩展到命令中,前提是它要么有剩余参数,要么是外部命令。 + +这是一个具有剩余参数的自定义命令示例: + +```nu +def foo [ --flag req opt? ...args ] { + { flag: $flag, req: $req, opt: $opt, args: $args } | to nuon +} +``` + +它有一个标志 (`--flag`)、一个必需的位置参数 (`req`)、一个可选的位置参数 (`opt?`) 和一个剩余参数 (`args`)。 + +如果你有一个要传递给 `args` 的参数列表,你可以像在[列表字面量中](#in-list-literals)扩展列表一样扩展它。规则相同:扩展运算符仅在变量、子表达式和列表字面量之前被识别,并且中间不允许有空格。 + +```nu +foo "bar" "baz" ...[1 2 3] # 使用 ...,数字被视为单独的参数 +# => { flag: false, req: bar, opt: baz, args: [1, 2, 3] } +foo "bar" "baz" [1 2 3] # 不使用 ...,[1 2 3] 被视为单个参数 +# => { flag: false, req: bar, opt: baz, args: [[1, 2, 3]] } +``` + +使用扩展运算符的一种更有用的方法是,如果你有另一个带有剩余参数的命令,并且你希望它将其参数转发给 `foo`: + +```nu +def bar [ ...args ] { foo --flag "bar" "baz" ...$args } +bar 1 2 3 +# => { flag: true, req: bar, opt: baz, args: [1, 2, 3] } +``` + +你可以在一次调用中扩展多个列表,也可以穿插单个参数: + +```nu +foo "bar" "baz" 1 ...[2 3] 4 5 ...(6..9 | take 2) last +# => { flag: false, req: bar, opt: baz, args: [1, 2, 3, 4, 5, 6, 7, last] } +``` + +标志/命名参数可以跟在扩展参数之后,就像它们可以跟在常规剩余参数之后一样: + +```nu +foo "bar" "baz" 1 ...[2 3] --flag 4 +# => { flag: true, req: bar, opt: baz, args: [1, 2, 3, 4] } +``` + +如果扩展参数在可选位置参数之前,则该可选参数被视为省略: + +```nu +foo "bar" ...[1 2] "not opt" # null 表示没有为 opt 提供参数 +# => { flag: false, req: bar, opt: null, args: [1, 2, "not opt"] } diff --git a/zh-CN/book/overlays.md b/zh-CN/book/overlays.md index 10e9d778024..115a460bd45 100644 --- a/zh-CN/book/overlays.md +++ b/zh-CN/book/overlays.md @@ -19,17 +19,26 @@ module spam { "foo" } - export alias bar = "bar" + export alias bar = echo "bar" - export env BAZ { - "baz" + export-env { + load-env { BAZ: "baz" } } } ``` 我们将在本章中使用这个模块。每当你看到 `overlay use spam`,就应该知道 `spam` 是指这个模块。 -要创建覆层,请调用 [`overlay use`](/commands/docs/overlay_use.md)。 +::: tip +该模块可以通过[模块](modules.md)中描述的三种方法中的任何一种来创建: + +- "内联"模块(本例中使用) +- 文件 +- 目录 + +::: + +要创建覆层,请调用 [`overlay use`](/commands/docs/overlay_use.md): ```nu overlay use spam @@ -50,8 +59,12 @@ overlay list # => ───┴────── ``` +它将模块的定义带入当前作用域,并以与 [`use`](/commands/docs/use.md) 命令相同的方式评估 [`export-env`](/commands/docs/export-env.md) 代码块(参见[模块](modules.md#environment-variables)章节)。 + +::: tip 在下面的章节中,`>` 的提示语前面会有最后一个活动覆层的名称。 `(spam)> some-command` 表示当输入命令时,`spam` 覆层是最后活动的覆层。 +::: ## 移除覆层 @@ -73,7 +86,7 @@ Error: Can't run executable... 任何添加的覆层都会在作用域结束时被移除: ```nu -(zero)> do { overlay use spam; foo } +(zero)> do { overlay use spam; foo } # 覆层仅在代码块内活动 foo (zero)> overlay list @@ -82,7 +95,7 @@ foo ───┴────── ``` -此外,[`overlay hide`](/commands/docs/overlay_hide.md) 在没有参数的情况下,将删除最后一个活动的覆层。 +移除覆层的最后一种方法是调用不带参数的 [`overlay hide`](/commands/docs/overlay_hide.md),它将删除最后一个活动的覆层。 ## 覆层是可记录的 @@ -132,7 +145,6 @@ eggs `eggs` 命令被添加到 `scratchpad` 中,同时保持 `spam` 不变。 -_0.64 版本新增:_ 为了让上述步骤不那么冗长,你可以使用 [`overlay new`](/commands/docs/overlay_new.md) 命令: ```nu @@ -145,7 +157,45 @@ _0.64 版本新增:_ ::: -## 保存定义 +## 前缀覆层 + +[`overlay use`](/commands/docs/overlay_use.md) 命令会将模块中的所有命令和别名直接放入当前命名空间。 +但是,你可能希望将它们作为子命令保留在模块名称之后。 +这就是 `--prefix` 的作用: + +```nu +(zero)> module spam { + export def foo [] { "foo" } +} + +(zero)> overlay use --prefix spam + +(spam)> spam foo +foo +``` + +请注意,这不适用于环境变量。 + +## 重命名覆层 + +你可以使用 `as` 关键字更改添加的覆层的名称: + +```nu +(zero)> module spam { export def foo [] { "foo" } } + +(zero)> overlay use spam as eggs + +(eggs)> foo +foo + +(eggs)> overlay hide eggs + +(zero)> +``` + +如果你有一个通用的脚本名称,例如 virtualenv 的 `activate.nu`,但你希望为你的覆层提供一个更具描述性的名称,这可能很有用。 + +## 保留定义 有时,你可能想删除一个覆层,但保留所有你添加的自定义定义,而不必在下一个活动覆层中重新定义它们: @@ -162,6 +212,25 @@ eggs `--keep-custom` 标志正是用来做这个的。 +也可以使用 `--keep-env` 标志保留在覆层内定义的环境变量列表,但删除其余部分: + +```nu +(zero)> module spam { + export def foo [] { "foo" } + export-env { $env.FOO = "foo" } +} + +(zero)> overlay use spam + +(spam)> overlay hide spam --keep-env [ FOO ] + +(zero)> foo +Error: Can't run executable... + +(zero)> $env.FOO +foo +``` + ## 覆层顺序 覆层被排成一个堆栈。 diff --git a/zh-CN/book/pipelines.md b/zh-CN/book/pipelines.md index 25745b14a90..a540f640f67 100644 --- a/zh-CN/book/pipelines.md +++ b/zh-CN/book/pipelines.md @@ -7,35 +7,275 @@ Nu 的核心设计之一是管道,这个设计思想可以追溯到几十年 一个管道由三部分组成:输入、过滤器和输出。 ```nu -open "Cargo.toml" | inc package.version --minor | save "Cargo_new.toml" +open Cargo.toml | update workspace.dependencies.base64 0.24.2 | save Cargo_new.toml ``` -第一条命令:`open "Cargo.toml"` 是一个输入(有时也称为 "源" 或 "生产者"),它创建或加载数据,并将其送入管道。管道待处理的值正是来自于此输入。像[`ls`](/commands/docs/ls.md)这样的命令也是输入,因为它们从文件系统中获取数据,并通过管道发送以便能被后续使用。 +第一条命令:`open Cargo.toml` 是一个输入(有时也称为 "源" 或 "生产者"),它创建或加载数据,并将其送入管道。管道待处理的值正是来自于此输入。像[`ls`](/commands/docs/ls.md)这样的命令也是输入,因为它们从文件系统中获取数据,并通过管道发送以便能被后续使用。 -第二个命令:`inc package.version --minor` 是一个过滤器。过滤器获取输入的数据并对其进行处理。它们可能会修改它(如我们例子中的[`inc`](/commands/docs/inc.md)命令),或者在值通过时对其做其他操作,如记录。 +第二个命令:`update workspace.dependencies.base64 0.24.2` 是一个过滤器。过滤器获取输入的数据并对其进行处理。它们可能会修改它(如我们例子中的[`update`](/commands/docs/update.md)命令),或者在值通过时对其做其他操作,如记录。 最后一条命令:`save "Cargo_new.toml"` 是一个输出(有时称为 "接收者")。输出从管道中获取输入,并对其进行一些最终操作。在我们的例子中,我们在最后一步把通过管道的内容保存到一个文件中。还有一些其他类型的输出命令可以获取数值并供用户查看。 `$in` 变量可以将管道收集成一个值,允许你将整个流作为一个参数访问,比如: ```nu -echo 1 2 3 | $in.1 * $in.2 +[1 2 3] | $in.1 * $in.2 # => 6 ``` ## 多行管道 -如果一个管道对一行来说有些长,你可以把它放在`(`和`)`里,以创建一个子表达式: +如果一个管道对一行来说有些长,你可以把它放在`(`和`)`里: ```nu -( +let year = ( "01/22/2021" | parse "{month}/{day}/{year}" | get year ) ``` -也可以参考 [子表达式](variables_and_subexpressions.html#子表达式) +## 分号 + +看这个例子: + +```nu +line1; line2 | line3 +``` + +这里,分号与管道结合使用。当使用分号时,不会产生任何输出数据用于管道。因此,紧跟在分号后面的 `$in` 变量将不起作用。 + +- 因为 `line1` 后面有分号,所以该命令将运行到完成并在屏幕上显示。 +- `line2` | `line3` 是一个正常的管道。它运行,其内容在 `line1` 的内容之后显示。 + +## 管道输入和特殊的 `$in` 变量 + +Nu 的大部分可组合性来自于特殊的 `$in` 变量,它保存着当前的管道输入。 + +`$in` 在以下情况下特别有用: + +- 命令或外部参数中 +- 过滤器中 +- 接受管道输入的自定义命令定义或脚本中 + +### `$in` 作为命令参数或表达式的一部分 + +比较下面两个创建以明天日期为名称一部分的目录的命令行。以下是等效的: + +使用子表达式: + +```nu +mkdir $'((date now) + 1day | format date '%F') Report' +``` + +或者使用管道: + +```nu +date now # 1: 今天 +| $in + 1day # 2: 明天 +| format date '%F' # 3: 格式化为 YYYY-MM-DD +| $'($in) Report' # 4: 格式化目录名 +| mkdir $in # 5: 创建目录 +``` + +虽然第二种形式对于这个刻意设计的例子可能过于冗长,但你会注意到几个优点: + +- 它可以通过简单的 (上箭头) 来重复前一个命令并添加管道的下一个阶段,从而逐步组合。 +- 它可以说更具可读性。 +- 如果需要,可以对每个步骤进行注释。 +- 管道中的每个步骤都可以用 [`inspect` 进行调试](/commands/docs/inspect.html)。 + +让我们检查上面例子中每一行 `$in` 的内容: + +- 在第 2 行,`$in` 指的是第 1 行 `date now` 的结果(一个日期时间值)。 +- 在第 4 行,`$in` 指的是第 3 行格式化后的明天日期,并用于插值字符串中。 +- 在第 5 行,`$in` 指的是第 4 行插值字符串的结果,例如 '2024-05-14 Report'。 + +### 过滤器闭包中的管道输入 + +某些[过滤器命令](/commands/categories/filters.html)可能会修改其闭包的管道输入,以便更方便地访问预期的上下文。例如: + +```nu +1..10 | each {$in * 2} +``` + +`each` 过滤器不是引用整个 10 个数字的范围,而是将 `$in` 修改为引用*当前迭代*的值。 + +在大多数过滤器中,管道输入及其产生的 `$in` 将与闭包参数相同。对于 `each` 过滤器,以下示例与上面的示例等效: + +```nu +1..10 | each {|value| $value * 2} +``` + +然而,一些过滤器会为其闭包的输入分配一个更方便的值。`update` 过滤器就是一个例子。`update` 命令闭包的管道输入(以及 `$in`)指的是正在更新的*列*,而闭包参数指的是整个记录。因此,以下两个示例也是等效的: + +```nu +ls | update name {|file| $file.name | str upcase} +ls | update name {str upcase} +``` + +对于大多数过滤器,第二个版本将引用整个 `file` 记录(包含 `name`、`type`、`size` 和 `modified` 列)。然而,对于 `update`,它特指正在更新的*列*的内容,在本例中是 `name`。 + +### 自定义命令定义和脚本中的管道输入 + +参见:[自定义命令 -> 管道输入](custom_commands.html#pipeline-input) + +### `$in` 何时改变(以及何时可以重用)? + +- **_规则 1:_** 当在闭包或块中的管道的第一个位置使用时,`$in` 指的是该闭包/块的管道(或过滤器)输入。 + + 示例: + + ```nushell + def echo_me [] { + print $in + } + true | echo_me + # => true + ``` + +- **_规则 1.5:_** 这在当前作用域中始终成立。即使在闭包或块的后续行中,当在*任何管道*的第一个位置使用时,`$in` 都是相同的值。 + + 示例: + + ```nu + [ a b c ] | each { + print $in + print $in + $in + } + ``` + + 在每次迭代中,所有三个 `$in` 的值都是相同的,所以输出为: + + ```nu + a + a + b + b + c + c + ╭───┬───╮ + │ 0 │ a │ + │ 1 │ b │ + │ 2 │ c │ + ╰───┴───╯ + ``` + +- **_规则 2:_** 当在管道中的任何其他位置(而不是第一个位置)使用时,`$in` 指的是前一个表达式的结果: + + 示例: + + ```nushell + 4 # 管道输入 + | $in * $in # 此表达式中 $in 为 4 + | $in / 2 # 此表达式中 $in 现在为 16 + | $in # $in 现在为 8 + # => 8 + ``` + +- **_规则 2.5:_** 在闭包或块内部,规则 2 的使用发生在一个新的作用域(子表达式)中,其中“新”的 `$in` 值是有效的。这意味着规则 1 和规则 2 的使用可以在同一个闭包或块中共存。 + + 示例: + + ```nushell + 4 | do { + print $in # 闭包作用域的 $in 为 4 + + let p = ( # 显式子表达式,但无论如何都会创建一个 + $in * $in # 初始管道位置的 $in 在这里仍然是 4 + | $in / 2 # $in 现在是 16 + ) # $p 是结果,8 - 子表达式作用域结束 + + print $in # 在闭包作用域,"原始"的 $in 仍然是 4 + print $p + } + ``` + + 所以 3 个 `print` 语句的输出是: + + ```nu + 4 + 4 + 8 + ``` + + 同样,即使上面的命令使用更紧凑、隐式的子表达式形式,这也将成立: + + 示例: + + ```nushell + 4 | do { + print $in # 闭包作用域的 $in 为 4 + let p = $in * $in | $in / 2 # 隐式 let 子表达式 + print $in # 在闭包作用域, $in 仍然是 4 + print $p + } + + 4 + 4 + 8 + ``` + +- **_规则 3:_** 当没有输入时使用 `$in`,`$in` 为 null。 + + 示例: + + ```nushell + # 输入 + 1 | do { $in | describe } + # => int + "Hello, Nushell" | do { $in | describe } + # => string + {||} | do { $in | describe } + # => closure + + # 没有输入 + do { $in | describe } + # => nothing + ``` + +* **_规则 4:_** 在由分号分隔的多语句行中,`$in` 不能用于捕获前一个*语句*的结果。 + + 这与没有输入相同: + + ```nushell + ls / | get name; $in | describe + # => nothing + ``` + + 相反,只需继续管道: + + ```nushell + ls / | get name | $in | describe + # => list + ``` + +### 多行代码中 `$in` 的最佳实践 + +虽然可以如上所示重用 `$in`,但在闭包/块的第一行将其值赋给另一个变量通常有助于提高可读性和调试。 + +示例: + +```nu +def "date info" [] { + let day = $in + print ($day | format date '%v') + print $'... was a ($day | format date '%A')' + print $'... was day ($day | format date '%j') of the year' +} + +'2000-01-01' | date info +# => 1-Jan-2000 +# => ... was a Saturday +# => ... was day 001 of the year +``` + +### `$in` 的可收集性 + +目前,在管道中的流上使用 `$in` 会产生一个“已收集”的值,这意味着管道在处理 `$in` 之前会“等待”流完成。但是,此行为在将来的版本中不保证。为确保将流收集到单个变量中,请使用 [`collect` 命令](/commands/docs/collect.html)。 + +同样,当正常的管道输入足够时,请避免使用 `$in`,因为在内部 `$in` 会强制从 `PipelineData` 转换为 `Value`,并且*可能*导致性能下降和/或内存使用增加。 ## 与外部命令交互 @@ -53,20 +293,230 @@ Nu 命令之间使用 Nu 的数据类型进行通信(见[数据类型](types_o Nu 在两个外部命令之间以与其他 Shell 相同的方式处理数据管道,比如 Bash。`external_command_1`的`stdout`与`external_command_2`的`stdin`相连,这让数据在两个命令之间自然流动。 -## 幕后解说 +### 命令输入和输出类型 + +上面的基础部分描述了如何在管道中将命令组合为输入、过滤器或输出。 +如何使用命令取决于它们在输入/输出处理方面提供了什么。 + +你可以使用 [`help `](/commands/docs/help.md) 来检查命令支持什么,它会显示相关的*输入/输出类型*。 + +例如,通过 `help first` 我们可以看到 [`first` 命令](/commands/docs/first.md) 支持多种输入和输出类型: + +```nu +help first +# => […] +# => Input/output types: +# => ╭───┬───────────┬────────╮ +# => │ # │ input │ output │ +# => ├───┼───────────┼────────┤ +# => │ 0 │ list │ any │ +# => │ 1 │ binary │ binary │ +# => │ 2 │ range │ any │ +# => ╰───┴───────────┴────────╯ + +[a b c] | first took 1ms +# => a + +1..4 | first took 21ms +# => 1 +``` + +再举一个例子,[`ls` 命令](/commands/docs/ls.md) 支持输出但不支持输入: + +```nu +help ls +# => […] +# => Input/output types: +# => ╭───┬─────────┬────────╮ +# => │ # │ input │ output │ +# => ├───┼─────────┼────────┤ +# => │ 0 │ nothing │ table │ +# => ╰───┴─────────┴────────╯ +``` + +这意味着,例如,尝试将管道输入到 `ls` (`echo .. | ls`) 会导致意想不到的结果。 +输入流被忽略,`ls` 默认列出当前目录。 + +要将像 `ls` 这样的命令集成到管道中,你必须显式引用输入并将其作为参数传递: + +```nu +echo .. | ls $in +``` + +请注意,这仅在 `$in` 与参数类型匹配时才有效。例如,`[dir1 dir2] | ls $in` 将失败,并显示错误 `can't convert list to string`。 + +没有默认行为的其他命令可能会以不同的方式失败,并显示明确的错误。 + +例如,`help sleep` 告诉我们 [`sleep`](/commands/docs/sleep.md) 不支持输入和输出类型: + +```nu +help sleep +# => […] +# => Input/output types: +# => ╭───┬─────────┬─────────╮ +# => │ # │ input │ output │ +# => ├───┼─────────┼─────────┤ +# => │ 0 │ nothing │ nothing │ +# => ╰───┴─────────┴─────────╯ +``` + +当我们错误地将管道输入到它时,我们不会像上面的 `ls` 示例那样得到意想不到的行为,而是会收到一个错误: + +```nu +echo 1sec | sleep +# => Error: nu::parser::missing_positional +# => +# => × Missing required positional argument. +# => ╭─[entry #53:1:18] +# => 1 │ echo 1sec | sleep +# => ╰──── +# => help: Usage: sleep ...(rest) . Use `--help` for more information. +``` + +虽然没有固定的规则,但 Nu 通常会尝试复制已建立的命令行为约定, +或者做“感觉正确”的事情。 +例如,`sleep` 不支持输入流的行为与 Bash `sleep` 的行为相匹配。 + +然而,许多命令确实有管道输入/输出,如果 कभी不清楚,请如上所述检查它们的 `help` 文档。 -你可能想知道,既然[`ls`](/commands/docs/ls.md)是一个输入而不是一个输出,我们为何能看到一个表格?其实 Nu 使用了另一个叫做[`table`](/commands/docs/table.md)的命令为我们自动添加了这个输出。[`table`](/commands/docs/table.md)命令被附加到任何没有输出的管道上,这使得我们可以看到结果。 +## 渲染显示结果 -实际上,该命令: +在交互模式下,当管道结束时,[`display_output` 钩子配置](https://www.nushell.sh/book/hooks.html#changing-how-output-is-displayed) 定义了结果将如何显示。 +默认配置使用 [`table` 命令](/commands/docs/table.md) 将结构化数据呈现为可视化表格。 + +以下示例显示了 `display_output` 钩子如何呈现 + +- 使用 `table -e` 的展开表格 +- 使用 `table` 的未展开表格 +- 空闭包 `{||}` 和空字符串 `''` 导致简单输出 +- 可以分配 `null` 来清除任何自定义,恢复到默认行为 + +```nu +$env.config.hooks.display_output = { table -e } +[1,2,3,[4,5,6]] +# => ╭───┬───────────╮ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ ╭───┬───╮ │ +# => │ │ │ 0 │ 4 │ │ +# => │ │ │ 1 │ 5 │ │ +# => │ │ │ 2 │ 6 │ │ +# => │ │ ╰───┴───╯ │ +# => ╰───┴───────────╯ + +$env.config.hooks.display_output = { table } +[1,2,3,[4,5,6]] +# => ╭───┬────────────────╮ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ [list 3 items] │ +# => ╰───┴────────────────╯ + +$env.config.hooks.display_output = {||} +[1,2,3,[4,5,6]] +# => 1 +# => 2 +# => 3 +# => [4 +# => 5 +# => 6] + +$env.config.hooks.display_output = '' +[1,2,3,[4,5,6]] +# => 1 +# => 2 +# => 3 +# => [4 +# => 5 +# => 6] + +# 清除为默认行为 +$env.config.hooks.display_output = null +[1,2,3,[4,5,6]] +# => ╭───┬────────────────╮ +# => │ 0 │ 1 │ +# => │ 1 │ 2 │ +# => │ 2 │ 3 │ +# => │ 3 │ [list 3 items] │ +# => ╰───┴────────────────╯ +``` + +## 将输出结果传递给外部命令 + +有时你希望将 Nushell 结构化数据输出到外部命令以进行进一步处理。但是,Nushell 对结构化数据的默认格式化选项可能不是你想要的。 +例如,你想在“/usr/share/vim/runtime”下找到一个名为“tutor”的文件并检查其所有权 + +```nu +ls /usr/share/nvim/runtime/ +# => ╭────┬───────────────────────────────────────┬──────┬─────────┬───────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├────┼───────────────────────────────────────┼──────┼─────────┼───────────────┤ +# => │ 0 │ /usr/share/nvim/runtime/autoload │ dir │ 4.1 KB │ 2 days ago │ +# => .......... +# => .......... +# => .......... +# => +# => │ 31 │ /usr/share/nvim/runtime/tools │ dir │ 4.1 KB │ 2 days ago │ +# => │ 32 │ /usr/share/nvim/runtime/tutor │ dir │ 4.1 KB │ 2 days ago │ +# => ├────┼───────────────────────────────────────┼──────┼─────────┼───────────────┤ +# => │ # │ name │ type │ size │ modified │ +# => ╰────┴───────────────────────────────────────┴──────┴─────────┴───────────────╯ +``` + +你决定使用 `grep` 并将结果通过[管道](https://www.nushell.sh/book/pipelines.html)传递给外部的 `^ls` + +```nu +ls /usr/share/nvim/runtime/ | get name | ^grep tutor | ^ls -la $in +# => ls: cannot access ''$'\342\224\202'' 32 '$'\342\224\202'' /usr/share/nvim/runtime/tutor '$'\342\224\202\n': No such file or directory +``` + +怎么了?Nushell 在将列表和表格作为文本传递给外部命令之前,会对其进行渲染(通过添加边框字符,如 `╭`,`─`,`┬`,`╮`)。如果这不是你想要的行为,你必须在将数据传递给外部命令之前,显式地将其转换为字符串。例如,你可以使用 [`to text`](/commands/docs/to_text.md) 来实现: ```nu -ls +ls /usr/share/nvim/runtime/ | get name | to text | ^grep tutor | tr -d '\n' | ^ls -la $in +# => total 24 +# => drwxr-xr-x@ 5 pengs admin 160 14 Nov 13:12 . +# => drwxr-xr-x@ 4 pengs admin 128 14 Nov 13:42 en +# => -rw-r--r--@ 1 pengs admin 5514 14 Nov 13:42 tutor.tutor +# => -rw-r--r--@ 1 pengs admin 1191 14 Nov 13:42 tutor.tutor.json ``` -和以下管道: +(实际上,对于这个简单的用法,你只需使用 [`find`](/commands/docs/find.md)) ```nu -ls | table +ls /usr/share/nvim/runtime/ | get name | find tutor | ansi strip | ^ls -al ...$in ``` -是一样的。 +## Nushell 中的命令输出 + +与外部命令不同,Nushell 命令类似于函数。大多数 Nushell 命令不会向 `stdout` 打印任何内容,而只是返回数据。 + +```nu +do { ls; ls; ls; "What?!" } +``` + +这意味着上面的代码不会在当前目录下三次显示文件。 +实际上,在 shell 中运行此代码只会显示 `"What?!"`,因为这是本例中 `do` 命令返回的值。但是,如果使用系统 `^ls` 命令而不是 `ls`,则确实会三次打印目录,因为 `^ls` 在运行时会打印其结果。 + +了解何时显示数据对于使用影响命令(如 `table`)显示输出的配置变量非常重要。 + +```nu +do { $env.config.table.mode = "none"; ls } +``` + +例如,上面的示例将 `$env.config.table.mode` 配置变量设置为 `none`,这会导致 `table` 命令在没有额外边框的情况下呈现数据。然而,如前所示,该命令实际上等同于 + +```nu +do { $env.config.table.mode = "none"; ls } | table +``` + +因为 Nushell `$env` 变量是[作用域的](https://www.nushell.sh/book/environment.html#scoping),这意味着示例中的 `table` 命令不受 `do` 块内环境修改的影响,数据将不会以应用的配置显示。 + +如果希望尽早显示数据,可以在作用域内显式应用 `| table`,或使用 `print` 命令。 + +```nu +do { $env.config.table.mode = "none"; ls | table } +do { $env.config.table.mode = "none"; print (ls) } +``` diff --git a/zh-CN/book/plugins.md b/zh-CN/book/plugins.md index 4237fe45a14..2a8ca349722 100644 --- a/zh-CN/book/plugins.md +++ b/zh-CN/book/plugins.md @@ -2,47 +2,324 @@ Nu 可以通过插件进行扩展。插件的行为与 Nu 的内置命令很相似,另外的好处是它们可以与 Nu 本身分开添加。 -Nu 的插件是可执行的;Nu 在需要时启动它们,并通过 [stdin, stdout 和 stderr](https://en.wikipedia.org/wiki/Standard_streams) 与它们进行通信。Nu 的插件可以使用 JSON 或 [Cap'n Proto](https://capnproto.org/) 作为它们的通信编码方式。 +::: warning 重要 +插件使用 `nu-plugin` 协议与 Nushell 通信。此协议是版本化的,插件必须使用 Nushell 提供的相同 `nu-plugin` 版本。 -## 添加一个插件 +更新 Nushell 时,请确保也更新你已注册的任何插件。 +::: -要添加一个插件,请调用[`plugin add`](/commands/docs/plugin_add.md)命令来告诉 Nu 在哪里可以找到它,与此同时,你还需要告诉 Nushell 这个插件使用什么方式进行编码。 +[[toc]] -Linux+macOS: +## 概述 + +- 为了使用插件,它需要被: + + - 安装 + - 添加 + - 导入 + +有两种类型的插件: + +- “核心插件”是官方维护的,通常与 Nushell 一起安装,在与 Nushell 可执行文件相同的目录中。 +- 第三方插件也可从许多来源获得。 + +`$NU_LIB_DIRS` 常量或 `$env.NU_LIB_DIRS` 环境变量可用于设置插件的搜索路径。 + +### 核心插件快速入门 + +要开始使用 Polars 插件: + +1. 大多数包管理器会自动随 Nushell 安装核心插件。然而,一个显著的例外是 `cargo`。如果你使用 `cargo` 安装了 Nushell,请参阅下面的[安装核心插件](#core-plugins)。 + +2. (推荐) 将插件搜索路径设置为包括 Nushell 及其插件的安装目录。假设核心插件与 Nushell 二进制文件安装在同一目录中,可以将以下内容添加到你的启动配置中: + + ```nu + const NU_PLUGIN_DIRS = [ + ($nu.current-exe | path dirname) + ...$NU_PLUGIN_DIRS + ] + ``` + +3. 将插件添加到插件注册表。这只需要做一次。名称是插件*文件*的名称,包括其扩展名: + + ```nu + # 在 Unix/Linux 平台上: + plugin add nu_plugin_polars + # 或者在 Windows 上 + plugin add nu_plugin_polars.exe + + plugin list # 确认它已添加到注册表 + ``` + + 或者,如果你在第 2 步中没有将二进制目录添加到插件路径,你仍然可以使用绝对路径: + + ```nu + plugin add ~/.local/share/rust/cargo/bin/nu_plugin_polars + ``` + +4. 导入插件(以立即开始使用)或重新启动 Nushell。注册表中的所有插件在 Nushell 启动时都会自动导入: + + ```nu + # 插件的名称,不带前导的 `nu_plugin` 和任何扩展名 + use polars + ``` + +5. 确认插件正在工作: + + ```nu + ls | polars into-df | describe + # => NuDataFrame + ``` + +## 安装插件 + +### 核心插件 + +Nushell 附带了一组官方维护的插件,其中包括: + +- `polars`:使用 [Polars 库](https://github.com/pola-rs/polars) 通过 DataFrame 进行极快的列式操作。有关更多详细信息,请参阅[DataFrame 章节](dataframes.html)。 +- `formats`:支持多种附加数据格式 - EML、ICS、INI、plist 和 VCF。 +- `gstat`:以 Nushell 结构化数据的形式返回 Git 仓库的状态信息。 +- `query`:支持查询 SQL、XML、JSON、HTML(通过选择器)和网页元数据。 +- `inc`:递增一个值或版本(例如,semver)。此插件既可作为最终用户插件,也可作为如何创建插件的简单开发者示例。 + +Nushell 还附带了几个插件,作为插件开发人员的示例或工具。其中包括 `nu_plugin_example`、`nu_plugin_custom_values` 和 `nu_plugin_stress_internals`。 + +核心插件通常与 Nushell 版本一起分发,并且应该已经安装在与 Nushell 可执行文件相同的目录中。如果你的系统上是这种情况,核心插件应该使用正确的 `nu-plugin` 协议版本。如果你的包管理系统分别安装它们,请确保在更新 Nushell 本身时也更新核心插件。 + +::: tip 使用 Cargo 安装 +例如,当使用 `cargo install nu --locked` 直接从 crates.io 安装或升级 Nushell 时,也可以使用 `cargo install nu_plugin_ --locked` 安装或更新该版本的相应核心插件。 + +要安装所有默认(非开发人员)插件,请在 Nushell 中运行: + +```nu +[ nu_plugin_inc + nu_plugin_polars + nu_plugin_gstat + nu_plugin_formats + nu_plugin_query +] | each { cargo install $in --locked } | ignore +``` + +::: + +### 第三方插件 + +你可以在 crates.io、在线 Git 仓库、[`awesome-nu`](https://github.com/nushell/awesome-nu/blob/main/plugin_details.md) 和其他来源找到第三方插件。与你在系统上运行的任何第三方代码一样,请确保你信任其来源。 + +要在你的系统上安装第三方插件,你首先需要确保该插件使用与你的系统相同的 Nu 版本: + +- 使用 `version` 命令确认你的 Nushell 版本 +- 通过检查其 `Cargo.toml` 文件来确认插件所需的版本 + +要按名称从 crates.io 安装插件,请运行: ```nu -plugin add --encoding=capnp ./my_plugins/my-cool-plugin +cargo install nu_plugin_ --locked ``` -Windows: +从仓库(例如 GitHub)安装时,请在克隆的仓库内运行以下命令: ```nu -plugin add --encoding=capnp .\my_plugins\my-cool-plugin.exe +cargo install --path . --locked ``` -当 [`plugin add`](/commands/docs/plugin_add.md) 被调用时: +这将创建一个可用于添加插件的二进制文件。 + +::: tip Cargo 安装位置 +默认情况下,使用 `cargo install` 安装的二进制文件位于你的主目录下的 `.cargo/bin` 中。 +但是,这可能会根据你的系统配置而改变。 +::: -1. Nu 启动该插件并通过 stdin 向其发送 "签名" 信息; -2. 插件通过 stdout 响应,包含其签名(名称、描述、参数、标志等)的消息; -3. Nu 将插件的签名保存在`$nu.plugin-path`位置的文件中,因此在注册之后的多次启动中都是有效的; +## 注册插件 -一旦注册,该插件就可以作为你的命令集的一部分被使用: +要将插件添加到插件注册表文件,请调用 [`plugin add`](/commands/docs/plugin_add.md) 命令来告诉 Nu 在哪里可以找到它。 + +::: tip 提示 +插件文件名必须以 `nu_plugin_` 开头,Nu 使用此文件名前缀来识别插件。 +::: + +- Linux 和 macOS: + + ```nu + plugin add ./my_plugins/nu_plugin_cool + ``` + +- Windows: + + ```nu + plugin add .\my_plugins\nu_plugin_cool.exe + ``` + +当 [`plugin add`](/commands/docs/plugin_add.md) 被调用时,Nu: + +- 运行插件二进制文件 +- 通过[插件协议](/zh-CN/contributor-book/plugin_protocol_reference.md)进行通信,以确保兼容性并获取其支持的所有命令的列表 +- 然后将此插件信息保存到插件注册表文件(`$nu.plugin-path`)中,该文件充当缓存 + +### 导入插件 + +添加到注册表后,下次启动 `nu` 时,插件将被导入并在该会话中可用。 + +你也可以通过调用 `plugin use` 在当前会话中立即导入(或重新加载)插件。在这种情况下,使用插件的名称(而不是文件名),不带 `nu_plugin` 前缀: ```nu -help commands | where is_plugin == true +plugin use cool ``` -## 示例 +没有必要在你的配置文件中添加 `plugin use` 语句。所有先前注册的插件在启动时都会自动加载。 -Nu 的主版本中包含了一些插件的例子,这些例子对学习插件协议的工作方式很有帮助: +::: tip 提示 +`plugin use` 是一个解析器关键字,因此在评估脚本时,它将首先被评估。这意味着虽然你可以在 REPL 的不同行上执行 `plugin add` 然后 `plugin use`,但你不能在单个脚本中执行此操作。如果你需要在没有准备缓存文件的情况下使用特定插件或一组插件运行 `nu`,你可以将 `--plugins` 选项传递给 `nu`,并附带一个插件可执行文件列表: + +```nu +nu --plugins '[./my_plugins/nu_plugin_cool]' +``` + +::: + +### 插件搜索路径 + +Nushell 包括两个可用于设置插件搜索路径的 `list` 变量。这仅适用于使用 `plugin add` 注册插件时,但如果你经常添加和删除插件,这可能是一个很好的快捷方式。 + +- `const NU_PLUGIN_DIRS`:一个常量,设置后立即生效。但是,作为一个常量,只有某些命令可以与它一起使用。例如,可以像[上面的快速入门](#core-plugin-quickstart)中看到的那样更新它。 +- `$env.NU_PLUGIN_DIRS`:一个环境变量,是可变的,可以接受任何更新其列表的命令。但是,对其的更改直到解析*下一个*表达式时才会生效。 + +### 更新插件 + +更新插件时,重要的是再次运行 `plugin add`,就像上面一样,从插件加载新的签名,并允许 Nu 将它们重写到插件文件(`$nu.plugin-path`)。然后你可以 `plugin use` 以在当前会话中获取更新的签名。 + +## 管理插件 + +已安装的插件使用 [`plugin list`](/commands/docs/plugin_list.md) 显示: + +```nu +plugin list +# => +╭───┬─────────┬────────────┬─────────┬───────────────────────┬───────┬───────────────────────────────╮ +│ # │ name │ is_running │ pid │ filename │ shell │ commands │ +├───┼─────────┼────────────┼─────────┼───────────────────────┼───────┼───────────────────────────────┤ +│ 0 │ gstat │ true │ 1389890 │ .../nu_plugin_gstat │ │ ╭───┬───────╮ │ +│ │ │ │ │ │ │ │ 0 │ gstat │ │ +│ │ │ │ │ │ │ ╰───┴───────╯ │ +│ 1 │ inc │ false │ │ .../nu_plugin_inc │ │ ╭───┬─────╮ │ +│ │ │ │ │ │ │ │ 0 │ inc │ │ +│ │ │ │ │ │ │ ╰───┴─────╯ │ +│ 2 │ example │ false │ │ .../nu_plugin_example │ │ ╭───┬───────────────────────╮ │ +│ │ │ │ │ │ │ │ 0 │ nu-example-1 │ │ +│ │ │ │ │ │ │ │ 1 │ nu-example-2 │ │ +│ │ │ │ │ │ │ │ 2 │ nu-example-3 │ │ +│ │ │ │ │ │ │ │ 3 │ nu-example-config │ │ +│ │ │ │ │ │ │ │ 4 │ nu-example-disable-gc │ │ +│ │ │ │ │ │ │ ╰───┴───────────────────────╯ │ +╰───┴─────────┴────────────┴─────────┴───────────────────────┴───────┴───────────────────────────────╯ +``` + +已安装插件的所有命令在当前作用域中都可用: + +```nu +scope commands | where type == "plugin" +``` + +### 插件生命周期 + +插件在使用时保持运行,并且在一段时间不活动后默认会自动停止。此行为由[插件垃圾回收器](#plugin-garbage-collector)管理。要手动停止插件,请使用其名称调用 `plugin stop`: + +例如,从相应的插件运行 `gstat` 命令,然后检查其 `is_running` 状态: + +```nu +gstat +# => gstat output +plugin list | where name == gstat | select name is_running +# => +╭───┬───────┬────────────╮ +│ # │ name │ is_running │ +├───┼───────┼────────────┤ +│ 0 │ gstat │ true │ +╰───┴───────┴────────────╯ +``` + +现在手动停止插件,我们可以看到它不再运行: + +```nu +plugin stop gstat +plugin list | where name == gstat | select name is_running +# => +╭───┬───────┬────────────╮ +│ # │ name │ is_running │ +├───┼───────┼────────────┤ +│ 0 │ gstat │ false │ +╰───┴───────┴────────────╯ +``` + +### 插件垃圾回收器 + +如上所述,Nu 带有一个插件垃圾回收器,它会在一段时间(默认为 10 秒)不活动后自动停止未使用的插件。此行为是完全可配置的: + +```nu +$env.config.plugin_gc = { + # 未另行指定的插件的设置: + default: { + enabled: true # 设置为 false 以永不自动停止插件 + stop_after: 10sec # 插件不活动后等待多长时间才停止它 + } + # 特定插件的设置,按插件名称 + # (即你在 `plugin list` 中看到的名称): + plugins: { + gstat: { + stop_after: 1min + } + inc: { + stop_after: 0sec # 尽快停止 + } + example: { + enabled: false # 永不自动停止 + } + } +} +``` + +有关何时认为插件处于活动状态的信息,请参阅[贡献者手册中的相关部分](/zh-CN/contributor-book/plugins.html#plugin-garbage-collection)。 + +## 移除插件 + +要移除插件,请调用 `plugin rm `。请注意,这是插件名称,而不是文件名。例如,如果你之前添加了插件 `~/.cargo/bin/nu_plugin_gstat`,其名称将是 `gstat`。要移除它: + +```nu +plugin rm gstat +``` + +你可以通过运行 `plugin list` 来确认插件的名称。 + +运行 `plugin rm` 会从注册表中删除插件,以便下次启动 Nushell 时不会加载它。但是,由插件创建的任何命令在当前 Nushell 会话结束之前仍保留在作用域中。 + +## 插件开发者 + +Nu 插件是可执行文件;Nu 在需要时启动它们,并通过 [stdin 和 stdout](https://en.wikipedia.org/wiki/Standard_streams) 或[本地套接字](https://en.wikipedia.org/wiki/Inter-process_communication)与它们通信。Nu 插件可以使用 [JSON](https://www.json.org/) 或 [MessagePack](https://msgpack.org/) 作为其通信编码。 + +### 示例 + +Nu 的主仓库包含一些插件的例子,这些例子对学习插件协议的工作方式很有帮助: - [Rust](https://github.com/nushell/nushell/tree/main/crates/nu_plugin_example) - [Python](https://github.com/nushell/nushell/blob/main/crates/nu_plugin_python) -## 调试 +### 调试 调试插件的最简单方法是打印到 stderr;插件的标准错误流会通过 Nu 重定向并显示给用户。 -## 帮助 +#### 跟踪 + +Nu 插件协议消息流可以使用 [trace_nu_plugin](https://crates.io/crates/trace_nu_plugin/) 进行捕获以用于诊断目的。 + +::: warning +只要插件与跟踪包装器一起安装,跟踪输出就会累积。可能会产生大文件。完成跟踪后,请务必使用 `plugin rm` 删除插件,并在没有跟踪包装器的情况下重新安装。\*\* +::: + +### 开发者帮助 Nu 的插件文档尚在撰写中,如果你对某件事情不确定 [Nu Discord](https://discord.gg/NtAbbGn)上的 #plugins 频道是一个提问的好地方! + +### 更多细节 + +[贡献者手册中的插件章节](/zh-CN/contributor-book/plugins.md)从软件开发人员的角度提供了有关插件工作原理的更多细节。 diff --git a/zh-CN/book/programming_in_nu.md b/zh-CN/book/programming_in_nu.md new file mode 100644 index 00000000000..029f9a3bff8 --- /dev/null +++ b/zh-CN/book/programming_in_nu.md @@ -0,0 +1,29 @@ +# Nu 编程 + +本章更详细地介绍了作为编程语言的 Nushell。 +每个主要的语言特性都有自己的章节。 + +就像大多数编程语言允许你定义函数一样,Nushell 使用[自定义命令](custom_commands.md)来实现这个目的。 + +你可能习惯于使用其他 shell 的[别名](aliases.md)。 +Nushell 的别名工作方式类似,是编程语言的一部分,而不仅仅是一个 shell 功能。 + +常见的操作,如加法或正则表达式搜索,可以使用[运算符](operators.md)来完成。 +并非所有操作都支持所有数据类型,当出现不匹配时,Nushell 会确保让你知道。 + +你可以将中间结果存储到[变量](variables.md)中。 +变量可以是不可变的、可变的或解析时常量。 + +最后三个部分旨在组织你的代码: + +[脚本](scripts.md)是最简单的代码组织形式:你只需将代码放入一个文件并加载它。 +但是,你也可以使用“特殊”的 `main` 命令将脚本作为带有命令行签名的独立程序运行。 + +通过[模块](modules.md),就像在许多其他编程语言中一样,可以从更小的部分组合你的代码。 +模块让你定义一个公共接口与私有命令,你可以从中导入自定义命令、别名和环境变量。 + +[覆层(Overlays)](overlays.md)建立在模块之上。 +通过定义一个覆层(overlay),你将模块的定义引入其自己的可交换“层”,该层应用于其他覆层之上。 +这使得可以实现诸如激活虚拟环境或用自定义变体覆盖默认命令集等功能。 + +如果你想证明你的可重用代码完美无缺,标准库还有一个[测试框架](testing.md)。 diff --git a/zh-CN/book/quick_tour.md b/zh-CN/book/quick_tour.md new file mode 100644 index 00000000000..5fd9de1e918 --- /dev/null +++ b/zh-CN/book/quick_tour.md @@ -0,0 +1,238 @@ +# 快速入门 + +[[toc]] + +## Nushell 命令输出 _数据_ + +了解 Nu 功能的最简单方法就是看一些例子,让我们开始吧。 + +当你运行像 [`ls`](/commands/docs/ls.md) 这样的命令时,你会注意到的第一件事是,返回的不是一个文本块,而是一个结构化的表格。 + +```nu:no-line-numbers +ls +# => ╭────┬─────────────────────┬──────┬───────────┬──────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├────┼─────────────────────┼──────┼───────────┼──────────────┤ +# => │ 0 │ CITATION.cff │ file │ 812 B │ 2 months ago │ +# => │ 1 │ CODE_OF_CONDUCT.md │ file │ 3.4 KiB │ 9 months ago │ +# => │ 2 │ CONTRIBUTING.md │ file │ 11.0 KiB │ 5 months ago │ +# => │ 3 │ Cargo.lock │ file │ 194.9 KiB │ 15 hours ago │ +# => │ 4 │ Cargo.toml │ file │ 9.2 KiB │ 15 hours ago │ +# => │ 5 │ Cross.toml │ file │ 666 B │ 6 months ago │ +# => │ 6 │ LICENSE │ file │ 1.1 KiB │ 9 months ago │ +# => │ 7 │ README.md │ file │ 12.0 KiB │ 15 hours ago │ +# => ... +``` + +这个表格不仅仅是漂亮地格式化了输出。就像电子表格一样,它允许我们*交互式地*处理数据。 + +## 对数据进行操作 + +接下来,让我们按每个文件的大小对这个表进行排序。为此,我们将获取 [`ls`](/commands/docs/ls.md) 的输出,并将其输入到一个可以根据列中的*值*对表进行排序的命令中。 + +```nu:no-line-numbers +ls | sort-by size | reverse +# => ╭───┬─────────────────┬──────┬───────────┬──────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├───┼─────────────────┼──────┼───────────┼──────────────┤ +# => │ 0 │ Cargo.lock │ file │ 194.9 KiB │ 15 hours ago │ +# => │ 1 │ toolkit.nu │ file │ 20.0 KiB │ 15 hours ago │ +# => │ 2 │ README.md │ file │ 12.0 KiB │ 15 hours ago │ +# => │ 3 │ CONTRIBUTING.md │ file │ 11.0 KiB │ 5 months ago │ +# => │ 4 │ ... │ ... │ ... │ ... │ +# => │ 5 │ LICENSE │ file │ 1.1 KiB │ 9 months ago │ +# => │ 6 │ CITATION.cff │ file │ 812 B │ 2 months ago │ +# => │ 7 │ Cross.toml │ file │ 666 B │ 6 months ago │ +# => │ 8 │ typos.toml │ file │ 513 B │ 2 months ago │ +# => ╰───┴─────────────────┴──────┴───────────┴──────────────╯ +``` + +请注意,我们没有向 [`ls`](/commands/docs/ls.md) 传递命令行参数或开关。相反,我们使用 Nushell 的内置命令 [`sort-by`](/commands/docs/sort-by.md) 来对 `ls` 命令的*输出*进行排序。然后,为了让最大的文件显示在最上面,我们对 `sort-by` 的*输出*使用了 [`reverse`](/commands/docs/reverse.md)。 + +::: tip 酷! +如果你仔细比较排序顺序,你可能会发现数据不是按字母顺序排序的,甚至不是按*数值*排序的。相反,由于 `size` 列是 [`filesize` 类型](./types_of_data.md#file-sizes),Nushell 知道 `1.1 KiB` (kibibytes) 比 `812 B` (bytes) 大。 +::: + +# 使用 `where` 命令查找数据 + +Nu 提供了许多可以对前一个命令的结构化输出进行操作的命令。这些在 Nushell 中通常被归类为“过滤器”。 + +例如,我们可以使用 [`where`](/commands/docs/where.md) 来过滤表格的内容,使其只显示大于 10 KB 的文件: + +```nu +ls | where size > 10kb +# => ╭───┬─────────────────┬──────┬───────────┬───────────────╮ +# => │ # │ name │ type │ size │ modified │ +# => ├───┼─────────────────┼──────┼───────────┼───────────────┤ +# => │ 0 │ CONTRIBUTING.md │ file │ 11.0 KiB │ 5 months ago │ +# => │ 1 │ Cargo.lock │ file │ 194.6 KiB │ 2 minutes ago │ +# => │ 2 │ README.md │ file │ 12.0 KiB │ 16 hours ago │ +# => │ 3 │ toolkit.nu │ file │ 20.0 KiB │ 16 hours ago │ +# => ╰───┴─────────────────┴──────┴───────────┴───────────────╯ +``` + +## 不仅仅是目录 + +当然,这并不仅限于 `ls` 命令。Nushell 遵循 Unix 的哲学,即每个命令都只做好一件事,你通常可以期望一个命令的输出成为另一个命令的输入。这使我们能够以许多不同的组合来混合和匹配命令。 + +让我们看看另一个命令: + +```nu:no-line-numbers +ps +# => ╭───┬──────┬──────┬───────────────┬──────────┬──────┬───────────┬─────────╮ +# => │ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ +# => ├───┼──────┼──────┼───────────────┼──────────┼──────┼───────────┼─────────┤ +# => │ 0 │ 1 │ 0 │ init(void) │ Sleeping │ 0.00 │ 1.2 MiB │ 2.2 MiB │ +# => │ 1 │ 8 │ 1 │ init │ Sleeping │ 0.00 │ 124.0 KiB │ 2.3 MiB │ +# => │ 2 │ 6565 │ 1 │ SessionLeader │ Sleeping │ 0.00 │ 108.0 KiB │ 2.2 MiB │ +# => │ 3 │ 6566 │ 6565 │ Relay(6567) │ Sleeping │ 0.00 │ 116.0 KiB │ 2.2 MiB │ +# => │ 4 │ 6567 │ 6566 │ nu │ Running │ 0.00 │ 28.4 MiB │ 1.1 GiB │ +# => ╰───┴──────┴──────┴───────────────┴──────────┴──────┴───────────┴─────────╯ +``` + +你可能熟悉 Linux/Unix 的 `ps` 命令。它提供了系统中所有当前正在运行的进程及其当前状态的列表。与 `ls` 一样,Nushell 提供了一个跨平台的内置 [`ps` 命令](/commands/docs/ps.md),它以结构化数据的形式返回结果。 + +::: note +传统的 Unix `ps` 默认只显示当前进程及其父进程。Nushell 的实现默认显示系统上的所有进程。 + +通常情况下,在 Nushell 中运行 `ps` 会使用其 **_内部的_** 、跨平台的命令。但是,在 Unix/Linux 平台上,仍然可以通过在命令前加上*脱字符号*来运行 **_外部的_** 、依赖于系统的版本。例如: + +```nu +^ps aux # 运行 Unix ps 命令,以面向用户的形式显示所有进程 +``` + +更多细节请参见[运行外部系统命令](./running_externals.md)。 +::: + +如果我们只想显示正在活跃运行的进程怎么办?与上面的 `ls` 一样,我们也可以处理 `ps` *输出*的表格: + +```nu +ps | where status == Running +# => ╭───┬──────┬──────┬──────┬─────────┬──────┬──────────┬─────────╮ +# => │ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ +# => ├───┼──────┼──────┼──────┼─────────┼──────┼──────────┼─────────┤ +# => │ 0 │ 6585 │ 6584 │ nu │ Running │ 0.00 │ 31.9 MiB │ 1.2 GiB │ +# => ╰───┴──────┴──────┴──────┴─────────┴──────┴──────────┴─────────╯ +``` + +::: tip +还记得上面 `ls` 命令输出的 `size` 列是 `filesize` 类型吗?在这里,`status` 实际上只是一个字符串,你可以对它使用所有常规的字符串操作和命令,包括(如上所示)`==` 比较。 + +你可以使用以下命令检查表格列的类型: + +```nu +ps | describe +# => table (stream) +``` + +[`describe` 命令](/commands/docs/describe.md)可以用来显示任何命令或表达式的输出类型。 + +::: + +## 管道中的命令参数 + +有时,命令接受的是*参数*而不是管道*输入*。对于这种情况,Nushell 提供了 [`$in` 变量](./pipelines.md#pipeline-input-and-the-special-in-variable),让你可以在变量形式中使用前一个命令的输出。例如: + +```nu:line-numbers +ls +| sort-by size +| reverse +| first +| get name +| cp $in ~ +``` + +::: tip Nushell 设计说明 +只要有可能,Nushell 命令都会被设计为对管道*输入*进行操作。然而,有些命令,比如本例中的 `cp`,有两个(或更多)具有不同含义的参数。在这种情况下,`cp` 需要同时知道要*复制*的路径和*目标*路径。因此,这个命令使用两个*位置参数*会更符合人体工程学。 +::: + +::: tip +为了可读性,Nushell 命令可以跨越多行。上面的命令与下面这个相同: + +```nu +ls | sort-by size | reverse | first | get name | cp $in ~ +``` + +另请参阅:[多行编辑](./line_editor.md#multi-line-editing) +::: + +前三行与我们在上面第二个例子中使用的命令相同,所以让我们来看看后三行: + +4. [`first` 命令](/commands/docs/first.md)只是从表格中返回第一个值。在这种情况下,这意味着大小最大的文件。如果使用上面第二个例子中的目录列表,那就是 `Cargo.lock` 文件。这个“文件”是表格中的一个 [`record`](./types_of_data.md#records)(记录),它仍然包含 `name`、`type`、`size` 和 `modified` 列/字段。 +5. `get name` 从前一个命令返回 `name` 字段的*值*,即 `"Cargo.lock"`(一个字符串)。这也是一个 [`cell-path`](./types_of_data.md#cell-paths)(单元格路径)的简单例子,可用于导航和隔离结构化数据。 +6. 最后一行使用 `$in` 变量来引用第 5 行的输出。结果是一个命令,意思是*“将 'Cargo.lock' 复制到主目录”* + +::: tip +[`get`](/commands/docs/get.md) 和它的对应命令 [`select`](/commands/docs/select.md) 是 Nushell 中最常用的两个过滤器,但乍一看可能不容易发现它们之间的区别。当你准备好更广泛地使用它们时,请参阅[使用 `get` 和 `select`](./navigating_structured_data.md#using-get-and-select)指南。 +::: + +## 获取帮助 + +Nushell 提供了一个广泛的、内置的帮助系统。例如 + +```nu +# help +help ls +# 或者 +ls --help +# 也可以 +help operators +help escapes +``` + +::: tip 酷! +按 F1 键访问帮助[菜单](./line_editor.md#menus)。在这里搜索 `ps` 命令,但*先不要按 Enter 键*! + +相反,按 向下箭头 键,你会发现你正在滚动浏览示例部分。高亮一个示例,*然后*按 Enter 键,该示例将被输入到命令行中,准备运行! + +这是探索和学习大量 Nushell 命令的好方法。 +::: + +帮助系统还有一个“搜索”功能: + +```nu +help --find filesize +# 或 +help -f filesize +``` + +现在你可能不会感到惊讶,帮助系统本身就是基于结构化数据的!请注意,`help -f filesize` 的输出是一个表格。 + +每个命令的帮助都作为一条记录存储,包含: + +- 名称 +- 类别 +- 类型(内置、插件、自定义) +- 接受的参数 +- 显示其可接受和输出的数据类型的签名 +- 等等 + +你可以使用以下命令将*所有*命令(外部命令除外)看作一个大表格: + +```nu +help commands +``` + +::: tip +请注意,上面输出的 `params` 和 `input_output` 列是*嵌套*表。Nushell 允许[任意嵌套的数据结构](./navigating_structured_data.md#background)。 +::: + +## 开始 `explore`(浏览) 这里 + +`help commands` 的输出很长。你可以把它发送到像 `less` 或 `bat` 这样的分页器中,但 Nushell 包含一个内置的 `explore` 命令,它不仅可以让你滚动,还可以深入到嵌套数据中。试试: + +```nu +help commands | explore +``` + +然后按 Enter 键访问数据本身。使用箭头键滚动到 `cp` 命令,再到 `params` 列。再次按 Enter 键可以深入查看 `cp` 命令可用的完整参数列表。 + +::: note +按一次 Esc 键从滚动模式返回到视图;再按一次则返回到上一个视图(如果已在顶层视图,则退出)。 +::: + +::: tip +当然,你可以在 Nushell 中对 _任何_ 结构化数据使用 `explore` 命令。这可能包括来自 Web API 的 JSON 数据、电子表格或 CSV 文件、YAML,或任何可以在 Nushell 中表示为结构化数据的东西。 + +试试 `$env.config | explore` 玩玩吧! +::: diff --git a/zh-CN/book/regular_expressions.md b/zh-CN/book/regular_expressions.md index ecf186eef49..5678766b564 100644 --- a/zh-CN/book/regular_expressions.md +++ b/zh-CN/book/regular_expressions.md @@ -1,3 +1,3 @@ # 正则表达式 -Nushell 命令中的正则表达式是由 `rust-lang/regex` 包处理的。如果你想了解更多,请查看 crate 的文档:"[Regex](https://github.com/rust-lang/regex)"。 +Nushell 命令中的正则表达式是由 `rust-lang/regex` 包处理的。如果你想了解更多,请查看 crate 的文档:"[regex](https://github.com/rust-lang/regex)"。 diff --git a/zh-CN/book/running_externals.md b/zh-CN/book/running_externals.md new file mode 100644 index 00000000000..20b3171abcd --- /dev/null +++ b/zh-CN/book/running_externals.md @@ -0,0 +1,25 @@ +# 运行系统(外部)命令 + +Nu提供了一组可以在不同操作系统上使用的命令("内部"命令),这种一致性在创建跨平台代码时很有帮助。但有时,你可能想运行一个与Nu内部命令同名的外部命令。例如,要运行外部的[`ls`](/commands/docs/ls.md)或[`date`](/commands/docs/date.md)命令,可以在前面加上脱字符(^)。使用脱字符前缀会调用用户`PATH`中找到的外部命令(例如`/bin/ls`),而不是Nu内部的[`ls`](/commands/docs/ls.md)命令。 + +Nu内部命令: + +```nu +ls +``` + +外部命令(通常是`/usr/bin/ls`): + +```nu +^ls +``` + +::: note +在Windows上,`ls`默认是PowerShell的 _别名_,因此`^ls`不会找到匹配的系统 _命令_。 +::: + +## Windows额外说明 + +在Windows上运行外部命令时, +Nushell会将一些`CMD.EXE`内部命令转发给cmd,而不是尝试运行外部命令。 +[从CMD.EXE迁移](coming_from_cmd.md)包含了这些命令的列表并更详细地描述了这种行为。 diff --git a/zh-CN/book/scripts.md b/zh-CN/book/scripts.md index 67d1190cba7..5685f5094b7 100644 --- a/zh-CN/book/scripts.md +++ b/zh-CN/book/scripts.md @@ -17,7 +17,7 @@ source myscript.nu ```nu # myscript.nu def greet [name] { - echo "hello" $name + ["hello" $name] } greet "world" @@ -31,7 +31,7 @@ greet "world" greet "world" def greet [name] { - echo "hello" $name + ["hello" $name] } ``` @@ -52,15 +52,14 @@ a b; c | d ``` -当这个脚本运行时,Nushell 将首先运行`a`命令至完成并查看其结果。接下来,Nushell 将按照["组"部分](types_of_data.html#组)中的规则运行`b; c | d`。 +当这个脚本运行时,Nushell 将首先运行`a`命令至完成并查看其结果。接下来,Nushell 将按照[“分号”部分](pipelines.html#semicolons)中的规则运行`b; c | d`。 ## 参数化脚本 -脚本文件可以选择性地包含一个特殊的 "main" 命令。`main`将在任何其他 Nu 代码之后运行,主要用于向脚本添加参数。你可以在脚本名称后面传递参数(`nu