介绍
你好,欢迎来到 Nushell 项目。这个项目的目标是继承 Unix Shell 中用管道把简单的命令连接在一起的理念,并将其带到更具现代风格的开发中。因此,Nushell 不是一个纯粹的 shell 或编程语言,而是通过将一种丰富的编程语言和功能齐全的 shell 结合到一个软件包中,实现了二者的连接。
Nu 汲取了很多常见领域的灵感:传统 Shell 比如 Bash、基于对象的 Shell 比如 PowerShell、逐步类型化的语言比如 TypeScript、函数式编程、系统编程,等等。但是,Nu 并不试图成为万金油,而是把精力集中在做好这几件事上:
- 作为一个具有现代感的灵活的跨平台 Shell;
- 作为一种现代的编程语言,解决与数据有关的问题;
- 给予清晰的错误信息和干净的 IDE 支持。
了解 Nu 能做什么的最简单的方法是从一些例子开始,所以让我们深入了解一下。
当你运行 ls
这样的命令时,你会注意到的第一件事是,你得到的不是一个文本块,而是一个结构化的表格:
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 KiB │ 3 days ago │
# => │ 3 │ Gemfile.lock │ file │ 6.9 KiB │ 3 days ago │
# => │ 4 │ LICENSE │ file │ 1.1 KiB │ 3 days ago │
# => │ 5 │ README.md │ file │ 213 B │ 3 days ago │
# => ...
该表不仅仅是以不同的方式显示目录,就像电子表格中的表一样,它还允许我们以更加互动的方式来处理数据。
我们要做的第一件事是按大小对我们的表进行排序。要做到这一点,我们将从 ls
中获取输出,并将其输入到一个可以根据列的内容对表进行排序的命令中:
ls | sort-by size | reverse
# => ╭────┬───────────────────────┬──────┬───────────┬─────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├────┼───────────────────────┼──────┼───────────┼─────────────┤
# => │ 0 │ Gemfile.lock │ file │ 6.9 KiB │ 3 days ago │
# => │ 1 │ SUMMARY.md │ file │ 3.7 KiB │ 3 days ago │
# => │ 2 │ Gemfile │ file │ 1.1 KiB │ 3 days ago │
# => │ 3 │ LICENSE │ file │ 1.1 KiB │ 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 千字节的文件。
ls | where size > 1kb
# => ╭───┬───────────────────┬──────┬─────────┬────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼───────────────────┼──────┼─────────┼────────────┤
# => │ 0 │ Gemfile │ file │ 1.1 KiB │ 3 days ago │
# => │ 1 │ Gemfile.lock │ file │ 6.9 KiB │ 3 days ago │
# => │ 2 │ LICENSE │ file │ 1.1 KiB │ 3 days ago │
# => │ 3 │ SUMMARY.md │ file │ 3.7 KiB │ 3 days ago │
# => ╰───┴───────────────────┴──────┴─────────┴────────────╯
就像在 Unix 哲学中一样,能够让命令相互对话给我们提供了在许多不同的组合中对命令进行混搭的方法。我们来看看一个不同的命令:
ps
# => ╭─────┬───────┬───────┬──────────────────────────────────────────────┬─────────┬───────┬──────────┬──────────╮
# => │ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │
# => ├─────┼───────┼───────┼──────────────────────────────────────────────┼─────────┼───────┼──────────┼──────────┤
# => │ 0 │ 87194 │ 1 │ mdworker_shared │ Sleep │ 0.00 │ 25.9 MB │ 418.0 GB │
# => │ 1 │ 87183 │ 2314 │ Arc Helper (Renderer) │ Sleep │ 0.00 │ 59.9 MB │ 1.6 TB │
# => │ 2 │ 87182 │ 2314 │ Arc Helper (Renderer) │ Sleep │ 0.23 │ 224.3 MB │ 1.6 TB │
# => │ 3 │ 87156 │ 87105 │ Code Helper (Plugin) │ Sleep │ 0.00 │ 56.0 MB │ 457.4 GB │
# => ...
如果你使用过 Linux,你可能对 ps
命令很熟悉。通过它,我们可以得到一个当前系统正在运行的所有进程的列表,它们的状态是什么,以及它们的名字是什么,我们还可以看到这些进程的 CPU 负载。
如果我们想显示那些正在活跃使用 CPU 的进程呢?就像我们之前对 ls
命令所做的那样,我们也可以利用 ps
命令返回给我们的表格来做到:
ps | where cpu > 5
# => ╭───┬───────┬───────┬─────────────────────────────────────────┬─────────┬───────┬──────────┬──────────╮
# => │ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │
# => ├───┼───────┼───────┼─────────────────────────────────────────┼─────────┼───────┼──────────┼──────────┤
# => │ 0 │ 86759 │ 86275 │ nu │ Running │ 6.27 │ 38.7 MB │ 419.7 GB │
# => │ 1 │ 89134 │ 1 │ com.apple.Virtualization.VirtualMachine │ Running │ 23.92 │ 1.5 GB │ 427.3 GB │
# => │ 2 │ 70414 │ 1 │ VTDecoderXPCService │ Sleep │ 19.04 │ 17.5 MB │ 419.7 GB │
# => │ 3 │ 2334 │ 1 │ TrackpadExtension │ Sleep │ 7.47 │ 25.3 MB │ 418.8 GB │
# => │ 4 │ 1205 │ 1 │ iTerm2 │ Sleep │ 11.92 │ 657.2 MB │ 421.7 GB │
# => ╰───┴───────┴───────┴─────────────────────────────────────────┴─────────┴───────┴──────────┴──────────╯
到目前为止,我们一直在使用 ls
和 ps
来列出文件和进程。Nu 还提供了其他可以创建有用信息表格的命令。接下来,让我们试一下 date
和 sys
。
运行 date now
输出关于当前日期和时间的信息:
date now
# => 2022-03-07 14:14:51.684619600 -08:00
为了将获得的日期以表格形式展示,我们可以把它输入到 date to-table
中:
date now | date to-table
# => ╭───┬──────┬───────┬─────┬──────┬────────┬────────┬──────────╮
# => │ # │ year │ month │ day │ hour │ minute │ second │ timezone │
# => ├───┼──────┼───────┼─────┼──────┼────────┼────────┼──────────┤
# => │ 0 │ 2022 │ 3 │ 7 │ 14 │ 45 │ 3 │ -08:00 │
# => ╰───┴──────┴───────┴─────┴──────┴────────┴────────┴──────────╯
运行 sys
可以得到 Nu 所运行的系统的信息:
sys
# => ╭───────┬───────────────────╮
# => │ host │ {record 6 fields} │
# => │ cpu │ [table 4 rows] │
# => │ disks │ [table 3 rows] │
# => │ mem │ {record 4 fields} │
# => │ temp │ [table 1 row] │
# => │ net │ [table 4 rows] │
# => ╰───────┴───────────────────╯
这与我们之前看到的表格有些不同。sys
命令输出了一个在单元格中包含结构化表格而非简单值的表格。要查看这些数据,我们需要获取(get
)待查看的列:
sys | get host
# => ╭────────────────┬────────────────────────╮
# => │ name │ Debian GNU/Linux │
# => │ os version │ 11 │
# => │ kernel version │ 5.10.92-v8+ │
# => │ hostname │ lifeless │
# => │ uptime │ 19day 21hr 34min 45sec │
# => │ sessions │ [table 1 row] │
# => ╰────────────────┴────────────────────────╯
get
命令让我们深入表的某一列内容中。在这里,我们要查看的是 host
列,它包含了 Nu 正在运行的主机的信息:操作系统名称、主机名、CPU,以及更多。让我们来获取系统上的用户名:
sys | get host.sessions.name
# => ╭───┬──────────╮
# => │ 0 │ sophiajt │
# => ╰───┴──────────╯
现在,系统中只有一个名为 sophiajt 的用户。你会注意到,我们可以传递一个列路径(host.sessions.name
部分),而不仅仅是简单的列名称。Nu 会接受列路径并输出表中相应的数据。
你可能已经注意到其他一些不同之处:我们没有一个数据表,而只有一个元素:即字符串 "sophiajt"
。Nu 既能处理数据表,也能处理字符串。字符串是使用 Nu 外部命令的一个重要部分。
让我们看看字符串在 Nu 外部命令里面是如何工作的。我们以之前的例子为例,运行外部的 echo
命令(^
告诉 Nu 不要使用内置的 echo
命令):
sys | get host.sessions.name | each { |elt| ^echo $elt }
# => sophiajt
敏锐的读者可能会发现这看起来和我们之前的非常相似!确实如此,但有一个重要的区别:我们用前面的值调用了 ^echo
。这允许我们把数据从 Nu 中传到外部命令 echo
(或者 Nu 之外的任何命令,比如 git
)。
获取帮助
任何 Nu 的内置命令的帮助文本都可以通过 help
命令来找到。要查看所有命令,请运行 help commands
。你也可以通过执行 help -f <topic>
来搜索一个主题:
help path
# => Explore and manipulate paths.
# =>
# => There are three ways to represent a path:
# =>
# => * As a path literal, e.g., '/home/viking/spam.txt'
# => * As a structured path: a table with 'parent', 'stem', and 'extension' (and
# => * 'prefix' on Windows) columns. This format is produced by the 'path parse'
# => subcommand.
# => * As an inner list of path parts, e.g., '[[ / home viking spam.txt ]]'.
# => Splitting into parts is done by the `path split` command.
# =>
# => All subcommands accept all three variants as an input. Furthermore, the 'path
# => join' subcommand can be used to join the structured path or path parts back into
# => the path literal.
# =>
# => Usage:
# => > path
# =>
# => Subcommands:
# => path basename - Get the final component of a path
# => path dirname - Get the parent directory of a path
# => path exists - Check whether a path exists
# => path expand - Try to expand a path to its absolute form
# => path join - Join a structured path or a list of path parts.
# => path parse - Convert a path into structured data.
# => path relative-to - Get a path as relative to another path.
# => path split - Split a path into parts by a separator.
# => path type - Get the type of the object a path refers to (e.g., file, dir, symlink)
# =>
# => Flags:
# => -h, --help
# => Display this help message