Project Path: .claude
Source Tree:
.claude
├── AGENTS.md
├── CLAUDE.nushell.md
├── docs
│ └── nushell
│ ├── nu_scripts
│ │ ├── README.md
│ │ ├── aliases
│ │ │ ├── README.md
│ │ │ ├── bat
│ │ │ │ └── bat-aliases.nu
│ │ │ ├── chezmoi
│ │ │ │ └── chezmoi-aliases.nu
│ │ │ ├── docker
│ │ │ │ ├── README.md
│ │ │ │ └── docker-aliases.nu
│ │ │ ├── exa
│ │ │ │ └── exa-aliases.nu
│ │ │ ├── eza
│ │ │ │ └── eza-aliases.nu
│ │ │ └── git
│ │ │ └── git-aliases.nu
│ │ ├── custom-completions
│ │ │ ├── as
│ │ │ │ └── as-completions.nu
│ │ │ ├── auto-generate
│ │ │ │ └── README.md
│ │ │ ├── aws
│ │ │ │ ├── aws-completions.nu
│ │ │ │ └── readme.md
│ │ │ └── bat
│ │ │ └── bat-completions.nu
│ │ ├── custom-menus
│ │ │ ├── README.md
│ │ │ ├── current_session_history_menu.nu
│ │ │ ├── extra
│ │ │ │ ├── commands.nu
│ │ │ │ ├── commands_with_description.nu
│ │ │ │ └── vars.nu
│ │ │ ├── fuzzy
│ │ │ │ ├── README.md
│ │ │ │ ├── directory.nu
│ │ │ │ ├── history.nu
│ │ │ │ └── modules.nu
│ │ │ └── zoxide-menu.nu
│ │ ├── example-config
│ │ │ ├── config.nu
│ │ │ └── init.nu
│ │ ├── modules
│ │ │ ├── argx
│ │ │ │ └── mod.nu
│ │ │ ├── aws
│ │ │ │ └── select-aws-profile.nu
│ │ │ ├── background_task
│ │ │ │ ├── README.md
│ │ │ │ └── task.nu
│ │ │ ├── capture-foreign-env
│ │ │ │ ├── README.md
│ │ │ │ └── mod.nu
│ │ │ └── clone-all
│ │ │ ├── README.md
│ │ │ └── clone-all.nu
│ │ ├── nu-hooks
│ │ │ └── nu-hooks
│ │ │ ├── direnv
│ │ │ │ ├── config.nu
│ │ │ │ └── direnv.nu
│ │ │ ├── dynamic-load
│ │ │ │ └── dynamic-load.nu
│ │ │ └── filesystem
│ │ │ └── autojump.nu
│ │ └── sourced
│ │ ├── api_wrappers
│ │ │ └── wolframalpha.nu
│ │ ├── cool-oneliners
│ │ │ ├── README.md
│ │ │ ├── assets
│ │ │ │ └── js_map.json
│ │ │ ├── cargo_search.nu
│ │ │ ├── cdpath-implementation.nu
│ │ │ ├── clear_screen_buf.nu
│ │ │ ├── dict.nu
│ │ │ ├── file_cat.nu
│ │ │ ├── file_convert_naming_case.nu
│ │ │ ├── filesize.nu
│ │ │ ├── find_in.nu
│ │ │ ├── git_gone.nu
│ │ │ ├── js_map_to_markdown.nu
│ │ │ ├── npm_update_versions.nu
│ │ │ ├── parse_aws_s3_ls.nu
│ │ │ ├── parse_fish_command_history.nu
│ │ │ ├── pwd-short.nu
│ │ │ ├── simple_http_request.nu
│ │ │ ├── top_commands.nu
│ │ │ ├── wt_color.nu
│ │ │ └── xml_search_schema.nu
│ │ ├── examples
│ │ │ ├── date_in_local_timezones.nu
│ │ │ └── netstat.nu
│ │ ├── fun
│ │ │ └── website_builder.nu
│ │ └── github
│ │ ├── branch-protections
│ │ │ ├── README.md
│ │ │ ├── branch-protections.nu
│ │ │ └── branch-protections.yml
│ │ └── merged-branches
│ │ ├── README.md
│ │ ├── merged-branches.nu
│ │ └── merged-branches.yml
│ ├── nushell.github.io
│ │ ├── book
│ │ │ ├── 3rdpartyprompts.md
│ │ │ ├── README.md
│ │ │ ├── advanced.md
│ │ │ ├── aliases.md
│ │ │ ├── background_jobs.md
│ │ │ ├── cheat_sheet.md
│ │ │ ├── coloring_and_theming.md
│ │ │ ├── coming_from_bash.md
│ │ │ ├── coming_from_cmd.md
│ │ │ ├── coming_to_nu.md
│ │ │ ├── configuration.md
│ │ │ ├── control_flow.md
│ │ │ ├── creating_errors.md
│ │ │ ├── custom_commands.md
│ │ │ ├── custom_completions.md
│ │ │ ├── dataframes.md
│ │ │ ├── default_shell.md
│ │ │ ├── design_notes.md
│ │ │ ├── directory_stack.md
│ │ │ ├── environment.md
│ │ │ ├── explore.md
│ │ │ ├── externs.md
│ │ │ ├── getting_started.md
│ │ │ ├── hooks.md
│ │ │ ├── how_nushell_code_gets_run.md
│ │ │ ├── installation.md
│ │ │ ├── line_editor.md
│ │ │ ├── loading_data.md
│ │ │ ├── metadata.md
│ │ │ ├── modules
│ │ │ │ ├── creating_modules.md
│ │ │ │ └── using_modules.md
│ │ │ ├── modules.md
│ │ │ ├── moving_around.md
│ │ │ ├── navigating_structured_data.md
│ │ │ ├── nu_as_a_shell.md
│ │ │ ├── nu_fundamentals.md
│ │ │ ├── nushell_map.md
│ │ │ ├── nushell_map_functional.md
│ │ │ ├── nushell_map_imperative.md
│ │ │ ├── nushell_operator_map.md
│ │ │ ├── operators.md
│ │ │ ├── overlays.md
│ │ │ ├── parallelism.md
│ │ │ ├── pipelines.md
│ │ │ ├── plugins.md
│ │ │ ├── programming_in_nu.md
│ │ │ ├── quick_tour.md
│ │ │ ├── regular_expressions.md
│ │ │ ├── running_externals.md
│ │ │ ├── scripts.md
│ │ │ ├── sorting.md
│ │ │ ├── special_variables.md
│ │ │ ├── standard_library.md
│ │ │ ├── stdout_stderr_exit_codes.md
│ │ │ ├── style_guide.md
│ │ │ ├── table_of_contents.md
│ │ │ ├── testing.md
│ │ │ ├── thinking_in_nu.md
│ │ │ ├── types_of_data.md
│ │ │ ├── variables.md
│ │ │ ├── working_with_lists.md
│ │ │ ├── working_with_records.md
│ │ │ ├── working_with_strings.md
│ │ │ └── working_with_tables.md
│ │ ├── commands
│ │ │ ├── README.md
│ │ │ ├── categories
│ │ │ │ ├── bits.md
│ │ │ │ ├── bytes.md
│ │ │ │ ├── chart.md
│ │ │ │ ├── conversions.md
│ │ │ │ ├── core.md
│ │ │ │ ├── database.md
│ │ │ │ ├── dataframe.md
│ │ │ │ ├── dataframe_or_lazyframe.md
│ │ │ │ ├── date.md
│ │ │ │ ├── debug.md
│ │ │ │ ├── default.md
│ │ │ │ ├── deprecated.md
│ │ │ │ ├── env.md
│ │ │ │ ├── experimental.md
│ │ │ │ ├── expression.md
│ │ │ │ ├── filesystem.md
│ │ │ │ ├── filters.md
│ │ │ │ ├── formats.md
│ │ │ │ ├── generators.md
│ │ │ │ ├── hash.md
│ │ │ │ ├── history.md
│ │ │ │ ├── lazyframe.md
│ │ │ │ ├── math.md
│ │ │ │ ├── misc.md
│ │ │ │ ├── network.md
│ │ │ │ ├── path.md
│ │ │ │ ├── platform.md
│ │ │ │ ├── plugin.md
│ │ │ │ ├── prompt.md
│ │ │ │ ├── random.md
│ │ │ │ ├── removed.md
│ │ │ │ ├── shells.md
│ │ │ │ ├── strings.md
│ │ │ │ ├── system.md
│ │ │ │ └── viewers.md
│ │ │ └── docs
│ │ │ ├── alias.md
│ │ │ ├── all.md
│ │ │ ├── ansi.md
│ │ │ ├── ansi_gradient.md
│ │ │ ├── ansi_link.md
│ │ │ ├── ansi_strip.md
│ │ │ ├── any.md
│ │ │ ├── append.md
│ │ │ ├── ast.md
│ │ │ ├── attr.md
│ │ │ ├── attr_category.md
│ │ │ ├── attr_deprecated.md
│ │ │ ├── attr_example.md
│ │ │ ├── attr_search-terms.md
│ │ │ ├── banner.md
│ │ │ ├── bits.md
│ │ │ ├── bits_and.md
│ │ │ ├── bits_not.md
│ │ │ ├── bits_or.md
│ │ │ ├── bits_rol.md
│ │ │ ├── bits_ror.md
│ │ │ ├── bits_shl.md
│ │ │ ├── bits_shr.md
│ │ │ ├── bits_xor.md
│ │ │ ├── break.md
│ │ │ ├── bytes.md
│ │ │ ├── bytes_add.md
│ │ │ ├── bytes_at.md
│ │ │ ├── bytes_build.md
│ │ │ ├── bytes_collect.md
│ │ │ ├── bytes_ends-with.md
│ │ │ ├── bytes_index-of.md
│ │ │ ├── bytes_length.md
│ │ │ ├── bytes_remove.md
│ │ │ ├── bytes_replace.md
│ │ │ ├── bytes_reverse.md
│ │ │ ├── bytes_split.md
│ │ │ ├── bytes_starts-with.md
│ │ │ ├── cal.md
│ │ │ ├── cd.md
│ │ │ ├── char.md
│ │ │ ├── chunk-by.md
│ │ │ ├── chunks.md
│ │ │ ├── clear.md
│ │ │ ├── collect.md
│ │ │ ├── columns.md
│ │ │ ├── commandline.md
│ │ │ ├── commandline_edit.md
│ │ │ ├── commandline_get-cursor.md
│ │ │ ├── commandline_set-cursor.md
│ │ │ ├── compact.md
│ │ │ ├── complete.md
│ │ │ ├── config.md
│ │ │ ├── config_env.md
│ │ │ ├── config_flatten.md
│ │ │ ├── config_nu.md
│ │ │ ├── config_reset.md
│ │ │ ├── config_use-colors.md
│ │ │ ├── const.md
│ │ │ ├── continue.md
│ │ │ ├── cp.md
│ │ │ ├── date.md
│ │ │ ├── date_format.md
│ │ │ ├── date_from-human.md
│ │ │ ├── date_humanize.md
│ │ │ ├── date_list-timezone.md
│ │ │ ├── date_now.md
│ │ │ ├── date_to-timezone.md
│ │ │ ├── debug.md
│ │ │ ├── debug_env.md
│ │ │ ├── debug_experimental-options.md
│ │ │ ├── debug_info.md
│ │ │ ├── debug_profile.md
│ │ │ ├── decode.md
│ │ │ ├── decode_base32.md
│ │ │ ├── decode_base32hex.md
│ │ │ ├── decode_base64.md
│ │ │ ├── decode_hex.md
│ │ │ ├── def.md
│ │ │ ├── default.md
│ │ │ ├── describe.md
│ │ │ ├── detect.md
│ │ │ ├── detect_columns.md
│ │ │ ├── do.md
│ │ │ ├── drop.md
│ │ │ ├── drop_column.md
│ │ │ ├── drop_nth.md
│ │ │ ├── du.md
│ │ │ ├── each.md
│ │ │ ├── each_while.md
│ │ │ ├── echo.md
│ │ │ ├── encode.md
│ │ │ ├── encode_base32.md
│ │ │ ├── encode_base32hex.md
│ │ │ ├── encode_base64.md
│ │ │ ├── encode_hex.md
│ │ │ ├── enumerate.md
│ │ │ ├── error.md
│ │ │ ├── error_make.md
│ │ │ ├── every.md
│ │ │ ├── exec.md
│ │ │ ├── exit.md
│ │ │ ├── explain.md
│ │ │ ├── explore.md
│ │ │ ├── export-env.md
│ │ │ ├── export.md
│ │ │ ├── export_alias.md
│ │ │ ├── export_const.md
│ │ │ ├── export_def.md
│ │ │ ├── export_extern.md
│ │ │ ├── export_module.md
│ │ │ ├── export_use.md
│ │ │ ├── extern.md
│ │ │ ├── fill.md
│ │ │ ├── filter.md
│ │ │ ├── find.md
│ │ │ ├── first.md
│ │ │ ├── flatten.md
│ │ │ ├── for.md
│ │ │ ├── format.md
│ │ │ ├── format_bits.md
│ │ │ ├── format_date.md
│ │ │ ├── format_duration.md
│ │ │ ├── format_filesize.md
│ │ │ ├── format_number.md
│ │ │ ├── format_pattern.md
│ │ │ ├── from.md
│ │ │ ├── from_csv.md
│ │ │ ├── from_eml.md
│ │ │ ├── from_ics.md
│ │ │ ├── from_ini.md
│ │ │ ├── from_json.md
│ │ │ ├── from_msgpack.md
│ │ │ ├── from_msgpackz.md
│ │ │ ├── from_nuon.md
│ │ │ ├── from_ods.md
│ │ │ ├── from_plist.md
│ │ │ ├── from_ssv.md
│ │ │ ├── from_toml.md
│ │ │ ├── from_tsv.md
│ │ │ ├── from_url.md
│ │ │ ├── from_vcf.md
│ │ │ ├── from_xlsx.md
│ │ │ ├── from_xml.md
│ │ │ ├── from_yaml.md
│ │ │ ├── from_yml.md
│ │ │ ├── generate.md
│ │ │ ├── get.md
│ │ │ ├── glob.md
│ │ │ ├── grid.md
│ │ │ ├── group-by.md
│ │ │ ├── gstat.md
│ │ │ ├── hash.md
│ │ │ ├── hash_md5.md
│ │ │ ├── hash_sha256.md
│ │ │ ├── headers.md
│ │ │ ├── help.md
│ │ │ ├── help_aliases.md
│ │ │ ├── help_commands.md
│ │ │ ├── help_escapes.md
│ │ │ ├── help_externs.md
│ │ │ ├── help_modules.md
│ │ │ ├── help_operators.md
│ │ │ ├── help_pipe-and-redirect.md
│ │ │ ├── hide-env.md
│ │ │ ├── hide.md
│ │ │ ├── histogram.md
│ │ │ ├── history.md
│ │ │ ├── history_import.md
│ │ │ ├── history_session.md
│ │ │ ├── http.md
│ │ │ ├── http_delete.md
│ │ │ ├── http_get.md
│ │ │ ├── http_head.md
│ │ │ ├── http_options.md
│ │ │ ├── http_patch.md
│ │ │ ├── http_post.md
│ │ │ ├── http_put.md
│ │ │ ├── if.md
│ │ │ ├── ignore.md
│ │ │ ├── inc.md
│ │ │ ├── input.md
│ │ │ ├── input_list.md
│ │ │ ├── input_listen.md
│ │ │ ├── insert.md
│ │ │ ├── inspect.md
│ │ │ ├── interleave.md
│ │ │ ├── into.md
│ │ │ ├── into_binary.md
│ │ │ ├── into_bool.md
│ │ │ ├── into_cell-path.md
│ │ │ ├── into_datetime.md
│ │ │ ├── into_duration.md
│ │ │ ├── into_filesize.md
│ │ │ ├── into_float.md
│ │ │ ├── into_glob.md
│ │ │ ├── into_int.md
│ │ │ ├── into_record.md
│ │ │ ├── into_sqlite.md
│ │ │ ├── into_string.md
│ │ │ ├── into_value.md
│ │ │ ├── is-admin.md
│ │ │ ├── is-empty.md
│ │ │ ├── is-not-empty.md
│ │ │ ├── is-terminal.md
│ │ │ ├── items.md
│ │ │ ├── job.md
│ │ │ ├── job_flush.md
│ │ │ ├── job_id.md
│ │ │ ├── job_kill.md
│ │ │ ├── job_list.md
│ │ │ ├── job_recv.md
│ │ │ ├── job_send.md
│ │ │ ├── job_spawn.md
│ │ │ ├── job_tag.md
│ │ │ ├── job_unfreeze.md
│ │ │ ├── join.md
│ │ │ ├── keybindings.md
│ │ │ ├── keybindings_default.md
│ │ │ ├── keybindings_list.md
│ │ │ ├── keybindings_listen.md
│ │ │ ├── kill.md
│ │ │ ├── last.md
│ │ │ ├── length.md
│ │ │ ├── let-env.md
│ │ │ ├── let.md
│ │ │ ├── lines.md
│ │ │ ├── load-env.md
│ │ │ ├── loop.md
│ │ │ ├── ls.md
│ │ │ ├── match.md
│ │ │ ├── math.md
│ │ │ ├── math_abs.md
│ │ │ ├── math_arccos.md
│ │ │ ├── math_arccosh.md
│ │ │ ├── math_arcsin.md
│ │ │ ├── math_arcsinh.md
│ │ │ ├── math_arctan.md
│ │ │ ├── math_arctanh.md
│ │ │ ├── math_avg.md
│ │ │ ├── math_ceil.md
│ │ │ ├── math_cos.md
│ │ │ ├── math_cosh.md
│ │ │ ├── math_exp.md
│ │ │ ├── math_floor.md
│ │ │ ├── math_ln.md
│ │ │ ├── math_log.md
│ │ │ ├── math_max.md
│ │ │ ├── math_median.md
│ │ │ ├── math_min.md
│ │ │ ├── math_mode.md
│ │ │ ├── math_product.md
│ │ │ ├── math_round.md
│ │ │ ├── math_sin.md
│ │ │ ├── math_sinh.md
│ │ │ ├── math_sqrt.md
│ │ │ ├── math_stddev.md
│ │ │ ├── math_sum.md
│ │ │ ├── math_tan.md
│ │ │ ├── math_tanh.md
│ │ │ ├── math_variance.md
│ │ │ ├── merge.md
│ │ │ ├── merge_deep.md
│ │ │ ├── metadata.md
│ │ │ ├── metadata_access.md
│ │ │ ├── metadata_set.md
│ │ │ ├── mkdir.md
│ │ │ ├── mktemp.md
│ │ │ ├── module.md
│ │ │ ├── move.md
│ │ │ ├── mut.md
│ │ │ ├── mv.md
│ │ │ ├── nu-check.md
│ │ │ ├── nu-highlight.md
│ │ │ ├── open.md
│ │ │ ├── overlay.md
│ │ │ ├── overlay_hide.md
│ │ │ ├── overlay_list.md
│ │ │ ├── overlay_new.md
│ │ │ ├── overlay_use.md
│ │ │ ├── panic.md
│ │ │ ├── par-each.md
│ │ │ ├── parse.md
│ │ │ ├── path.md
│ │ │ ├── path_basename.md
│ │ │ ├── path_dirname.md
│ │ │ ├── path_exists.md
│ │ │ ├── path_expand.md
│ │ │ ├── path_join.md
│ │ │ ├── path_parse.md
│ │ │ ├── path_relative-to.md
│ │ │ ├── path_self.md
│ │ │ ├── path_split.md
│ │ │ ├── path_type.md
│ │ │ ├── plugin.md
│ │ │ ├── plugin_add.md
│ │ │ ├── plugin_list.md
│ │ │ ├── plugin_rm.md
│ │ │ ├── plugin_stop.md
│ │ │ ├── plugin_use.md
│ │ │ ├── polars.md
│ │ │ ├── polars_agg-groups.md
│ │ │ ├── polars_agg.md
│ │ │ ├── polars_all-false.md
│ │ │ ├── polars_all-true.md
│ │ │ ├── polars_append.md
│ │ │ ├── polars_arg-max.md
│ │ │ ├── polars_arg-min.md
│ │ │ ├── polars_arg-sort.md
│ │ │ ├── polars_arg-true.md
│ │ │ ├── polars_arg-unique.md
│ │ │ ├── polars_arg-where.md
│ │ │ ├── polars_as-date.md
│ │ │ ├── polars_as-datetime.md
│ │ │ ├── polars_as.md
│ │ │ ├── polars_cache.md
│ │ │ ├── polars_cast.md
│ │ │ ├── polars_col.md
│ │ │ ├── polars_collect.md
│ │ │ ├── polars_columns.md
│ │ │ ├── polars_concat-str.md
│ │ │ ├── polars_concat.md
│ │ │ ├── polars_contains.md
│ │ │ ├── polars_convert-time-zone.md
│ │ │ ├── polars_count-null.md
│ │ │ ├── polars_count.md
│ │ │ ├── polars_cumulative.md
│ │ │ ├── polars_cut.md
│ │ │ ├── polars_datepart.md
│ │ │ ├── polars_decimal.md
│ │ │ ├── polars_drop-duplicates.md
│ │ │ ├── polars_drop-nulls.md
│ │ │ ├── polars_drop.md
│ │ │ ├── polars_dummies.md
│ │ │ ├── polars_explode.md
│ │ │ ├── polars_expr-not.md
│ │ │ ├── polars_fetch.md
│ │ │ ├── polars_fill-nan.md
│ │ │ ├── polars_fill-null.md
│ │ │ ├── polars_filter-with.md
│ │ │ ├── polars_filter.md
│ │ │ ├── polars_first.md
│ │ │ ├── polars_flatten.md
│ │ │ ├── polars_get-day.md
│ │ │ ├── polars_get-hour.md
│ │ │ ├── polars_get-minute.md
│ │ │ ├── polars_get-month.md
│ │ │ ├── polars_get-nanosecond.md
│ │ │ ├── polars_get-ordinal.md
│ │ │ ├── polars_get-second.md
│ │ │ ├── polars_get-week.md
│ │ │ ├── polars_get-weekday.md
│ │ │ ├── polars_get-year.md
│ │ │ ├── polars_get.md
│ │ │ ├── polars_group-by.md
│ │ │ ├── polars_horizontal.md
│ │ │ ├── polars_implode.md
│ │ │ ├── polars_integer.md
│ │ │ ├── polars_into-df.md
│ │ │ ├── polars_into-dtype.md
│ │ │ ├── polars_into-lazy.md
│ │ │ ├── polars_into-nu.md
│ │ │ ├── polars_into-repr.md
│ │ │ ├── polars_into-schema.md
│ │ │ ├── polars_is-duplicated.md
│ │ │ ├── polars_is-in.md
│ │ │ ├── polars_is-not-null.md
│ │ │ ├── polars_is-null.md
│ │ │ ├── polars_is-unique.md
│ │ │ ├── polars_join-where.md
│ │ │ ├── polars_join.md
│ │ │ ├── polars_last.md
│ │ │ ├── polars_len.md
│ │ │ ├── polars_list-contains.md
│ │ │ ├── polars_lit.md
│ │ │ ├── polars_lowercase.md
│ │ │ ├── polars_math.md
│ │ │ ├── polars_max.md
│ │ │ ├── polars_mean.md
│ │ │ ├── polars_median.md
│ │ │ ├── polars_min.md
│ │ │ ├── polars_n-unique.md
│ │ │ ├── polars_not.md
│ │ │ ├── polars_open.md
│ │ │ ├── polars_otherwise.md
│ │ │ ├── polars_over.md
│ │ │ ├── polars_pivot.md
│ │ │ ├── polars_profile.md
│ │ │ ├── polars_qcut.md
│ │ │ ├── polars_quantile.md
│ │ │ ├── polars_query.md
│ │ │ ├── polars_rename.md
│ │ │ ├── polars_replace-time-zone.md
│ │ │ ├── polars_replace.md
│ │ │ ├── polars_reverse.md
│ │ │ ├── polars_rolling.md
│ │ │ ├── polars_sample.md
│ │ │ ├── polars_save.md
│ │ │ ├── polars_schema.md
│ │ │ ├── polars_select.md
│ │ │ ├── polars_set-with-idx.md
│ │ │ ├── polars_set.md
│ │ │ ├── polars_shape.md
│ │ │ ├── polars_shift.md
│ │ │ ├── polars_slice.md
│ │ │ ├── polars_sort-by.md
│ │ │ ├── polars_std.md
│ │ │ ├── polars_store-get.md
│ │ │ ├── polars_store-ls.md
│ │ │ ├── polars_store-rm.md
│ │ │ ├── polars_str-join.md
│ │ │ ├── polars_str-lengths.md
│ │ │ ├── polars_str-replace-all.md
│ │ │ ├── polars_str-replace.md
│ │ │ ├── polars_str-slice.md
│ │ │ ├── polars_str-split.md
│ │ │ ├── polars_str-strip-chars.md
│ │ │ ├── polars_strftime.md
│ │ │ ├── polars_struct-json-encode.md
│ │ │ ├── polars_sum.md
│ │ │ ├── polars_summary.md
│ │ │ ├── polars_take.md
│ │ │ ├── polars_truncate.md
│ │ │ ├── polars_unique.md
│ │ │ ├── polars_unnest.md
│ │ │ ├── polars_unpivot.md
│ │ │ ├── polars_uppercase.md
│ │ │ ├── polars_value-counts.md
│ │ │ ├── polars_var.md
│ │ │ ├── polars_when.md
│ │ │ ├── polars_with-column.md
│ │ │ ├── port.md
│ │ │ ├── prepend.md
│ │ │ ├── print.md
│ │ │ ├── ps.md
│ │ │ ├── pwd.md
│ │ │ ├── query.md
│ │ │ ├── query_db.md
│ │ │ ├── query_json.md
│ │ │ ├── query_web.md
│ │ │ ├── query_webpage-info.md
│ │ │ ├── query_xml.md
│ │ │ ├── random.md
│ │ │ ├── random_binary.md
│ │ │ ├── random_bool.md
│ │ │ ├── random_chars.md
│ │ │ ├── random_dice.md
│ │ │ ├── random_float.md
│ │ │ ├── random_int.md
│ │ │ ├── random_uuid.md
│ │ │ ├── reduce.md
│ │ │ ├── registry.md
│ │ │ ├── registry_query.md
│ │ │ ├── reject.md
│ │ │ ├── rename.md
│ │ │ ├── return.md
│ │ │ ├── reverse.md
│ │ │ ├── rm.md
│ │ │ ├── roll.md
│ │ │ ├── roll_down.md
│ │ │ ├── roll_left.md
│ │ │ ├── roll_right.md
│ │ │ ├── roll_up.md
│ │ │ ├── rotate.md
│ │ │ ├── run-external.md
│ │ │ ├── save.md
│ │ │ ├── schema.md
│ │ │ ├── scope.md
│ │ │ ├── scope_aliases.md
│ │ │ ├── scope_commands.md
│ │ │ ├── scope_engine-stats.md
│ │ │ ├── scope_externs.md
│ │ │ ├── scope_modules.md
│ │ │ ├── scope_variables.md
│ │ │ ├── select.md
│ │ │ ├── seq.md
│ │ │ ├── seq_char.md
│ │ │ ├── seq_date.md
│ │ │ ├── shuffle.md
│ │ │ ├── skip.md
│ │ │ ├── skip_until.md
│ │ │ ├── skip_while.md
│ │ │ ├── sleep.md
│ │ │ ├── slice.md
│ │ │ ├── sort-by.md
│ │ │ ├── sort.md
│ │ │ ├── source-env.md
│ │ │ ├── source.md
│ │ │ ├── split.md
│ │ │ ├── split_cell-path.md
│ │ │ ├── split_chars.md
│ │ │ ├── split_column.md
│ │ │ ├── split_list.md
│ │ │ ├── split_row.md
│ │ │ ├── split_words.md
│ │ │ ├── start.md
│ │ │ ├── stor.md
│ │ │ ├── stor_create.md
│ │ │ ├── stor_delete.md
│ │ │ ├── stor_export.md
│ │ │ ├── stor_import.md
│ │ │ ├── stor_insert.md
│ │ │ ├── stor_open.md
│ │ │ ├── stor_reset.md
│ │ │ ├── stor_update.md
│ │ │ ├── str.md
│ │ │ ├── str_camel-case.md
│ │ │ ├── str_capitalize.md
│ │ │ ├── str_contains.md
│ │ │ ├── str_distance.md
│ │ │ ├── str_downcase.md
│ │ │ ├── str_ends-with.md
│ │ │ ├── str_expand.md
│ │ │ ├── str_index-of.md
│ │ │ ├── str_join.md
│ │ │ ├── str_kebab-case.md
│ │ │ ├── str_length.md
│ │ │ ├── str_pascal-case.md
│ │ │ ├── str_replace.md
│ │ │ ├── str_reverse.md
│ │ │ ├── str_screaming-snake-case.md
│ │ │ ├── str_snake-case.md
│ │ │ ├── str_starts-with.md
│ │ │ ├── str_stats.md
│ │ │ ├── str_substring.md
│ │ │ ├── str_title-case.md
│ │ │ ├── str_trim.md
│ │ │ ├── str_upcase.md
│ │ │ ├── sys.md
│ │ │ ├── sys_cpu.md
│ │ │ ├── sys_disks.md
│ │ │ ├── sys_host.md
│ │ │ ├── sys_mem.md
│ │ │ ├── sys_net.md
│ │ │ ├── sys_temp.md
│ │ │ ├── sys_users.md
│ │ │ ├── table.md
│ │ │ ├── take.md
│ │ │ ├── take_until.md
│ │ │ ├── take_while.md
│ │ │ ├── tee.md
│ │ │ ├── term.md
│ │ │ ├── term_query.md
│ │ │ ├── term_size.md
│ │ │ ├── timeit.md
│ │ │ ├── to.md
│ │ │ ├── to_csv.md
│ │ │ ├── to_html.md
│ │ │ ├── to_json.md
│ │ │ ├── to_md.md
│ │ │ ├── to_msgpack.md
│ │ │ ├── to_msgpackz.md
│ │ │ ├── to_nuon.md
│ │ │ ├── to_plist.md
│ │ │ ├── to_text.md
│ │ │ ├── to_toml.md
│ │ │ ├── to_tsv.md
│ │ │ ├── to_xml.md
│ │ │ ├── to_yaml.md
│ │ │ ├── to_yml.md
│ │ │ ├── touch.md
│ │ │ ├── transpose.md
│ │ │ ├── try.md
│ │ │ ├── tutor.md
│ │ │ ├── ulimit.md
│ │ │ ├── uname.md
│ │ │ ├── uniq-by.md
│ │ │ ├── uniq.md
│ │ │ ├── update.md
│ │ │ ├── update_cells.md
│ │ │ ├── upsert.md
│ │ │ ├── url.md
│ │ │ ├── url_build-query.md
│ │ │ ├── url_decode.md
│ │ │ ├── url_encode.md
│ │ │ ├── url_join.md
│ │ │ ├── url_parse.md
│ │ │ ├── url_split-query.md
│ │ │ ├── use.md
│ │ │ ├── values.md
│ │ │ ├── version.md
│ │ │ ├── version_check.md
│ │ │ ├── view.md
│ │ │ ├── view_blocks.md
│ │ │ ├── view_files.md
│ │ │ ├── view_ir.md
│ │ │ ├── view_source.md
│ │ │ ├── view_span.md
│ │ │ ├── watch.md
│ │ │ ├── where.md
│ │ │ ├── which.md
│ │ │ ├── while.md
│ │ │ ├── whoami.md
│ │ │ ├── window.md
│ │ │ ├── with-env.md
│ │ │ ├── wrap.md
│ │ │ └── zip.md
│ │ └── cookbook
│ │ ├── README.md
│ │ ├── custom_completers.md
│ │ ├── direnv.md
│ │ ├── external_completers.md
│ │ ├── files.md
│ │ ├── foreign_shell_scripts.md
│ │ ├── git.md
│ │ ├── help.md
│ │ ├── http.md
│ │ ├── input_listen_keys.md
│ │ ├── jq_v_nushell.md
│ │ ├── modules.md
│ │ ├── parsing.md
│ │ ├── parsing_git_log.md
│ │ ├── pattern_matching.md
│ │ ├── polars_v_pandas_v_nushell.md
│ │ ├── setup.md
│ │ ├── ssh_agent.md
│ │ ├── system.md
│ │ └── tables.md
│ ├── tree-sitter-nu
│ │ ├── installation
│ │ │ └── neovim.md
│ │ └── plugin
│ │ └── init.lua
│ └── vscode-nushell-lang
│ ├── README.md
│ ├── example.nu
│ ├── generate-example.nu
│ ├── generate-patterns.nu
│ ├── language-configuration.json
│ ├── snippets
│ │ ├── convention.md
│ │ └── nushell.json
│ ├── syntaxes
│ │ ├── codeblock.json
│ │ └── nushell.tmLanguage.json
│ └── used_keywords.jsonc
├── nushell
│ ├── ai-completions-main.nu
│ ├── claude-command-file.md
│ ├── generator.md
│ ├── gh-stars-readme.md
│ ├── instructions-md.md
│ ├── nushell
│ │ └── shared
│ │ └── utils
│ │ ├── argument-parser.nu
│ │ ├── command-builder.nu
│ │ └── template-engine.nu
│ ├── nushell-cheatsheet-tldr.md
│ ├── nushell-quickref.md
│ ├── readme.md
│ └── tree-sitter-integration.nu
└── nushell.md
AGENTS.md:
# AI Agent Guidelines for Nushell Scripts Repository
## Executive Summary
This document delineates comprehensive guidelines for autonomous AI agents interacting with this nushell scripts repository. The prescribed methodologies prioritize functional programming paradigms, type safety, and adherence to Unix philosophy principles.
## Core Architectural Principles
### 1. Type System Enforcement
All nushell implementations must leverage the language's gradual type system:
```nushell
# Mandatory type signatures for all public APIs
def transform-data [
input: table<name: string, value: int> # Input schema
]: table<name: string, value: int> -> table<name: string, processed: float> {
# Implementation with guaranteed type safety
}Prioritize pure functions and immutable data transformations:
# Preferred: Functional pipeline composition
let result = $data
| filter-valid
| transform-schema
| aggregate-metrics
| format-output
# Avoided: Imperative mutation patterns
mut result = []
for item in $data {
# Side-effect heavy operations
}Implement lazy evaluation and streaming iterators for optimal memory utilization:
# Efficient streaming pipeline
def process-large-dataset []: any -> any {
# Leverages nushell's internal streaming architecture
each { |row|
# Process one element at a time
# Memory complexity: O(1) per element
}
}# src/modules/category/subcategory.nu
module subcategory {
# Private implementation details
def --wrapped internal-helper [...args] { }
# Public API surface
export def public-command [
required: string
--optional: int = 42
--flag (-f)
...rest: any
]: string -> table {
# Implementation
}
# Module-level main for direct invocation
export def main [] {
# Default module behavior
}
}Every public interface requires comprehensive documentation:
# Calculates the Levenshtein distance between two strings
#
# This implementation uses dynamic programming with space optimization,
# achieving O(min(m,n)) space complexity and O(m*n) time complexity.
#
# Parameters:
# source: The source string for comparison
# target: The target string for comparison
# --max-distance (-m): Early termination threshold (default: unlimited)
#
# Returns:
# int: The minimum edit distance between strings
#
# Examples:
# 'kitten' | levenshtein-distance 'sitting' # => 3
# levenshtein-distance 'abc' 'def' --max-distance 2 # => null (exceeds threshold)
#
# Performance characteristics:
# - Memory: O(min(|source|, |target|))
# - Time: O(|source| × |target|)
# - Early termination when distance exceeds threshold
export def levenshtein-distance [
target: string
--max-distance (-m): int
]: string -> int {
# Implementation details...
}# Define custom error types with rich metadata
def validate-input [data: any]: any -> any {
if ($data | describe) != "table" {
error make {
msg: "Type validation failed"
label: {
text: $"Expected table, received ($data | describe)"
span: (metadata $data).span
}
help: "Ensure input conforms to table<column: type> schema"
}
}
# Continue processing
$data
}
# Graceful error recovery with try-catch patterns
def resilient-operation []: nothing -> any {
try {
dangerous-operation
} catch { |err|
log error $"Operation failed: ($err.msg)"
# Return safe default or propagate
null
}
}# tests/test-module-name.nu
use std/assert
use ../src/modules/module-name.nu *
# Property-based testing
def "test property invariants" [] {
let test_cases = generate-test-cases 1000
for case in $test_cases {
let result = function-under-test $case.input
assert ($result | validate-invariant)
assert equal $result.type $case.expected_type
}
}
# Edge case validation
def "test boundary conditions" [] {
let edge_cases = [
[input, expected];
[null, null]
["", ""]
[[], []]
[0, 0]
[-1, error]
[9223372036854775807, overflow] # i64::MAX
]
for case in $edge_cases {
if $case.expected == "error" {
assert error { function-under-test $case.input }
} else {
assert equal (function-under-test $case.input) $case.expected
}
}
}Always document and optimize computational complexity:
# O(n log n) sorting with custom comparator
def optimized-sort [
key: closure # Key extraction function
]: table -> table {
# Implement efficient sorting algorithm
# Document complexity characteristics
}
# Memoization for expensive computations
def memoized-fibonacci [n: int]: nothing -> int {
# Initialize cache in environment
if 'FIB_CACHE' not-in $env {
$env.FIB_CACHE = {}
}
if $n in $env.FIB_CACHE {
return ($env.FIB_CACHE | get $n)
}
let result = if $n <= 1 { $n } else {
(memoized-fibonacci ($n - 1)) + (memoized-fibonacci ($n - 2))
}
$env.FIB_CACHE = ($env.FIB_CACHE | upsert $n $result)
$result
}For performance-critical operations, implement Rust plugins:
use nu_plugin::{LabeledError, Plugin, PluginCommand};
use nu_protocol::{Category, PluginSignature, Type, Value};
struct OptimizedCommand;
impl PluginCommand for OptimizedCommand {
type Plugin = OptimizedPlugin;
fn signature(&self) -> PluginSignature {
PluginSignature::build("optimized-operation")
.input_output_types(vec![
(Type::Table(vec![]), Type::Table(vec![])),
])
.category(Category::Filters)
}
fn run(
&self,
plugin: &OptimizedPlugin,
call: &EvaluatedCall,
input: Value,
) -> Result<Value, LabeledError> {
// SIMD-optimized implementation
}
}# Wrap external tools with type-safe interfaces
def --wrapped "external-tool" [
--config (-c): path # Configuration file
--parallel (-p): int = (sys cpu | length) # Parallelism level
...args: string # Passthrough arguments
]: any -> any {
# Validate inputs
if $config != null and not ($config | path exists) {
error make { msg: $"Configuration file not found: ($config)" }
}
# Execute with proper error handling
let result = (
^external-tool
...(if $config != null { ["--config", $config] } else { [] })
--parallel $parallel
...$args
| complete
)
if $result.exit_code != 0 {
error make {
msg: $"External tool failed: ($result.stderr)"
label: { text: $"Exit code: ($result.exit_code)" }
}
}
$result.stdout | from json # Parse structured output
}Before submitting changes, ensure:
- All functions have explicit type signatures
- Documentation includes complexity analysis
- Error paths are comprehensively handled
- Tests cover edge cases and invariants
- Performance benchmarks meet requirements
- No imperative mutations where functional alternatives exist
- Streaming iterators used for large datasets
- Module exports follow hierarchical organization
- Custom completions provided for user-facing commands
- Integration tests validate module interactions
# Result monad implementation
def result-map [f: closure]: record -> record {
if $in.type == "ok" {
{ type: "ok", value: (do $f $in.value) }
} else {
$in # Propagate error unchanged
}
}
def result-flatmap [f: closure]: record -> record {
if $in.type == "ok" {
do $f $in.value
} else {
$in
}
}
# Usage in pipelines
def complex-operation []: any -> record {
validate-input
| result-map { transform-data }
| result-flatmap { enrichment-step }
| result-map { format-output }
}# Infinite sequence generation with lazy evaluation
def lazy-sequence [
initial: any
next: closure # State transformation function
]: nothing -> any {
generate $initial { |state|
{
out: $state
next: (do $next $state)
}
}
}
# Fibonacci sequence generator
lazy-sequence 0 { |n| $n + 1 } | first 10All contributions must:
- Pass static analysis via
nu --ide-check - Achieve >90% test coverage
- Include performance benchmarks for new algorithms
- Document all public APIs with examples
- Follow functional programming best practices
- Minimize external dependencies
- Provide custom completions for interactive commands
- Include integration tests for module interactions
These guidelines ensure consistency, maintainability, and optimal performance across the codebase.
`CLAUDE.nushell.md`:
```md
# CLAUDE.md - Nushell Code Generation Guide
## Overview
This guide provides comprehensive patterns, best practices, and examples for generating Nushell code. Nushell is a new type of shell that works with structured data, offering powerful pipelines, broad OS compatibility, and a modern command-line experience.
## Core Nushell Principles
### Everything is Data
- Nushell treats everything as structured data (tables, records, lists)
- Commands output structured data that can be piped and transformed
- Use `describe` to inspect data types
### Pipeline-Oriented
- Commands are connected via pipes (`|`)
- Data flows from left to right
- Each command transforms the data stream
### Immutable by Default
- Variables are immutable unless declared with `mut`
- Prefer functional, immutable patterns over stateful loops
- Use `each`, `map`, `filter` instead of traditional loops
## Syntax and Language Features
### Variables and Types
```nushell
# Immutable variables
let name = "World"
let count = 42
let pi = 3.14159
# Mutable variables
mut counter = 0
$counter = $counter + 1
# Accessing variables
$name
$env.PATH # Environment variables
# Special variables
$in # Pipeline input
$nu # Nushell configuration
$env # Environment variables
# Strings
"hello" | 'world' | `backtick strings`
$"Interpolated ($variable) string"
$'Raw string with \n escape'
# Numbers
42 | 3.14 | 1.5e10 | 0x1F | 0b1010 | 0o755
# Booleans
true | false
# Dates and durations
2025-01-01 | 2hr | 3min | 4sec | 5ms
# File sizes
100b | 1kb | 2mb | 3gb | 4tb
# Lists
[1 2 3] | ["a", "b", "c"]
# Records
{ name: "Alice", age: 30 }
# Tables (list of records)
[[name age]; ["Alice" 30] ["Bob" 25]]
# Ranges
1..10 # Inclusive: 1 to 10
1..<10 # Exclusive end: 1 to 9
1.. # Unbounded# Simple command
def greet [name: string] {
$"Hello, ($name)!"
}
# Command with type annotations
def add [a: int, b: int]: int -> int {
$a + $b
}
# Command with pipeline input
def double []: int -> int {
$in * 2
}
# Command with implicit return
def latest-file [] {
ls | sort-by modified | last
}# Required positional parameters
def greet [name: string, age: int] {
$"Hello ($name), you are ($age) years old"
}
# Optional parameters with defaults
def greet [name: string = "World"] {
$"Hello, ($name)!"
}
# Named flags (--flag)
def greet [name: string, --age: int] {
if $age == null {
$"Hello, ($name)!"
} else {
$"Hello, ($name)! You are ($age) years old."
}
}
# Boolean switches
def greet [name: string, --caps] {
let greeting = $"Hello, ($name)!"
if $caps {
$greeting | str upcase
} else {
$greeting
}
}
# Rest parameters (variadic)
def multi-greet [...names: string] {
for name in $names {
print $"Hello, ($name)!"
}
}# Input/output type signatures
def inc []: int -> int { $in + 1 }
def "str stats" []: string -> record { }
def process []: list<int> -> list<int> { }
# Nothing type for side effects
def save-file [path: string]: nothing -> nothing {
$in | save $path
}
# Any type for flexible input
def inspect []: any -> string {
$in | describe
}# Define subcommands with spaces
def "str is-alphanumeric" []: string -> bool {
# Implementation
}
# Command groups
def "math add" [a: int, b: int] { $a + $b }
def "math sub" [a: int, b: int] { $a - $b }
def "math mul" [a: int, b: int] { $a * $b }# Document your commands
# Calculate the nth Fibonacci number
#
# This uses a recursive algorithm
# which is simple but not efficient
# for large values of n
def fib [
n: int # The position in the sequence
]: nothing -> int {
if $n <= 1 { $n } else { (fib ($n - 1)) + (fib ($n - 2)) }
}# Commands that modify environment
def --env go-home [] {
cd ~
}
def --env set-path [dir: string] {
$env.PATH = ($env.PATH | prepend $dir)
}# Wrap external commands
def --wrapped ezal [...rest] {
eza -l --icons ...$rest
}
# Conditional wrapping
def --wrapped ezal [...rest] {
if '-G' in $rest {
eza ...$rest
} else {
eza -l --icons ...$rest
}
}# if/else expressions
let result = if $x > 0 {
'positive'
} else if $x == 0 {
'zero'
} else {
'negative'
}
# match expressions
let result = match $value {
1 => 'one',
2 => 'two',
3..10 => 'three to ten',
_ => 'other'
}
# match with pattern matching
match $record {
{ name: "Alice", age: $a } => $"Alice is ($a) years old",
{ name: $n, age: 30 } => $"($n) is thirty",
_ => "Unknown person"
}
# match with guards
match $foo {
{ count: $c } if $c > 10 => "many",
{ count: $c } if $c > 0 => "some",
_ => "none"
}# Prefer functional approaches
[1 2 3] | each { $in * 2 } # Better
# But loops are available when needed
mut sum = 0
for x in [1 2 3] {
$sum = $sum + $x
}
# while loops
mut x = 0
while $x < 10 {
print $x
$x = $x + 1
}
# infinite loops
mut counter = 0
loop {
if $counter > 10 { break }
$counter = $counter + 1
}# try/catch for error handling
try {
dangerous_operation
} catch {
print $"Error occurred: ($in)"
}
# Custom errors
error make {
msg: "Something went wrong",
label: {
text: "error here",
span: (metadata $value).span
}
}
# Null coalescing
let result = $value? | default "fallback"# Simple module file: math.nu
export def add [a: int, b: int] { $a + $b }
export def sub [a: int, b: int] { $a - $b }
# Module with main command
export def main [] { "Math utilities" }
export def add [a: int, b: int] { $a + $b }
# Module with environment
export-env {
$env.MATH_PRECISION = 2
}
# Module with submodules
export module ./statistics.nu
export use ./algebra.nu# File structure
my-utils/
├── mod.nu # Main module file
├── math.nu # Submodule
├── strings.nu # Submodule
└── helpers.nu # Internal helpers (not exported)
# mod.nu content
export module ./math.nu
export module ./strings.nu
# Or re-export into parent
export use ./math.nu *
export use ./strings.nu *# Import entire module
use math.nu
math add 1 2
# Import all exports
use math.nu *
add 1 2
# Import specific items
use math.nu [add, sub]
use std/assert
# Import with aliases
use very-long-module-name.nu as vlm# Assertions for testing
use std/assert
assert equal $a $b
assert ($value > 0)
# Logging
use std/log
log info "Application started"
log error "Something went wrong"
# Formats
use std/formats *
$data | to json
$data | from yamluse std/assert
# Basic assertions
assert true
assert ($value == expected)
assert equal $actual $expected
assert not equal $a $b
assert greater $a $b
assert less or equal $a $b
# String assertions
assert str contains $haystack $needle
assert str starts-with $string $prefix
# Custom assertions
def "assert even" [n: int] {
assert ($n mod 2 == 0) --error-label {
text: $"($n) is not even",
span: (metadata $n).span
}
}# Test file: test_math.nu
use std/assert
use math.nu *
def "test addition" [] {
assert equal (add 2 3) 5
assert equal (add -1 1) 0
}
def "test subtraction" [] {
assert equal (sub 5 3) 2
assert equal (sub 0 1) -1
}
# Run tests
def main [] {
test addition
test subtraction
print "All tests passed!"
}use std/assert
def main [] {
print "Running tests..."
let test_commands = (
scope commands
| where ($it.type == "custom")
and ($it.name | str starts-with "test ")
and not ($it.name | str starts-with "ignore")
| get name
)
for test in $test_commands {
print $"Running: ($test)"
nu -c $"source ($env.CURRENT_FILE); ($test)"
}
print "Tests completed!"
}
def "test example" [] {
assert equal (1 + 1) 2
}# Filter and transform
ls | where size > 1mb | select name size modified
# Group and aggregate
ls | group-by type | transpose key value | each { |row|
{
type: $row.key,
count: ($row.value | length)
}
}
# Sort and limit
ps | sort-by cpu | last 10
# Pivot data
$data | pivot column_name value_name# Read files
open data.json | from json
open data.csv | from csv
open data.txt | lines
# Write files
$data | to json | save output.json
$data | to csv | save output.csv
"text" | save output.txt
# File operations
ls **/*.rs # Glob patterns
cd ~/projects
mkdir -p deeply/nested/directory
rm -rf old_directory# String operations
"hello" | str upcase
"WORLD" | str downcase
" trim " | str trim
"hello world" | str replace "world" "nushell"
"one,two,three" | split row ","
# String interpolation
let name = "Alice"
$"Hello, ($name)!"
$"2 + 2 = (2 + 2)"# Create tables
let data = [[name age]; ["Alice" 30] ["Bob" 25]]
# Access fields
$data | get name
$data.0.name # First row's name
$data | select name age
# Update data
$data | upsert status "active"
$data | update age { $in + 1 }
# Merge data
$data1 | merge $data2# Collect results
1..10 | each { $in * 2 } | collect
# Reduce/fold
[1 2 3 4] | reduce --fold 0 { |it, acc| $acc + $it }
# Parallel processing
1..100 | par-each { |n|
expensive_computation $n
}
# Tee pattern (process and pass through)
ls | tee { print $"Files: ($in | length)" } | where size > 1mb# Use descriptive command names with hyphens
def parse-config-file [] { }
# Use type annotations
def calculate [x: int, y: int]: int -> int { }
# Document complex commands
# Parse configuration from YAML file
# Returns a record with config values
def parse-config [path: string]: nothing -> record { }
# Prefer pipelines over loops
# Good
ls | each { |file| process $file }
# Avoid
for file in (ls) { process $file }# Use lazy evaluation
1.. | each { $in * 2 } | take 10 # Only processes 10 items
# Use par-each for CPU-bound work
$large_list | par-each { expensive_op $in }
# Avoid repeated computations
let result = expensive_computation
# Use $result multiple times
# Stream processing for large data
open large_file.txt | lines | each { process_line $in }# Validate inputs
def process [value: int] {
if $value < 0 {
error make { msg: "Value must be non-negative" }
}
# Process value
}
# Use try/catch for external commands
try {
^external-command
} catch {
print $"Command failed: ($in)"
# Handle error
}
# Provide helpful error messages
assert ($condition) "Expected condition to be true, but got false"# Export only public interface
export def public-command [] { }
def private-helper [] { } # Not exported
# Use main for primary functionality
export def main [] { }
export def subcommand [] { }
# Group related commands
export def "db connect" [] { }
export def "db query" [] { }
export def "db close" [] { }# Bad: Using loop for transformation
mut result = []
for item in $list {
$result = ($result | append ($item * 2))
}
# Good: Using pipeline
$list | each { $in * 2 }# Bad: Unnecessary mutation
mut sum = 0
for n in [1 2 3] {
$sum = $sum + $n
}
# Good: Functional approach
[1 2 3] | reduce --fold 0 { |it, acc| $acc + $it }# Bad: Deep nesting
if $condition1 {
if $condition2 {
if $condition3 {
# Do something
}
}
}
# Good: Early returns or match
if not $condition1 { return }
if not $condition2 { return }
if not $condition3 { return }
# Do something# Bad: No type information
def process [data] { }
# Good: Clear types
def process [data: list<record>]: nothing -> table { }# Comprehensive argument parser with validation
def "parse arguments" [
raw_args: string,
command_spec: record
] -> record {
# Parse help flag first
if ($raw_args | str contains "--help") or ($raw_args | str contains "-h") {
print_help $command_spec
return {parsed: {}, help_shown: true}
}
# Split arguments preserving quoted strings
let args = ($raw_args | split row ' ' | where $it != "")
mut parsed = {}
mut positional = []
mut i = 0
while $i < ($args | length) {
let arg = ($args | get $i)
if ($arg | str starts-with '--') {
# Long flag handling
let flag_name = ($arg | str replace '--' '')
if ($i + 1) < ($args | length) {
let next_arg = ($args | get ($i + 1))
if not ($next_arg | str starts-with '-') {
$parsed = ($parsed | insert $flag_name $next_arg)
$i = $i + 2
} else {
$parsed = ($parsed | insert $flag_name true)
$i = $i + 1
}
}
} else {
$positional = ($positional | append $arg)
$i = $i + 1
}
}
# Apply defaults and validate
for spec in ($command_spec.arguments? | default []) {
if ($spec.name not-in ($parsed | columns)) and ($spec.default? != null) {
$parsed = ($parsed | insert $spec.name $spec.default)
}
}
return {parsed: $parsed, help_shown: false}
}
# Create command specification
def "create command spec" [
name: string,
description: string,
arguments: list,
--usage: string,
--examples: list
] -> record {
mut spec = {
name: $name,
description: $description,
arguments: $arguments
}
if ($usage != null) {
$spec = ($spec | insert usage $usage)
}
if ($examples != null) {
$spec = ($spec | insert examples $examples)
}
return $spec
}
# Create argument specification
def "create arg spec" [
name: string,
description: string,
--type: string,
--short: string,
--required = false,
--default: any,
--choices: list,
--min: int,
--max: int
] -> record {
mut spec = {
name: $name,
description: $description,
required: $required
}
if ($type != null) { $spec = ($spec | insert type $type) }
if ($short != null) { $spec = ($spec | insert short $short) }
if ($default != null) { $spec = ($spec | insert default $default) }
if ($choices != null) { $spec = ($spec | insert choices $choices) }
if ($min != null) { $spec = ($spec | insert min $min) }
if ($max != null) { $spec = ($spec | insert max $max) }
return $spec
}#!/usr/bin/env nu
# Main command dispatcher with subcommands
def main [command?: string, ...args: string] {
let available_commands = [
"build", "test", "deploy", "status", "help"
]
if ($command | is-empty) or $command == "help" {
show_help $available_commands
return
}
if $command not-in $available_commands {
log error $"Unknown command: ($command)"
show_help $available_commands
exit 1
}
match $command {
"build" => { main build ...$args }
"test" => { main test ...$args }
"deploy" => { main deploy ...$args }
"status" => { main status ...$args }
_ => {
log error $"Command not implemented: ($command)"
exit 1
}
}
}
# Subcommand implementation
def "main build" [
--env: string = "development",
--clean = false,
--verbose = false
] {
log info $"Building for environment: ($env)"
if $clean {
log info "Cleaning build directory..."
rm -rf build/
}
# Build implementation
try {
# Build steps here
log success "✅ Build completed successfully"
} catch { |e|
log error $"❌ Build failed: ($e.msg)"
exit 1
}
}
def show_help [commands: list<string>] {
print "📦 Project Manager"
print ""
print "Usage: nu project.nu <command> [options]"
print ""
print "Available commands:"
for cmd in $commands {
print $" ($cmd | str pad-right 10) - ($get_command_description $cmd)"
}
}# modules/types.nu - Type definitions and validation
export def "validate email" [email: string] -> bool {
$email | str contains "@" and ($email | str contains ".")
}
export def "validate url" [url: string] -> bool {
$url | str starts-with "http://" or $url | str starts-with "https://"
}
# Create typed record with validation
export def "create user" [
name: string,
email: string,
age: int,
--role: string = "user"
] -> record {
# Validate inputs
if not (validate email $email) {
error make {
msg: "Invalid email address",
label: {
text: $"Email must contain @ and .: ($email)",
span: (metadata $email).span
}
}
}
if $age < 0 or $age > 150 {
error make {
msg: "Invalid age",
label: {
text: $"Age must be between 0 and 150: ($age)",
span: (metadata $age).span
}
}
}
# Return validated record
{
id: (random uuid),
name: ($name | str trim),
email: ($email | str trim | str downcase),
age: $age,
role: $role,
created_at: (date now),
updated_at: (date now)
}
}# config/manager.nu - Configuration management system
# Load configuration with fallbacks
export def "load config" [
--env: string = "development",
--override: record = {}
] -> record {
# Configuration hierarchy: overrides > env-specific > defaults
let default_config = load_default_config
let env_config = load_env_config $env
let final_config = ($default_config | merge $env_config | merge $override)
# Validate configuration
validate_config $final_config
return $final_config
}
def load_default_config [] -> record {
{
app: {
name: "MyApp",
version: "1.0.0",
port: 8080,
host: "localhost"
},
database: {
type: "sqlite",
path: "data/app.db"
},
logging: {
level: "info",
format: "json"
}
}
}
def load_env_config [env: string] -> record {
let config_file = $"config/($env).yaml"
if ($config_file | path exists) {
open $config_file | from yaml
} else {
{}
}
}
def validate_config [config: record] {
# Ensure required fields exist
let required = ["app.name", "app.port", "database.type"]
for field in $required {
let parts = ($field | split row ".")
let value = try {
$config | get $parts.0 | get $parts.1
} catch {
error make {
msg: $"Required configuration field missing: ($field)"
}
}
}
# Validate port range
if $config.app.port < 1 or $config.app.port > 65535 {
error make {
msg: $"Invalid port number: ($config.app.port)"
}
}
}# scripts/cross-env-orchestrator.nu
# Orchestrate tasks across multiple environments
def orchestrate [
task: string,
--environments: list<string> = ["python-env", "typescript-env", "rust-env"],
--parallel = false,
--stop-on-error = true
] {
log info $"🎭 Orchestrating '($task)' across ($environments | length) environments"
if $parallel {
orchestrate_parallel $task $environments
} else {
orchestrate_sequential $task $environments $stop_on_error
}
}
def orchestrate_sequential [
task: string,
environments: list<string>,
stop_on_error: bool
] {
mut results = []
for env in $environments {
log info $"🔧 Running ($task) in ($env)..."
let result = try {
execute_in_environment $env $task
{environment: $env, status: "success", output: "Task completed"}
} catch { |e|
{environment: $env, status: "failed", error: $e.msg}
}
$results = ($results | append $result)
if $result.status == "failed" and $stop_on_error {
log error $"❌ Failed in ($env), stopping orchestration"
break
}
}
# Display results
print_results $results
}
def execute_in_environment [env: string, task: string] {
cd $env
match $task {
"test" => {
match $env {
"python-env" => { ^uv run pytest }
"typescript-env" => { ^npm test }
"rust-env" => { ^cargo test }
_ => { error make {msg: $"Unknown environment: ($env)"} }
}
}
"build" => {
match $env {
"python-env" => { ^uv build }
"typescript-env" => { ^npm run build }
"rust-env" => { ^cargo build --release }
_ => { error make {msg: $"Unknown environment: ($env)"} }
}
}
_ => {
error make {msg: $"Unknown task: ($task)"}
}
}
cd ..
}# tests/framework.nu - Testing framework
# Test runner with discovery
def "run tests" [
--pattern: string = "test_*.nu",
--verbose = false,
--stop-on-failure = false
] {
log info "🧪 Starting test runner"
# Discover test files
let test_files = (ls tests/**/*.nu | where name =~ $pattern | get name)
log info $"Found ($test_files | length) test files"
mut total_tests = 0
mut passed_tests = 0
mut failed_tests = []
for file in $test_files {
log info $"Running tests in ($file)..."
# Source the test file
source $file
# Discover test functions
let test_functions = (
scope commands
| where type == "custom" and ($it.name | str starts-with "test ")
| get name
)
for test_fn in $test_functions {
$total_tests = $total_tests + 1
let result = run_single_test $test_fn $verbose
if $result.passed {
$passed_tests = $passed_tests + 1
if $verbose {
log success $"✅ ($test_fn)"
}
} else {
$failed_tests = ($failed_tests | append {
test: $test_fn,
error: $result.error
})
log error $"❌ ($test_fn): ($result.error)"
if $stop_on_failure {
break
}
}
}
}
# Print summary
print ""
print "=" * 50
print $"📊 Test Results: ($passed_tests)/($total_tests) passed"
if ($failed_tests | length) > 0 {
print ""
print "Failed tests:"
for test in $failed_tests {
print $" ❌ ($test.test): ($test.error)"
}
exit 1
} else {
log success "🎉 All tests passed!"
}
}
def run_single_test [test_name: string, verbose: bool] -> record {
try {
# Set up test environment
setup_test_env
# Run test
do $test_name
# Clean up
cleanup_test_env
{passed: true, error: null}
} catch { |e|
{passed: false, error: $e.msg}
}
}
# Test assertion helpers
def assert [condition: bool, --message: string = "Assertion failed"] {
if not $condition {
error make {msg: $message}
}
}
def "assert equal" [actual: any, expected: any] {
if $actual != $expected {
error make {
msg: $"Expected ($expected), got ($actual)"
}
}
}
def "assert contains" [haystack: string, needle: string] {
if not ($haystack | str contains $needle) {
error make {
msg: $"Expected '($haystack)' to contain '($needle)'"
}
}
}# templates/generator.nu - Template generation system
# Generate file from template
def "generate from template" [
template_name: string,
output_path: string,
variables: record
] {
log info $"📝 Generating from template: ($template_name)"
# Load template
let template_path = $"templates/($template_name).tpl"
if not ($template_path | path exists) {
error make {msg: $"Template not found: ($template_path)"}
}
let template = (open $template_path)
# Process template variables
let processed = (process_template $template $variables)
# Ensure output directory exists
mkdir ($output_path | path dirname)
# Write output
$processed | save $output_path --force
log success $"✅ Generated: ($output_path)"
}
def process_template [template: string, variables: record] -> string {
mut result = $template
# Replace variables in format {{variable_name}}
for key in ($variables | columns) {
let value = ($variables | get $key)
let pattern = $"\\{\\{($key)\\}\\}"
$result = ($result | str replace --all $pattern ($value | to text))
}
# Process conditionals {{#if variable}} ... {{/if}}
$result = (process_conditionals $result $variables)
# Process loops {{#each items}} ... {{/each}}
$result = (process_loops $result $variables)
return $result
}
def process_conditionals [template: string, variables: record] -> string {
mut result = $template
# Find all conditionals
let pattern = '\\{\\{#if ([^}]+)\\}\\}([\\s\\S]*?)\\{\\{/if\\}\\}'
# This is simplified - real implementation would use proper regex
# Process each conditional block
return $result
}# common/utils.nu - Common utilities
# Enhanced logging with levels and formatting
export def log [
level: string,
message: string,
--data: any = null
] {
let timestamp = (date now | format date "%Y-%m-%d %H:%M:%S")
let color = match $level {
"debug" => "gray"
"info" => "blue"
"success" => "green"
"warning" => "yellow"
"error" => "red"
_ => "white"
}
let icon = match $level {
"debug" => "🔍"
"info" => "ℹ️"
"success" => "✅"
"warning" => "⚠️"
"error" => "❌"
_ => "📝"
}
let formatted = $"[($timestamp)] ($icon) ($message)"
# Output to stderr for errors/warnings
if $level in ["error", "warning"] {
print -e $formatted
} else {
print $formatted
}
# Log additional data if provided
if $data != null {
print ($data | to json --indent 2)
}
# Also write to log file
let log_file = "logs/app.log"
mkdir logs
$"[($timestamp)] [($level | str upcase)] ($message)\n" | save --append $log_file
}
# Retry mechanism for flaky operations
export def retry [
operation: closure,
--max-attempts: int = 3,
--delay: duration = 1sec,
--backoff: float = 2.0
] -> any {
mut attempt = 1
mut current_delay = $delay
loop {
log debug $"Attempt ($attempt)/($max_attempts)"
let result = try {
do $operation
} catch { |e|
if $attempt >= $max_attempts {
log error $"All ($max_attempts) attempts failed"
error make {msg: $"Operation failed after ($max_attempts) attempts: ($e.msg)"}
}
log warning $"Attempt ($attempt) failed: ($e.msg), retrying in ($current_delay)..."
sleep $current_delay
# Exponential backoff
$current_delay = ($current_delay * $backoff)
$attempt = $attempt + 1
continue
}
return $result
}
}
# Safe file operations
export def "safe save" [
path: string,
content: any,
--backup = true
] {
if $backup and ($path | path exists) {
let backup_path = $"($path).backup.(date now | format date '%Y%m%d_%H%M%S')"
cp $path $backup_path
log info $"Created backup: ($backup_path)"
}
# Write to temporary file first
let temp_path = $"($path).tmp"
$content | save $temp_path
# Atomic rename
mv $temp_path $path --force
log success $"Saved: ($path)"
}# env/detector.nu - Environment detection and adaptation
# Detect current environment and capabilities
export def "detect environment" [] -> record {
{
os: (sys host | get name),
arch: (sys host | get arch),
nu_version: (version | get version),
shell: $nu.env.SHELL?,
in_container: (detect_container),
in_devbox: (detect_devbox),
in_git_repo: (detect_git),
available_tools: (detect_available_tools)
}
}
def detect_container [] -> bool {
# Check for Docker or Podman
("/proc/1/cgroup" | path exists) and (
open /proc/1/cgroup | str contains "docker" or
open /proc/1/cgroup | str contains "podman"
) or ("/.dockerenv" | path exists)
}
def detect_devbox [] -> bool {
($env.DEVBOX_SHELL_ENABLED? | default false) == true
}
def detect_git [] -> bool {
try {
git rev-parse --git-dir | complete | get exit_code
} | default 1 == 0
}
def detect_available_tools [] -> record {
let tools = ["docker", "git", "python", "node", "cargo", "go"]
mut available = {}
for tool in $tools {
let exists = (which $tool | is-not-empty)
if $exists {
let version = try {
match $tool {
"python" => (^python --version 2>&1 | str trim)
"node" => (^node --version | str trim)
_ => "unknown"
}
} catch { "error" }
$available = ($available | insert $tool {exists: true, version: $version})
} else {
$available = ($available | insert $tool {exists: false, version: null})
}
}
$available
}# Create overlay from module
module spam {
export def foo [] { "foo" }
export-env { $env.BAZ = "baz" }
}
overlay use spam
overlay list
overlay hide spam# Define constants for performance
const CONFIG_DIR = $nu.default-config-dir | path join "myapp"
const MAX_RETRIES = 3
# Use in commands
def load-config [] {
open $CONFIG_DIR | path join "config.toml"
}# Define custom completion
def animals [] {
["cat", "dog", "hamster"]
}
# Use in command
export def feed [pet: string@animals] {
$"Feeding ($pet)"
}# Inspect available commands
scope commands | where type == "custom"
# Inspect modules
scope modules
# Inspect variables
scope variables- Nushell Book - Main documentation
- Nushell Commands - Command reference
- Nushell Cookbook - Recipes and patterns
- Nushell Blog - Updates and articles
- Custom Commands - Defining functions
- Modules - Code organization
- Control Flow - Conditionals and loops
- Testing - Writing tests
- Pipelines - Core concept
- Working with Lists - Data manipulation
- Environment - Environment variables
- GitHub Repository - Source code
- Discord Server - Community chat
- Awesome Nushell - Curated list of resources
- Nu Scripts - Example scripts
- Nupm - Package manager for Nushell
- Nushell Plugins - Extension system
This guide is based on Nushell 0.107.0 (current version as of 2025). Check your version with:
version | get versionUpdate to the latest version for all features described in this guide.
`docs/nushell/nu_scripts/README.md`:
```md
# Nushell Scripts
This is a place to share Nushell scripts with each other. If you'd like to share your scripts, fork this repository, and [create a PR](https://github.com/nushell/nu_scripts/compare) that adds it to the repo.
## Sections
- [aliases](./aliases/)
- [benchmarks](./benchmarks/)
- [cool-oneliners](./sourced/cool-oneliners/)
- [custom-completions](./custom-completions/) - collection of custom completions for external commands.
- [custom-menus](./custom-menus/) - collection of custom nushell menus
- [example-config](./example-config/)
- [nu-hooks](./nu-hooks/)
- [modules](./modules/) - This has its dedicated [readme](./modules/README.md)
- [nu_101](./sourced/nu_101/) - Beginner introduction to nushell concepts.
- [prompt](./modules/prompt/)
- [themes](./themes/)
## Running Scripts
You can run nushell scripts in a few different ways.
1. You can type `nu <script name>`.
2. From with nushell, you can type `source <script name>` and if the script is just a bunch of commands it will run the script. If the script is a custom command it will load those custom commands into your current scope so you can run them like any other command.
docs/nushell/nu_scripts/aliases/README.md:
# Custom aliases
This current directory provides custom aliases. They can be used by importing their exported aliases via:
```nushell
use path/to/<command>/<command>-aliases.nu *With path/to/<command> being either the relative path of the file to your current working directory or its absolute path.
`docs/nushell/nu_scripts/aliases/bat/bat-aliases.nu`:
```nu
export alias b = bat
export alias bn = bat --number
export alias bnl = bat --number --line-range
export alias bp = bat --plain
export alias bpl = bat --plain --line-range
export alias bl = bat --line-range
docs/nushell/nu_scripts/aliases/chezmoi/chezmoi-aliases.nu:
export alias ch = chezmoi
export alias chad = chezmoi add
export alias chap = chezmoi apply
export alias chd = chezmoi diff
export alias chda = chezmoi data
export alias chs = chezmoi status
docs/nushell/nu_scripts/aliases/docker/README.md:
# docker alias in nushell
This plugin adds the following aliases:
| Alias | Command | Description |
| ------- | --------------------------------- | ---------------------------------------------------------------------------------------- |
| dbl | docker build | Build an image from a Dockerfile |
| dcin | docker container inspect | Display detailed information on one or more containers |
| dcls | docker container ls | List all the running docker containers |
| dclsa | docker container ls -a | List all running and stopped containers |
| dib | docker image build | Build an image from a Dockerfile (same as docker build) |
| dii | docker image inspect | Display detailed information on one or more images |
| dils | docker image ls | List docker images |
| dipu | docker image push | Push an image or repository to a remote registry |
| dirm | docker image rm | Remove one or more images |
| dit | docker image tag | Add a name and tag to a particular image |
| dlo | docker container logs | Fetch the logs of a docker container |
| dnc | docker network create | Create a new network |
| dncn | docker network connect | Connect a container to a network |
| dndcn | docker network disconnect | Disconnect a container from a network |
| dni | docker network inspect | Return information about one or more networks |
| dnls | docker network ls | List all networks the engine daemon knows about, including those spanning multiple hosts |
| dnrm | docker network rm | Remove one or more networks |
| dpo | docker container port | List port mappings or a specific mapping for the container |
| dpu | docker pull | Pull an image or a repository from a registry |
| dr | docker container run | Create a new container and start it using the specified command |
| drit | docker container run -it | Create a new container and start it in an interactive shell |
| drm | docker container rm | Remove the specified container(s) |
| drm! | docker container rm -f | Force the removal of a running container (uses SIGKILL) |
| dst | docker container start | Start one or more stopped containers |
| drs | docker container restart | Restart one or more containers |
| dstp | docker container stop | Stop one or more running containers |
| dtop | docker top | Display the running processes of a container |
| dvi | docker volume inspect | Display detailed information about one or more volumes |
| dvls | docker volume ls | List all the volumes known to docker |
| dvprune | docker volume prune | Cleanup dangling volumes |
| dxc | docker container exec | Run a new command in a running container |
| dxcit | docker container exec -it | Run a new command in a running container in an interactive shell |
| dsta | docker ps -q \| xargs docker stop | Stop all running containers |
## install and use
- install
```nushell
use {project_path}/aliases/docker/docker.nu-
use
docker-aliases + tab
`docs/nushell/nu_scripts/aliases/docker/docker-aliases.nu`:
```nu
export alias dbl = docker build
export alias dcin = docker container inspect
export alias dcls = docker container ls
export alias dclsa = docker container ls -a
export alias dib = docker image build
export alias dii = docker image inspect
export alias dils = docker image ls
export alias dipu = docker image push
export alias dirm = docker image rm
export alias dit = docker image tag
export alias dlo = docker container logs
export alias dnc = docker network create
export alias dncn = docker network connect
export alias dndcn = docker network disconnect
export alias dni = docker network inspect
export alias dnls = docker network ls
export alias dnrm = docker network rm
export alias dpo = docker container port
export alias dpu = docker pull
export alias dr = docker container run
export alias drit = docker container run -it
export alias drm = docker container rm
export alias drm! = docker container rm -f
export alias dst = docker container start
export alias drs = docker container restart
export alias dstp = docker container stop
export alias dtop = docker top
export alias dvi = docker volume inspect
export alias dvls = docker volume ls
export alias dvprune = docker volume prune
export alias dxc = docker container exec
export alias dxcit = docker container exec -it
# Alias for `docker ps -q | xargs docker stop`
export def dsta [] {
docker ps -q | xargs docker stop
}
docs/nushell/nu_scripts/aliases/exa/exa-aliases.nu:
export alias x = exa --icons
export alias xa = exa --icons --all
export alias xl = exa --long
export alias xla = exa --long --all
export alias xt = exa --icons --tree
export alias xta = exa --icons --tree --all
docs/nushell/nu_scripts/aliases/eza/eza-aliases.nu:
export alias x = eza --icons
export alias xa = eza --icons --all
export alias xl = eza --long
export alias xla = eza --long --all
export alias xt = eza --icons --tree
export alias xta = eza --icons --tree --all
docs/nushell/nu_scripts/aliases/git/git-aliases.nu:
# returns the name of the current branch
export def git_current_branch [] {
^git rev-parse --abbrev-ref HEAD
}
export def git_main_branch [] {
git remote show origin
| lines
| str trim
| find --regex 'HEAD .*?[:: ].+'
| first
| ansi strip
| str replace --regex 'HEAD .*?[:: ]\s*(.+)' '$1'
}
#
# Aliases
# (sorted alphabetically)
#
export alias ga = git add
export alias gaa = git add --all
export alias gapa = git add --patch
export alias gau = git add --update
export alias gav = git add --verbose
export alias gap = git apply
export alias gapt = git apply --3way
export alias gb = git branch
export alias gba = git branch --all
export alias gbd = git branch --delete
export alias gbD = git branch --delete --force
export alias gbl = git blame -b -w
export alias gbm = git branch --move
export alias gbmc = git branch --move (git_current_branch)
export alias gbnm = git branch --no-merged
export alias gbr = git branch --remote
export alias gbs = git bisect
export alias gbsb = git bisect bad
export alias gbsg = git bisect good
export alias gbsn = git bisect new
export alias gbso = git bisect old
export alias gbsr = git bisect reset
export alias gbss = git bisect start
export alias gc = git commit --verbose
export alias gc! = git commit --verbose --amend
export alias gcn = git commit --verbose --no-edit
export alias gcn! = git commit --verbose --no-edit --amend
export alias gca = git commit --verbose --all
export alias gca! = git commit --verbose --all --amend
export alias gcan! = git commit --verbose --all --no-edit --amend
export alias gcans! = git commit --verbose --all --signoff --no-edit --amend
export def gcam [message: string] {
git commit --all --message $message
}
export def gcsm [message: string] {
git commit --all --signoff $message
}
export alias gcas = git commit --all --signoff
export def gcasm [message: string] {
git commit --all --signoff --message $message
}
export alias gcb = git checkout -b
export alias gcd = git checkout develop
export alias gcf = git config --list
export alias gcl = git clone --recurse-submodules
export alias gclean = git clean --interactive -d
export def gpristine [] {
git reset --hard
git clean -d --force -x
}
export alias gcm = git checkout (git_main_branch)
export def gcmsg [message: string] {
git commit --message $message
}
export alias gco = git checkout
export alias gcor = git checkout --recurse-submodules
export alias gcount = git shortlog --summary --numbered
export alias gcp = git cherry-pick
export alias gcpa = git cherry-pick --abort
export alias gcpc = git cherry-pick --continue
export alias gcs = git commit --gpg-sign
export alias gcss = git commit --gpg-sign --signoff
export def gcssm [message: string] {
git commit --gpg-sign --signoff --message $message
}
export alias gd = git diff
export alias gdca = git diff --cached
export alias gdcw = git diff --cached --word-diff
export alias gdct = git describe --tags (git rev-list --tags --max-count=1)
export alias gds = git diff --staged
export alias gdt = git diff-tree --no-commit-id --name-only -r
export alias gdup = git diff @{upstream}
export alias gdw = git diff --word-diff
export alias gf = git fetch
export alias gfa = git fetch --all --prune
export alias gfo = git fetch origin
export alias gg = git gui citool
export alias gga = git gui citool --amend
export alias ghh = git help
export alias gignore = git update-index --assume-unchanged
export alias gl = git log
export alias glg = git log --stat
export alias glgp = git log --stat --patch
export alias glgg = git log --graph
export alias glgga = git log --graph --decorate --all
export alias glgm = git log --graph --max-count=10
export alias glo = git log --oneline --decorate
export alias glod = git log --graph $'--pretty=%Cred%h%Creset -%C(char lp)auto(char rp)%d%Creset %s %Cgreen(char lp)%ad(char rp) %C(char lp)bold blue(char rp)<%an>%Creset'
export alias glods = git log --graph $'--pretty=%Cred%h%Creset -%C(char lp)auto(char rp)%d%Creset %s %Cgreen(char lp)%ad(char rp) %C(char lp)bold blue(char rp)<%an>%Creset' --date=short
export alias glog = git log --oneline --decorate --graph
export alias gloga = git log --oneline --decorate --graph --all
export alias glol = git log --graph $'--pretty=%Cred%h%Creset -%C(char lp)auto(char rp)%d%Creset %s %Cgreen(char lp)%ar(char rp) %C(char lp)bold blue(char rp)<%an>%Creset'
export alias glola = git log --graph $'--pretty=%Cred%h%Creset -%C(char lp)auto(char rp)%d%Creset %s %Cgreen(char lp)%ar(char rp) %C(char lp)bold blue(char rp)<%an>%Creset' --all
export alias glols = git log --graph $'--pretty=%Cred%h%Creset -%C(char lp)auto(char rp)%d%Creset %s %Cgreen(char lp)%ar(char rp) %C(char lp)bold blue(char rp)<%an>%Creset' --stat
export alias gm = git merge
export alias gmtl = git mergetool --no-prompt
export alias gmtlvim = git mergetool --no-prompt --tool=vimdiff
export alias gma = git merge --abort
export def gmom [] {
let main = (git_main_branch)
git merge $"origin/($main)"
}
export alias gp = git push
export alias gpd = git push --dry-run
export alias gpf = git push --force-with-lease
export alias gpf! = git push --force
export alias gpl = git pull
export def gpoat [] {
git push origin --all; git push origin --tags
}
export alias gpod = git push origin --delete
export alias gpodc = git push origin --delete (git_current_branch)
def "nu-complete git pull rebase" [] {
["false","true","merges","interactive"]
}
export def gpr [rebase_type: string@"nu-complete git pull rebase"] {
git pull --rebase $rebase_type
}
export alias gpu = git push upstream
export alias gpv = git push --verbose
export alias gr = git remote
export alias gpra = git pull --rebase --autostash
export alias gprav = git pull --rebase --autostash --verbose
export alias gprv = git pull --rebase --verbose
export alias gpsup = git push --set-upstream origin (git_current_branch)
export alias gra = git remote add
export alias grb = git rebase
export alias grba = git rebase --abort
export alias grbc = git rebase --continue
export alias grbd = git rebase develop
export alias grbi = git rebase --interactive
export alias grbm = git rebase (git_main_branch)
export alias grbo = git rebase --onto
export alias grbs = git rebase --skip
export alias grev = git revert
export alias grh = git reset
export alias grhh = git reset --hard
export alias groh = git reset $"origin/(git_current_branch)" --hard
export alias grm = git rm
export alias grmc = git rm --cached
export def grmv [remote: string, new_name: string] {
git remote rename $remote $new_name
}
export def grrm [remote: string] {
git remote remove $remote
}
export alias grs = git restore
export def grset [remote: string, url: string] {
git remote set-url $remote $url
}
export alias grss = git restore --source
export alias grst = git restore --staged
export alias grt = cd (git rev-parse --show-toplevel or echo .)
export alias gru = git reset --
export alias grup = git remote update
export alias grv = git remote --verbose
export alias gsb = git status --short --branch
export alias gsd = git svn dcommit
export alias gsh = git show
export alias gshs = git show -s
export alias gsi = git submodule init
export alias gsps = git show --pretty=short --show-signature
export alias gsr = git svn rebase
export alias gss = git status --short
export alias gst = git status
export alias gsta = git stash push
export alias gstaa = git stash apply
export alias gstc = git stash clear
export alias gstd = git stash drop
export alias gstl = git stash list
export alias gstp = git stash pop
export alias gsts = git stash show --text
export alias gstu = gsta --include-untracked
export alias gstall = git stash --all
export alias gsu = git submodule update
export alias gsw = git switch
export alias gswc = git switch --create
export alias gts = git tag --sign
export def gtv [] {
git tag | lines | sort
}
export alias glum = git pull upstream (git_main_branch)
export alias gunignore = git update-index --no-assume-unchanged
export def gup [rebase_type: string@"nu-complete git pull rebase"] {
git pull --rebase $rebase_type
}
export alias gupv = git pull --rebase --verbose
export alias gupa = git pull --rebase --autostash
export alias gupav = git pull --rebase --autostash --verbose
export alias gwch = git whatchanged -p --abbrev-commit --pretty=medium
export alias gwt = git worktree
export def gwta [path: path, branch?: string] {
if $branch != null {
git worktree add $path $branch
} else {
git worktree add $path
}
}
export alias gwtls = git worktree list
export alias gwtmv = git worktree move
export def gwtm [worktree: string] {
git worktree remove $worktree
}
export alias gam = git am
export alias gamc = git am --continue
export alias gams = git am --skip
export alias gama = git am --abort
export alias gamscp = git am --show-current-patch
docs/nushell/nu_scripts/custom-completions/as/as-completions.nu:
# Omit false conditionals
export extern "as" [
--alternate # Initially turn on alternate macro syntax
--nocompress-debug-sections # Dont compress DWARF debug sections
--execstack # Require executable stack for this object
--noexecstack # Dont require executable stack for this object
--elf-stt-common # Generate ELF common symbols with STT_COMMON type
--sectname-subst # Enable section name substitution sequences
--gen-debug(-g) # Generate debugging information
--gstabs # Generate STABS debugging information
# --gstabs+ # (breaks the nu parser) Generate STABS debug info with GNU extensions
--gdwarf-2 # Generate DWARF2 debugging information
--gdwarf-sections # Generate per-function section names for DWARF line information
--help # Show help message and exit
--target-help # Show target specific options
--mri(-M) # Assemble in MRI compatibility mode
--reduce-memory-overheads # Prefer smaller memory use
--statistics # Print various measured statistics from execution
--strip-local-absolute # Strip local absolute symbols
--traditional-format # Use same format as native assembler when possible
--version # Print assembler version number and exit
--no-warn(-W) # Suppress warnings
--warn # Dont suppress warnings
--fatal-warnings # Treat warnings as errors
--listing-lhs-width
--listing-lhs-width2
--listing-rhs-width
--listing-cont-lines
--32 # Generate 32 bits code
--64 # Generate 64 bits code
--x32 # Generate x32 code
...args
]
# Generate ELF common symbols with STT_COMMON type
export extern "as yes no" [
--alternate # Initially turn on alternate macro syntax
--nocompress-debug-sections # Dont compress DWARF debug sections
--execstack # Require executable stack for this object
--noexecstack # Dont require executable stack for this object
--elf-stt-common # Generate ELF common symbols with STT_COMMON type
--sectname-subst # Enable section name substitution sequences
--gen-debug(-g) # Generate debugging information
--gstabs # Generate STABS debugging information
# --gstabs+ # (breaks the nu parser) Generate STABS debug info with GNU extensions
--gdwarf-2 # Generate DWARF2 debugging information
--gdwarf-sections # Generate per-function section names for DWARF line information
--help # Show help message and exit
--target-help # Show target specific options
--mri(-M) # Assemble in MRI compatibility mode
--reduce-memory-overheads # Prefer smaller memory use
--statistics # Print various measured statistics from execution
--strip-local-absolute # Strip local absolute symbols
--traditional-format # Use same format as native assembler when possible
--version # Print assembler version number and exit
--no-warn(-W) # Suppress warnings
--warn # Dont suppress warnings
--fatal-warnings # Treat warnings as errors
--listing-lhs-width
--listing-lhs-width2
--listing-rhs-width
--listing-cont-lines
--32 # Generate 32 bits code
--64 # Generate 64 bits code
--x32 # Generate x32 code
...args
]docs/nushell/nu_scripts/custom-completions/auto-generate/README.md:
# Note
Completions in this directory were generated via the auto-generate scripts.
These scripts are currently outdated and will not work in recent releases
of Nushell. The scripts themselves have been moved to `<repo_root>/need-update/custom-completions/auto-generate`
until they are updated.
Completions in this directory should still work in recent Nushell releases.
docs/nushell/nu_scripts/custom-completions/aws/aws-completions.nu:
def complete-commands [
context: string
] {
with-env {COMP_LINE: $context} {
aws_completer
| lines
| each {|x| $"($x) "}
}
}
export extern "aws" [
...command: string@complete-commands
]
docs/nushell/nu_scripts/custom-completions/aws/readme.md:
# Amazon Web Services CLI completions
A Nushell extern definition and completers for [Amazon Web Services CLI `aws`](https://aws.amazon.com/cli/).
This module utilizes the `aws_completer` binary that ships with `aws` to provide completions
## How to use
Add `source path/to/aws-completions.nu` to your `config.nu` (`$nu.config-path`).
docs/nushell/nu_scripts/custom-completions/bat/bat-completions.nu:
# A cat(1) clone with syntax highlighting and Git integration
export extern "bat" [
...file: path # file(s) to print / concatenate
--help # Print help (see a summary with '-h')
-h # Print help (see more with '--help')
--version # Print version
--show-all(-A) # Show non-printable chars like space, tab and \n
--nonprintable-notation: string@"nu-complete nonprintable-notation" # Set notation for non-printable characters
--plain(-p) # Only show plain style, no decorations
--language(-l): string@"nu-complete languages" # Explicitly set the language for syntax highlighting
--list-languages(-L) # Display a list of supported languages for syntax highlighting
--highlight-line: string # <N:M> Highlight from N to M line range with a different background color
--file-name: string # Specify the name to display for a file. Useful when piping data to bat from STDIN when bat does not otherwise know the filename
--diff(-d) # Only show lines that have been added/removed/modified with respect to the Git index. Checkout --diff-context=N
--diff-context: number # Include N lines of context around added/removed/modified lines when using '--diff'
--tabs: number # Set the tab width to T spaces. Use a width of 0 to pass tabs through directly
--wraps: string@"nu-complete wrap-modes" # Specify the text-wrapping mode
--chop-long-lines(-S) # Truncate all lines longer than screen width. Alias for '--wrap=never'
--terminal-width: number # Explicitly set the width of the terminal instead of determining it automatically
--number(-n) # Only show line numbers, no other decorations
--color: string@"nu-complete when" # Specify when to use colored output
--italic-text: string@"nu-complete ansi italics" # Specify when to use ANSI sequences for italic text in the output
--decorations: string@"nu-complete when" # Specify when to use the decorations that have been specified via '--style'
--force-colorization(-f) # Alias for '--decorations=always --color=always'
--paging: string@"nu-complete when" # Specify when to use the pager
--pager: string # Determine which pager is used
--map-syntax(-m): string # <glob:syntax> Map a glob pattern to an existing syntax name
--ignored-suffix: string # <ignored-suffix> Ignore extension. For example: 'bat --ignored-suffix ".dev" my_file.json.dev' will use JSON syntax, and ignore '.dev'
--theme: string@"nu-complete themes" # Set the theme for syntax highlighting
--list-themes # Display a list of supported themes for syntax highlighting
--style: string # Configure which elements (line numbers, file headers, grid borders, Git modifications, ..)
--line-range(-r): string # <N:M> Only print from N to M
--unbuffered(-u) # This option exists for POSIX-compliance reasons ('u' is for 'unbuffered'). The output is always unbuffered - this option is simply ignored
--diagnostic # Show diagnostic information for bug reports
--acknowledgements # Show acknowledgements
]
def "nu-complete nonprintable-notation" [] {
['unicode', 'caret']
}
def "nu-complete languages" [] {
^bat --list-languages
| lines
| parse "{value}:{description}"
}
def "nu-complete wrap-modes" [] {
['auto', 'never', 'character']
}
def "nu-complete when" [] {
['auto', 'never', 'always']
}
def "nu-complete ansi italics" [] {
['never', 'always']
}
def "nu-complete themes" [] {
^bat --list-themes
| lines
| parse "{value}"
}
docs/nushell/nu_scripts/custom-menus/README.md:
# Custom Menus Folder
In this folder you can find custom menus for nushell.
To make them work:
1. insert the code of chosen menu into the `menus` section of the `config.nu` file.
2. Check, that the name of the menu is unique
3. Add a shortcut to call for the needed menu in the `keybindings` section of the `config.nu` file
docs/nushell/nu_scripts/custom-menus/current_session_history_menu.nu:
# The part below should be pasted inside the 'menus' list of the 'config.nu' file
{
# session menu
name: current_session_history_menu
only_buffer_difference: false
marker: "# "
type: {
layout: list
page_size: 10
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
source: { |buffer, position|
history -l
| where session_id == (history session)
| select command
| where command =~ $buffer
| each { |it| {value: $it.command } }
| reverse
| uniq
}
# The part below should be pasted into the 'keybindgs' list of the 'config.nu' file
{
name: "current_session_history_menu"
modifier: alt
keycode: char_r
mode: emacs
event: { send: menu name: current_session_history_menu }
}docs/nushell/nu_scripts/custom-menus/extra/commands.nu:
# can be used in the REPL by adding something like the following block to `$env.config.keybindings` in your `config.nu`
# {
# name: commands_menu
# modifier: control
# keycode: char_t
# mode: [emacs, vi_normal, vi_insert]
# event: { send: menu name: commands_menu }
# }
{
name: commands_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
}
source: { |buffer, position|
scope commands
| where name =~ $buffer
| each { |it| {value: $it.name description: $it.description } }
}
}
docs/nushell/nu_scripts/custom-menus/extra/commands_with_description.nu:
# can be used in the REPL by adding something like the following block to `$env.config.keybindings` in your `config.nu`
# {
# name: commands_with_description_menu
# modifier: control
# keycode: char_s
# mode: [emacs, vi_normal, vi_insert]
# event: { send: menu name: commands_with_description }
# }
{
name: commands_with_description_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
}
source: { |buffer, position|
scope commands
| where name =~ $buffer
| each { |it| {value: $it.name description: $it.description} }
}
}
docs/nushell/nu_scripts/custom-menus/extra/vars.nu:
# can be used in the REPL by adding something like the following block to `$env.config.keybindings` in your `config.nu`
# {
# name: vars_menu
# modifier: alt
# keycode: char_o
# mode: [emacs, vi_normal, vi_insert]
# event: { send: menu name: vars_menu }
# }
{
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 { |it| {value: $it.name description: $it.type} }
}
}
docs/nushell/nu_scripts/custom-menus/fuzzy/README.md:
a few keybindings using the new `input list --fuzzy` command to interactively select what to do:
- `history.nu`: select a command from history
- `directories.nu`: select a directory, recursively from the current `PWD`
- `modules.nu`: recursively select and insert a `.nu` module from `NU_LIB_DIRS` inside a `use ...` command, ready to be run by pressing enter
docs/nushell/nu_scripts/custom-menus/fuzzy/directory.nu:
{
name: fuzzy_dir
modifier: control
keycode: char_s
mode: [emacs, vi_normal, vi_insert]
event: {
send: executehostcommand
cmd: "commandline edit --append (
ls **/*
| where type == dir
| get name
| input list --fuzzy
$'Please choose a (ansi magenta)directory(ansi reset) to (ansi cyan_underline)insert(ansi reset):'
)"
}
}
docs/nushell/nu_scripts/custom-menus/fuzzy/history.nu:
{
name: fuzzy_history
modifier: control
keycode: char_h
mode: [emacs, vi_normal, vi_insert]
event: {
send: executehostcommand
cmd: "commandline (
history
| each { |it| $it.command }
| uniq
| reverse
| input list --fuzzy
$'Please choose a (ansi magenta)command from history(ansi reset):'
)"
}
}
docs/nushell/nu_scripts/custom-menus/fuzzy/modules.nu:
{
name: fuzzy_module
modifier: control
keycode: char_u
mode: [emacs, vi_normal, vi_insert]
event: {
send: executehostcommand
cmd: '
commandline edit --replace "use "
commandline edit --insert (
$env.NU_LIB_DIRS
| each {|dir|
ls ($dir | path join "**" "*.nu")
| get name
| str replace $dir ""
| str trim -c "/"
}
| flatten
| input list --fuzzy
$"Please choose a (ansi magenta)module(ansi reset) to (ansi cyan_underline)load(ansi reset):"
)
'
}
}
docs/nushell/nu_scripts/custom-menus/zoxide-menu.nu:
def __zoxide_menu [] {
{
name: zoxide_menu
only_buffer_difference: true
marker: "| "
type: {
layout: columnar
page_size: 20
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
source: { |buffer, position|
zoxide query -ls $buffer
| parse -r '(?P<description>[0-9]+) (?P<value>.+)'
}
}
}
def __zoxide_keybinding [] {
{
name: zoxide_menu
modifier: control
keycode: char_o
mode: [emacs, vi_normal, vi_insert]
event: [
{ send: menu name: zoxide_menu }
]
}
}
def __edit_keybinding [] {
{
name: edit
modifier: alt
keycode: char_e
mode: [emacs, vi_normal, vi_insert]
event: [
{ send: OpenEditor }
]
}
}
export-env {
$env.config = ($env.config
| upsert menus ($env.config.menus | append (__zoxide_menu))
| upsert keybindings ($env.config.keybindings | append [(__zoxide_keybinding) (__edit_keybinding)])
)
}
docs/nushell/nu_scripts/example-config/config.nu:
# ~/.config/nushell/config.nu
#
# Nushell-s config file used in engine-q.
#
# It fetches all definitions and environment variables from the `init` module.
def build-config [] { { footer_mode: "50" } }
let config = build-config
use ~/.config/nushell/init.nu *
alias gd = git diff
docs/nushell/nu_scripts/example-config/init.nu:
# ~/.config/nushell/init.nu
#
# Init module that exports commands and environment variables wanted at startup
# commands
export def egd [...rest] {
with-env [GIT_EXTERNAL_DIFF 'difft'] { git diff $rest }
}
# we need to export the env we create with load-env
# because we are `use`-ing here and not `source`-ing this file
export-env {
load-env {
BROWSER: "firefox"
CARGO_TARGET_DIR: "~/.cargo/target"
EDITOR: "nvim"
VISUAL: "nvim"
PAGER: "less"
SHELL: "~/.cargo/bin/nu"
JULIA_NUM_THREADS: nproc
HOSTNAME: (hostname | split row '.' | first | str trim)
SHOW_USER: true
LS_COLORS: ([
"di=01;34;2;102;217;239"
"or=00;40;31"
"mi=00;40;31"
"ln=00;36"
"ex=00;32"
] | str join (char env_sep))
}
}
# prompt
PROMPT_COMMAND: { build-prompt }
export def build-prompt [] {
let usr_str = (if $env.SHOW_USER {
[
$env.USER
'@'
$env.HOSTNAME
':'
] | str join
} else {
''
})
let pwd_str = (if (pwd | str starts-with $env.HOME) {
(pwd | str replace $env.HOME '~' | str trim)
} else {
pwd
})
[ $usr_str $pwd_str ' ' ] | str join
}
docs/nushell/nu_scripts/modules/argx/mod.nu:
export def get-sign [cmd] {
let x = (scope commands | where name == $cmd).signatures?.0?.any?
mut s = []
mut n = {}
mut p = []
mut pr = []
mut r = []
for it in $x {
if $it.parameter_type == 'switch' {
if ($it.short_flag | is-not-empty) {
$s ++= [$it.short_flag]
}
if ($it.parameter_name | is-not-empty) {
$s ++= [$it.parameter_name]
}
} else if $it.parameter_type == 'named' {
if ($it.parameter_name | is-empty) {
$n = ($n | upsert $it.short_flag $it.short_flag)
} else if ($it.short_flag | is-empty) {
$n = ($n | upsert $it.parameter_name $it.parameter_name)
} else {
$n = ($n | upsert $it.short_flag $it.parameter_name)
}
} else if $it.parameter_type == 'positional' {
if $it.is_optional == false {
$p ++= [$it.parameter_name]
} else {
$pr ++= [$it.parameter_name]
}
} else if $it.parameter_type == 'rest' {
$r ++= [$it.parameter_name]
}
}
{ switch: $s, name: $n, positional: ($p ++ $pr), rest: $r }
}
# "test -h [123 (3213 3)] 123 `a sdf` --cd --ef sadf -g" | token
export def token [] {
let s = ($in | split row '' | slice 1..-2)
let s = if ($s | last) == ' ' { $s } else { $s | append ' ' }
mut par = []
mut res = []
mut cur = ''
mut esc = false
for c in $s {
if $c == '\' {
$esc = true
} else {
if $esc {
$cur ++= $c
$esc = false
} else {
if $c == ' ' and ($par | length) == 0 {
$res ++= [$cur]
$cur = ''
} else {
if $c in ['{' '[' '('] {
$par ++= [$c]
}
if $c in ['}' ']' ')'] {
$par = ($par | slice ..-2)
}
if $c in ['"' "'" '`'] {
if ($par | length) > 0 and ($par | last) == $c {
$par = ($par | slice ..-2)
} else {
$par ++= [$c]
}
}
$cur ++= $c
}
}
}
}
return $res
}
export def parse [] {
let token = ($in | token)
let sign = (get-sign $token.0)
mut sw = ''
mut pos = []
mut opt = {}
for c in $token {
if ($sw | is-empty) {
if ($c | str starts-with '-') {
let c = if ($c | str substring 1..<2) != '-' {
let k = ($c | str substring 1..)
if $k in $sign.name {
$'($sign.name | get $k)'
} else {
$k
}
} else {
$c | str substring 2..
}
if $c in $sign.switch {
$opt = ($opt | upsert $c true)
} else {
$sw = $c
}
} else {
$pos ++= [$c]
}
} else {
$opt = ($opt | upsert $sw $c)
$sw = ''
}
}
$opt._args = $pos
let p = $pos | slice 1..($sign.positional | length)
let rest = $pos | slice (($sign.positional | length) + 1)..-1
$opt._pos = ( $p | enumerate
| reduce -f {} {|it, acc|
$acc | upsert ($sign.positional | get $it.index) $it.item
} )
if ($sign.rest | length) > 0 {
$opt._pos = ($opt._pos | upsert $sign.rest.0 $rest)
}
$opt
}
# def test [a b x? ...y --cd(-c) --ef(-e):string -g -h:int --ij --lm:bool] {}
# (scope commands | where name == test).signatures?.0?.any?
# get-sign test | to yaml
# "test -h 111 123 'asdf' --cd --ef sadf -g -h 222" | cmd parse | to yaml
docs/nushell/nu_scripts/modules/aws/select-aws-profile.nu:
# This alias lets you choose your aws environment variables with ease.
#
# Dependencies:
# * fzf
#
# Installation:
# 1. store in ~/.config/nushell/select-aws-profile.nu
# 2. add to your config.nu: `use ~/.config/nushell/select-aws-profile.nu *`
#
# Usage:
# select-aws-profile
export def --env main [] {
hide AWS_REGION
do {
let creds = open ($env.HOME + "/.aws/credentials") | from toml
let selected_profile = $creds
| transpose name creds
| get name
| str join "\n"
| fzf
if $selected_profile != "" {
let out = {
AWS_PROFILE: $selected_profile,
AWS_ACCESS_KEY_ID: ($creds | get $selected_profile | get "aws_access_key_id"),
AWS_SECRET_ACCESS_KEY: ($creds | get $selected_profile | get "aws_secret_access_key"),
}
let region = ($creds | get $selected_profile | get -o "region")
if $region != null {
$out | insert AWS_REGION $region
} else {
$out
}
}
} | load-env
{
AWS_PROFILE: $env.AWS_PROFILE,
AWS_ACCESS_KEY_ID: $env.AWS_ACCESS_KEY_ID,
AWS_SECRET_ACCESS_KEY: $env.AWS_SECRET_ACCESS_KEY,
AWS_REGION: $env.AWS_REGION
}
}
docs/nushell/nu_scripts/modules/background_task/README.md:
# Background tasks with pueue
Makes Nushell "support" background tasks.
Note: Nushell has [native background jobs](https://www.nushell.sh/book/background_jobs.html) support through the [`job`](https://www.nushell.sh/commands/docs/job.html) command.
The `task` commands introduced here spawn new and independent processes rather than background threads like `job`.
The `task` processes will keep running even when you exit the current Nushell process, the `job` background threads will not.
## Prerequisite
Install [pueue](https://github.com/Nukesor/pueue) and make sure `pueued` is running and that `pueue` is in `PATH`.
## Usage
You will get tab completions and suggestions when you install the module.
Please check those.
To install the module, copy the `task.nu` to the `$env.NU_LIB_DIRS` directory, then do:
```nu
use task.nuIn your Nushell config under ~/.config/nushell.
You can use environment variables, since they are inherited from the parent when spawning a process.
$env.FOO = 123
task spawn {
echo $env.FOO
}If you want to pass serialized data, you can do this:
let foo = { a: 1 b: 2 c: 3 }
with-env { FOO: ($foo | to json) } {
task spawn {
let foo = ($env.FOO | from json)
echo $foo
}
}You can define these commands in a separate module, like so:
# --- in foo.nu ---
export def bar [] { echo bar }
# --- in main.nu ---
task spawn {
use foo.nu
foo bar
}- On some setups (e.g. NixOS with
nuinstalled as a binary in user's$HOME),sh(whichpueuedelegates tasks to run) might fail to findnuin the$PATH. In this case hard-coding the location of your nu binary in thetask spawnfunction definition intask.nucan solve the issue.
`docs/nushell/nu_scripts/modules/background_task/task.nu`:
```nu
# Spawn a task to run in the background, even when the shell is closed.
#
# Note that a fresh Nushell interpreter is spawned to execute the
# given task, so it won't inherit the current scope's variables,
# custom commands and alias definitions.
#
# It will only inherit environment variables which can be converted to a string.
#
# Note that the closure can't take arguments.
#
# Example usage: task spawn { echo "Hello, World!" }
export def spawn [
command: closure # The closure to run.
--working-directory (-w): directory # Specify the working directory the task will be run in.
--immediate (-i) # Immediately start the task.
--stashed (-s) # Create the task in Stashed state. Useful to avoid immediate execution if the queue is empty
--delay (-d): duration # Queue the task for execution only after the duration.
--group (-g): string # The group to spawn the task under.
--after (-a): int # Start the task once all specified tasks have successfully finished. As soon as one of the dependencies fails, this task will fail as well.
--priority (-o): string # Start this task with a higher priority. The higher the number, the faster it will be processed.
--label (-l): string # Label the task. This string will be shown in the `status` column of `task status`.
]: nothing -> int {
mut args = []
if $working_directory != null {
$args = ($args | prepend ["--working-directory", $working_directory])
}
if $immediate {
$args = ($args | prepend "--immediate")
}
if $stashed {
$args = ($args | prepend "--stashed")
}
if $delay != null {
$args = ($args | prepend ["--delay" ($delay | format duration sec | parse "{secs} {_}" | get 0.secs)])
}
if $group != null {
$args = ($args | prepend ["--group" $group])
}
if $after != null {
$args = ($args | prepend ["--after" $after])
}
if $priority != null {
$args = ($args | prepend ["--priority" $priority])
}
if $label != null {
$args = ($args | prepend ["--label" $label])
}
let source_path = mktemp --tmpdir --suffix "-nu-task"
(
view source $command
| str trim --left --char "{"
| str trim --right --char "}"
)
| save --force $source_path
(pueue add --print-task-id ...$args $"nu --config '($nu.config-path)' --env-config '($nu.env-path)' ($source_path)")
}
# Remove tasks from the queue.
# Running or paused tasks need to be killed first.
export def remove [
...ids: int # IDs of the tasks to remove from the status list.
] {
pueue remove ...$ids
}
# Switches the queue position of two tasks.
# Only works for queued or stashed tasks.
export def switch [
task_id_1: int # The first task ID.
task_id_2: int # The second task ID.
] {
pueue switch $task_id_1 $task_id_2
}
# Stash a task that is not currently running.
#
# Stashed tasks won't be automatically started.
# You will have to queue them or start them by hand.
export def stash [
...ids: int # IDs of the tasks to stash.
] {
pueue stash ...$ids
}
# Queue stashed tasks for execution.
export def queue [
...ids: int # IDs of the tasks to queue.
--delay (-d): duration # Queue only after the specified delay.
] {
let args = if $delay != null {
["--delay" ($delay | format duration sec | parse '{secs} {_}' | get 0.secs)]
} else {
[]
}
pueue enqueue ...$args ...$ids
}
# Resume operation of specific tasks or groups of tasks.
#
# By default, this resumes the default group and all its tasks.
# It can also be used force-start specific tasks or start whole groups.
export def start [
...ids: int # IDs of the tasks to start. By default all the tasks in the default group will be started.
--group (-g): string # Resume a specific group and all paused tasks in it. The group will be set to running and its paused tasks will be resumed.
--all (-a) # Resume all groups. All groups will be set to running and paused tasks will be resumed.
] {
mut args = []
if $group != null {
$args = ($args | prepend ["--group" $group])
}
if $all {
$args = ($args | prepend "--all")
}
pueue start ...$args
}
# Restart failed or successful task(s).
#
# By default, identical tasks will be created and
# enqueued, but it's possible to restart in-place.
#
# You can also edit a few properties, such as
# the path and the command of the task, before restarting.
export def restart [
...ids: int # IDs of the tasks to restart.
--all-failed (-a) # Restart all failed tasks across all groups. Nice to use in combination with `--in-place/i`.
--failed-in-group (-g): string # Like `--all-failed`, but only restart tasks failed tasks of a specific group. The group will be set to running and its paused tasks will be resumed.
--start-immediately (-k) # Immediately start the tasks, no matter how many open slots there are. This will ignore any dependencies tasks may have.
--stashed (-s) # Set the restarted task to a "Stashed" state. Useful to avoid immediate execution.
--in-place (-i) # Restart the tasks by reusing the already existing tasks.
--not-in-place (-n) # Opposite of `--in-place`. This is already the default unless you have `restart_in_place` set to true.
--edit (-e) # Edit the tasks' commands before restarting
--edit-path (-p) # Edit the tasks' paths before restarting
--edit-label (-l) # Edit the tasks' labels before restarting
] {
mut args = []
if $all_failed {
$args = ($args | prepend "--all-failed")
}
if $failed_in_group != null {
$args = ($args | prepend "--failed-in-group")
}
if $start_immediately {
$args = ($args | prepend "--start-immediately")
}
if $stashed {
$args = ($args | prepend "--stashed")
}
if $in_place {
$args = ($args | prepend "--in-place")
}
if $not_in_place {
$args = ($args | prepend "--not-in-place")
}
if $edit {
$args = ($args | prepend "--edit")
}
if $edit_path {
$args = ($args | prepend "--edit-path")
}
if $edit_label {
$args = ($args | prepend "--edit-label")
}
pueue restart ...$args ...$ids
}
# Either pause a running tasks or a specific groups of tasks.
#
# By default, pauses the default group and all its tasks.
#
# A paused group won't start any new tasks automatically.
export def pause [
...ids: int # IDs of the tasks to pause.
--group (-g): string # Pause a specific group
--all (-a) # Pause all groups.
--wait (-w) # Only pause the specified group and let already running tasks finish by themselves
] {
mut args = []
if $group != null {
$args = ($args | prepend ["--group" $group])
}
if $all {
$args = ($args | prepend "--all")
}
if $wait {
$args = ($args | prepend "--wait")
}
pueue pause ...$args ...$ids
}
# Kill specific running tasks or whole task groups.
#
# Kills all tasks of the default group when no ids or a specific group are provided.
export def kill [
...ids: int # IDs of the tasks to kill.
--group (-g): string # Kill all running tasks in a group. This also pauses the group.
--all (-a) # Kill all running tasks across ALL groups. This also pauses all groups.
--signal (-s): string # Send a UNIX signal instead of simply killing the process. DISCLAIMER: This bypasses Pueue's process handling logic! You might enter weird invalid states, use at your own descretion.
] {
mut args = []
if $group != null {
$args = ($args | prepend ["--group" $group])
}
if $all {
$args = ($args | prepend "--all")
}
if $signal != null {
$args = ($args | prepend ["--signal" $signal])
}
pueue kill ...$args ...$ids
}
# Send something to a task. Useful for sending confirmations such as "y\n".
export def send [
id: int # ID of the task to send something to.
input: string # The input that should be sent to the process.
] {
pueue send $id $input
}
# Edit the command, path or label of a stashed or queued task.
#
# By default only the command is edited.
#
# Multiple properties can be added in one go.
export def edit [
id: int # ID of the task to edit.
--command (-c) # Edit the task's command
--path (-p) # Edit the task's path
--label (-l) # Edit the task's label
] {
mut args = []
if $command {
$args = ($args | prepend "--command")
}
if $path {
$args = ($args | prepend "--path")
}
if $label {
$args = ($args | prepend "--label")
}
pueue edit ...$args $id
}
# Use this to add or remove groups.
#
# By default, this will simply display all known groups.
export def group [] {
pueue group --json | from json
}
# Create a new group with a name.
export def "group add" [
name: string # The name of the new group.
--parallel (-p): int # The amount of parallel tasks the group can run at one time.
] {
let args = if $parallel != null {
["--parallel" $parallel]
} else {
[]
}
pueue group add ...$args $name
}
# Remove a group with a name.
export def "group remove" [
name: string # The name of the group to be removed.
] {
pueue group remove $name
}
# Display the current status of all tasks.
export def status [
--detailed (-d) # Return a table with more detailed information.
] {
let output = (
pueue status --json
| from json
| get tasks
| transpose --ignore-titles status
| flatten
)
# TODO: Rename the Done column to done.
if not $detailed {
$output | select id label group Done? status? start? end?
} else {
$output
}
}
# Display the output of tasks.
#
# Only the last few lines will be shown by default for multiple tasks.
# If you want to follow the output, use `--tail/-t`.
export def log [
...ids: int # The tasks to check the outputs of.
--last (-l): int # Only print the last N lines of each task's output. This is done by default if you're looking at multiple tasks.
--tail (-t) # Follow the output as it is printing. Only works with 1 task. When used in conjunction with `--last`, the last N lines will be printed before starting to wait for output.
--detailed (-d) # Include all fields, don't simplify output.
] {
def process_raw [raw: string] {
let full = (
$raw
| from json
| transpose -i info
| flatten --all
| flatten --all
)
if $detailed {
$full
} else {
$full | select id label group Done? status? start? end?
}
}
if (($ids | length) == 1) {
if $tail {
let args = if $last != null {
["--lines" $last]
} else {
[]
}
pueue follow ...$ids
} else {
let args = if $last != null {
["--lines" $last]
} else {
[]
}
process_raw (pueue log --full --json ...$args ...$ids)
| first
}
} else {
if $tail {
echo $"(ansi red)--tail can only be used with one task.(ansi reset)"
return
}
let args = if $last != null {
["--lines" $last]
} else {
[]
}
process_raw (pueue log --full --json ...$args ...$ids)
}
}
# Wait until the provided tasks are finished.
#
# This is like join() or await in many languages.
export def wait [
...ids: int # IDs of the tasks to wait for.
--group (-g): string # Wait for all tasks in a specific group.
--all (-a) # Wait for all tasks across all groups and the default group.
--quiet (-q) # Don't show any log output while waiting.
--status (-s): string # Wait for tasks to reach a specific task status.
] {
mut args = []
if $group != null {
$args = ($args | prepend ["--group" $group])
}
if $all {
$args = ($args | prepend $all)
}
if $quiet {
$args = ($args | prepend $quiet)
}
if $status != null {
$args = ($args | prepend ["--status" $status])
}
pueue wait ...$args ...$ids
}
# Remove tasks from the status list.
export def clean [
--successful-only (-s) # Only clean tasks that finished successfully
--group (-g): string # Only clean tasks of a specific group
] {
mut args = []
if $successful_only {
$args = ($args | prepend "--successful-only")
}
if $group != null {
$args = ($args | prepend ["--group" $group])
}
pueue clean ...$args
}
# Shutdown pueue and thus this module.
export def shutdown [] {
pueue shutdown
}
# Set the maximum parallel tasks for a group.
#
# Note that no tasks will be stopped if the number is lowered.
# The limit only applies when schelduing.
export def set-parallel-limit [
max: int # The maximum parallel tasks allowed for a group when schelduing.
--group (-g): string # The group to set the limit for. By default this is `default`.
] {
let args = if $group != null {
["--group" $group]
} else {
[]
}
pueue parallel ...$args $max
}
const HERE = (path self)
export def main [] {
let mod_name = $HERE | path basename | str replace -r '\.nu$' ''
scope commands | where name =~ $"^($mod_name) " | select name description | transpose -rd
}
docs/nushell/nu_scripts/modules/capture-foreign-env/README.md:
# capture-foreign-env
This is a modified version of the [capture-foreign-env command from the nushell wiki](https://www.nushell.sh/cookbook/foreign_shell_scripts.html#detailed-explanation-of-capture-foreign-env).
Unlike the wiki version It relies on `null_byte` instead of `newline` as a delimiter for environment variables dumped by the `env` command which allows it to support multi-line foreign environment variables.
docs/nushell/nu_scripts/modules/capture-foreign-env/mod.nu:
# Returns a record of changed env variables after running a non-nushell script's contents (passed via stdin), e.g. a bash script you want to "source"
export def main [
--shell (-s): string = /bin/sh
# The shell to run the script in
# (has to support '-c' argument and POSIX 'env', 'echo', 'eval' commands)
--arguments (-a): list<string> = []
# Additional command line arguments to pass to the foreign shell
] {
let script_contents = $in;
let env_out = with-env { SCRIPT_TO_SOURCE: $script_contents } {
^$shell ...$arguments -c `
env -0
echo -n '<ENV_CAPTURE_EVAL_FENCE>'
eval "$SCRIPT_TO_SOURCE"
echo -n '<ENV_CAPTURE_EVAL_FENCE>'
env -0 -u _ -u _AST_FEATURES -u SHLVL`
}
| split row '<ENV_CAPTURE_EVAL_FENCE>'
| {
before: ($in | first | str trim --char (char nul) | split row (char nul))
after: ($in | last | str trim --char (char nul) | split row (char nul))
}
$env_out.after
| where { |line| $line not-in $env_out.before }
| parse "{key}={value}"
| transpose --header-row --as-record
}
docs/nushell/nu_scripts/modules/clone-all/README.md:
# Clone all
Do you want to automate cloning a list repos into a folder? This script is for you!
## Requirements:
- [`gh` cli](https://github.com/cli/cli)
## How to use it
Load the script:
- if you have cloned the repo before:
```nushell env.nu
source ~/your/directory/to/nu_scripts/modules/clone-all/clone-all.nuor if you have the file, and you want it to use in a nushell session:
use clone-all.nu *
# and it's ready to use in the current prompt!Then, create a list of github routes to repositories ORGANIZATION_NAME/REPO
like this:
let list_of_repos = [
"nushell/nushell"
"nushell/nu_scripts"
"nushell/vscode-nushell-lang"
]And then you need to pass that variable and a destination folder
clone all $list_of_repos $"($env.home)/other-repos/nu_repos"I (@AucaCoyan) use it for cloning both org repos and my forks
let nushell_repos = [
"nushell/nushell"
"nushell/nu_scripts"
"nushell/vscode-nushell-lang"
]
clone all $nushell_repos $"($env.home)/other-repos/nu"
let nushell_forks = [
"AucaCoyan/nushell"
"AucaCoyan/nu_scripts"
"AucaCoyan/vscode-nushell-lang"
]
clone all $nushell_forks $"($env.home)/repos"and do that with every gh org (work or open source!)
`docs/nushell/nu_scripts/modules/clone-all/clone-all.nu`:
```nu
# grabs the repo name of a github (ORG/repo) string
#
# for example
# grab repo name "organization/my_special_repo"
# returns "myspecial_repo"
# Grabs the repo name of a github (ORG/repo) string
def "grab repo name" [ghrepo: string]: [string -> string] {
$ghrepo | split column "/" | get column2 | last
}
# Generic fn to clone all repos of one organization into a specific folder
#
# # Parameters
# `list_of_repos` is a list of <ORG/REPO> from github
# for example:
# ```nu
# let list_of_repos = [
# "nushell/nushell"
# "nushell/nu_scripts"
# "nushell/vscode-nushell-lang"
# ]
#
# and destination is the location where those repos are cloned
# $ use clone-all.nu *
# $ clone all ['nushell/nu_scripts'] /home/my-dir/
# equals
# gh repo clone nushell/nu_scripts /home/my-dir/nu_scripts
# (note that it doesn't create the organization folder)
# Clones all the `list_of_repos` into `destination` folder
export def "clone all" [list_of_repos: list<string>, destination: path] {
print $" creating ($destination) folder"
mkdir $destination
for $repo in $list_of_repos {
let repo_name = grab repo name $repo
let single_repo_dir = $"($destination)/($repo_name)"
if ($single_repo_dir | path exists) {
print $"\n repo ($single_repo_dir) exists, skipping"
continue
} else {
print $"\n cloning ($repo)"
gh repo clone $repo $single_repo_dir
}
}
}
docs/nushell/nu_scripts/nu-hooks/nu-hooks/direnv/config.nu:
# you can use the following closure in your config in a few different ways, e.g.
#
# ```nushell
# $env.config.hooks.env_change.PWD = (
# $env.config.hooks.env_change.PWD | append (source nu-hooks/nu-hooks/direnv/config.nu)
# )
# ```
#
# or
#
# ```nushell
# $env.config.hooks.pre_prompt = (
# $env.config.hooks.pre_prompt | append (source nu-hooks/nu-hooks/direnv/config.nu)
# )
# ```
#
# > :bulb: **Note**
# > the former will update direnv when you enter and leave a directory whereas the later will update
# > on every new prompt, i.e. much more often.
{ ||
if (which direnv | is-empty) {
return
}
direnv export json | from json | default {} | load-env
# Direnv outputs $PATH as a string, but nushell silently breaks if isn't a list-like table.
# The following behemoth of Nu code turns this into nu's format while following the standards of how to handle quotes, use it if you need quote handling instead of the line below it:
# $env.PATH = $env.PATH | parse --regex ('' + `((?:(?:"(?:(?:\\[\\"])|.)*?")|(?:'.*?')|[^` + (char env_sep) + `]*)*)`) | each {|x| $x.capture0 | parse --regex `(?:"((?:(?:\\"|.))*?)")|(?:'(.*?)')|([^'"]*)` | each {|y| if ($y.capture0 != "") { $y.capture0 | str replace -ar `\\([\\"])` `$1` } else if ($y.capture1 != "") { $y.capture1 } else $y.capture2 } | str join }
$env.PATH = $env.PATH | split row (char env_sep)
}
docs/nushell/nu_scripts/nu-hooks/nu-hooks/direnv/direnv.nu:
def direnv [] {
[
{
condition: {|before, after| ($before != $after) and ($after | path join .env.yaml | path exists) }
code: "
open .env.yaml | load-env
"
}
{
condition: {|before, after| ($before != $after) and ($after | path join '.env' | path exists) }
code: "
open .env
| lines
| parse -r '(?P<k>.+?)=(?P<v>.+)'
| reduce -f {} {|x, acc| $acc | upsert $x.k $x.v}
| load-env
"
}
]
}
export-env {
$env.config = ( $env.config | upsert hooks.env_change.PWD { |config|
let o = ($config | get -o hooks.env_change.PWD)
let val = (direnv)
if $o == null {
$val
} else {
$o | append $val
}
})
}
docs/nushell/nu_scripts/nu-hooks/nu-hooks/dynamic-load/dynamic-load.nu:
def dynamic_load [] {
[
{
condition: {|before, after| (not ('.nu' in (overlay list))) and ('~/.nu' | path exists) }
code: "overlay use ~/.nu as .nu"
}
]
}
export-env {
$env.config = ( $env.config | upsert hooks.env_change.PWD { |config|
let o = ($config | get -o hooks.env_change.PWD)
let val = (dynamic_load)
if $o == null {
$val
} else {
$o | append $val
}
})
}
# const path = "~/.nu"
# const if ($path| path expand | path exists) {
# source $path
# }
docs/nushell/nu_scripts/nu-hooks/nu-hooks/filesystem/autojump.nu:
# This file configures autojump (https://github.com/wting/autojump) for nushell
#
# Dependencies
# * autojump
#
# Installation
# 1. store in ~/.config/nushell/autojump.nu
# 2. add to your config.nu: `source .config/nushell/autojump.nu`
#
# Usage
# Run `j` to jump around
def autojump_add_to_database [dir] {
$env.AUTOJUMP_SOURCED = 1
autojump --add $dir
}
def --env j [...dir] {
$env.AUTOJUMP_SOURCED = 1
cd (autojump ...$dir)
}
$env.config = ($env.config | upsert hooks.env_change.PWD {|config|
let val = ($config | get -o hooks.env_change.PWD)
if $val == null {
$val | append {|before, after| autojump_add_to_database $after }
} else {
[
{|before, after| autojump_add_to_database $after }
]
}
})
docs/nushell/nu_scripts/sourced/api_wrappers/wolframalpha.nu:
let appID = $env.WOLFRAMALPHA_APPID
#Fetch simple answer from WolframAlpha API
def wolfram [...query
] {
let query_string = ($query | str join " ")
let result = (http get ("https://api.wolframalpha.com/v1/result?" + ([[key value]; [appid $appID] [i $query_string]] | url build-query)))
$result + ""
}
#Fetch image with full answer from WolframAlpha API
def wolframimg [...query
] {
let query_string = ($query | str join " ")
let filename = ($query_string + ".png")
let link = ("https://api.wolframalpha.com/v1/simple?" + ([[key value]; [appid $appID] [i $query_string]] | url build-query) + "&background=F5F5F5&fontsize=20")
http get $link | save $filename
echo ("Query result saved in file: " + $filename)
}
docs/nushell/nu_scripts/sourced/cool-oneliners/README.md:
# cool oneliners
Capturing oneliners to and from the nushell community.
Consider these a living library.
Or an ongoing story of how people actually use `nu`.
## Naming and script requirements
- the filename should be an abbreviation of the general topic of the script
For example:
```sh
git_batch_extract.nu- the script should have two lines
- the first line should be a comment describing the script's purpose
- the second line should be the cool oneliner
`docs/nushell/nu_scripts/sourced/cool-oneliners/assets/js_map.json`:
```json
[
["cero", "zero"],
["uno", "one"],
["dos", "two"],
["tres", "three"]
]
docs/nushell/nu_scripts/sourced/cool-oneliners/cargo_search.nu:
def "cargo search" [ query: string, --limit=10] {
^cargo search $query --limit $limit
| lines
| each {
|line| if ($line | str contains "#") {
$line | parse --regex '(?P<name>.+) = "(?P<version>.+)" +# (?P<description>.+)'
} else {
$line | parse --regex '(?P<name>.+) = "(?P<version>.+)"'
}
}
| flatten
}docs/nushell/nu_scripts/sourced/cool-oneliners/cdpath-implementation.nu:
#!/usr/bin/nu
# I actually use it as a part of my startup, so I am not really sure how to pack it, yet I wouldd like to contribute
#-------------------------------------------------------------------------------------------------------------------------------
#
# How to use?
#-------------------------------------------------
#1) Add desired paths to the cdpath variable
#2) Use in your shell: $c [directory]
#2.5) You *have to* use an argument. If you wish to simply $cd, use $cd command.
#3) If the path exists, you will cd into the first match found (the command is iterating over the list in the correct order,
# i.e. first element is being iterated overin the first place)
#3.5) But if path does not exist, you will receive a proper echo.
#-----------------------------------------------------------------------------------------------------------------------------------
#
#Written by skelly37
#------------------------
# startup = [
# "let cdpath = [. /place/your ~/cdpath/here ]",
# "def c [dir] { let wd = (pwd); for element in $cdpath {if (pwd) == $wd {cd $element; for directory in (ls -a | select name type | each { if $it.type == Dir {echo $it.name} {} } ) {if $dir == $directory {cd $dir} {}}; if (pwd) == $element {cd $wd} {}} {}}; if (pwd) == $wd {cd $wd; echo \"No such path!\"} {}}",
# ]
#
export def --env c [dir] {
let CD_PATH = [. ($env.NU_PLUGIN_DIRS | get 0) $nu.default-config-dir ]
let wd = (pwd);
for element in $CD_PATH {
let element = ($element | path expand)
if (pwd) == $wd {
cd $element;
for directory in (ls -a | where type == dir | get name) {
if $dir == $directory {
cd $dir
break
}
};
if (pwd) == $element {
cd $wd
}
}
};
if (pwd) == $wd {
cd $wd
print "No such path!"
}
}
docs/nushell/nu_scripts/sourced/cool-oneliners/clear_screen_buf.nu:
### This clears out your screen buffer on a default mac terminal
### currently there is no way to do that in nushell
def cls [] {
ansi cls
ansi clsb
ansi home
}
docs/nushell/nu_scripts/sourced/cool-oneliners/dict.nu:
# Function querying free online English dictionary API for definition of given word(s)
def dict [...word #word(s) to query the dictionary API but they have to make sense together like "martial law", not "cats dogs"
] {
let query = ($word | str join %20)
let link = ('https://api.dictionaryapi.dev/api/v2/entries/en/' + ($query|str replace ' ' '%20'))
let output = (http get $link | rename word)
let w = ($output.word | first)
if $w == "No Definitions Found" {
echo $output.word
} else {
echo $output
| get meanings
| flatten
| select partOfSpeech definitions
| flatten
| flatten
| reject "synonyms"
| reject "antonyms"
}
}
docs/nushell/nu_scripts/sourced/cool-oneliners/file_cat.nu:
# Combine two files into one
def create_files [] {
[0,1,2,3] | slice 0..3 | save a.json
[4,5,6,7] | slice 0..3 | save b.json
}
create_files
echo (open a.json) (open b.json) | save c.json
open c.json | flatten
rm a.json b.json c.json
docs/nushell/nu_scripts/sourced/cool-oneliners/file_convert_naming_case.nu:
# rename any SQL files in current directory from hello_file.sql to hello-file.sql
ls ./*.sql | each { |f|
let ext = (echo $f.name | path parse | get extension);
let cur_stem = (echo $f.name | path parse | get stem);
let new_name = (build-string (echo $cur_stem | str kebab-case) "." $ext)
mv $f.name $new_name
}
docs/nushell/nu_scripts/sourced/cool-oneliners/filesize.nu:
## show directory sizes in current directory starting from the largest
ls -d|where type == dir|sort-by size|reverse|format filesize GB size
docs/nushell/nu_scripts/sourced/cool-oneliners/find_in.nu:
# Search terms in the specified files and/or folders based on the glob pattern provided.
def "find in" [
glob: glob, # The glob expression
--exclude (-e): list<string> # Patterns to exclude from the search: `find in` will not walk the inside of directories matching the excluded patterns.
...rest: any # Terms to find
]: nothing -> table<path: string, line: int, data: string> {
glob --exclude $exclude --no-dir $glob
| par-each {|e|
open --raw $e | lines | enumerate | rename line data |
find --columns [data] ...$rest |
each {|match| {path: ($e | path relative-to $env.PWD), ...$match}}
} | flatten
}
docs/nushell/nu_scripts/sourced/cool-oneliners/git_gone.nu:
# gently try to delete merged branches, excluding the checked out one
git branch --merged | lines | where $it !~ '\*' | str trim | where $it != 'master' and $it != 'main' | each { |it| git branch -d $it }
docs/nushell/nu_scripts/sourced/cool-oneliners/js_map_to_markdown.nu:
# Ingest JavaScript Map JSON into nu then to markdown
open assets/js_map.json | each { echo [[Español English]; [ $in.0 $in.1]] } | flatten | to md
docs/nushell/nu_scripts/sourced/cool-oneliners/npm_update_versions.nu:
# Increment the minor version for any package.json in the current directory with `nu_plugin_inc` (eigenrick — 08/16/2020)
ls -f */package.json | each {|it| open $it.name | inc version --minor | to json --indent 2 | save --raw --force $it.name }
docs/nushell/nu_scripts/sourced/cool-oneliners/parse_aws_s3_ls.nu:
# transform the aligned text output of aws s3 ls into something useful
# presumes you have the aws CLI
aws s3 ls s3://your-bucket-and-path | lines | each { str replace ' ' ' ' | str replace ' ' ' ' } | split column ' '
docs/nushell/nu_scripts/sourced/cool-oneliners/parse_fish_command_history.nu:
open ~/.config/fish/fish_history | from yaml | get cmd | find --regex '^git .*' | split column ' ' command subcommanddocs/nushell/nu_scripts/sourced/cool-oneliners/pwd-short.nu:
# Print working directory but abbreviates the home dir as ~
def pwd-short [] {
$env.PWD | str replace $nu.home-path '~'
}
docs/nushell/nu_scripts/sourced/cool-oneliners/simple_http_request.nu:
# Makes an http request to a URL and gets the "value" field off the JSON response.
(http get https://api.chucknorris.io/jokes/random).value
docs/nushell/nu_scripts/sourced/cool-oneliners/top_commands.nu:
# Evaluates the top 5 most used commands present in `nushell/history.txt`.
if $nu.history-enabled { open $nu.history-path | lines | uniq --count | sort-by --reverse count | first 5 | rename command amount } else { print --stderr "History is disabled!" }
docs/nushell/nu_scripts/sourced/cool-oneliners/wt_color.nu:
# Simple script that sets color of a windows terminal (https://github.com/microsoft/terminal).
def set-tab-color [idx: int] { ansi -e ( ["2;15;", ($idx | into string), (",|") ] | str join ) }
docs/nushell/nu_scripts/sourced/cool-oneliners/xml_search_schema.nu:
# Search the WordprocessingML XML Schema definition file for a simple type by name
# You'll need the wml.xsd file.
# To get that file, first download the following zip:
# https://www.ecma-international.org/wp-content/uploads/ECMA-376-Fifth-Edition-Part-1-Fundamentals-And-Markup-Language-Reference.zip
# Then, unzip the contents of OfficeOpenXML-XMLSchema-Strict.zip.
open wml.xsd | from xml | get content | where tag == simpleType | flatten | where name =~ BrType | get content.content.0.attributes
docs/nushell/nu_scripts/sourced/examples/date_in_local_timezones.nu:
def "nu core-team" [] {
[
[ 'name', 'tz'];
[ 'andres', 'America/Guayaquil']
[ 'fdncred', 'US/Central']
[ 'gedge', 'US/Eastern']
[ 'jt', 'NZ']
[ 'wycats', 'US/Pacific']
[ 'kubouch', 'Europe/Helsinki']
['elferherrera', 'Europe/London']
]
}
def "date local" [now] {
insert time {|value|
let converted = ($now | date to-timezone $value.tz);
$converted | format date '%c'
}
}
let next_call = ("2021-08-31 15:00:21.290597200 -05:00" | into datetime);
let people = (nu core-team | date local $next_call);
def say [closure] {
$in | each {|person|
do $closure (
$person | update name {|row| $row.name | str capitalize}
)
} | str join (char newline)
}
print ($people | say {|person| $"($person.name)'s timezone is ($person.tz)"})
print ($"
for the next call happening on ($next_call | format date '%c').. in their timezones they would be:
")
print ($people | say {|person| $"($person.name)'s: ($person.time)"})
docs/nushell/nu_scripts/sourced/examples/netstat.nu:
let ns = (netstat | lines | skip 1)
let first_batch = ($ns | take until {|it| $it =~ Active } | str join (char nl) | from ssv -m 1)
let second_batch = ($ns |
skip until {|it| $it =~ Active } |
skip 1 |
str join (char nl) |
str replace '\[ \]' "[]" --all |
from ssv -m 1 |
default I-Node "" |
default Path ""
| each {|row| if $row.Type == DGRAM {
$row | update Path { get I-Node } | update I-Node { get State } | update State ""
} else {
$row
}
}
)
print $first_batch
print $second_batch
docs/nushell/nu_scripts/sourced/fun/website_builder.nu:
# Converts markdown into their equivalent html pages
let $markdown_files = (ls **/*.md)
for $markdown in $markdown_files {
let $contents = (open --raw $markdown.name)
let $content_lines = ($contents | lines)
let $first_line = ($content_lines | first | str trim)
if $first_line == "---" {
let $header = ($content_lines
| skip 1
| take while {|x| ($x | str trim) != "---"}
| str join "\n"
| from yaml)
let $post = ($content_lines
| skip 1
| skip while {|x| ($x | str trim) != "---"}
| skip 1)
print $header
let $html_post = ($post
| each {|line|
if ($line | str starts-with "#") {
let $line = ($line | str replace "^# (.*)$" "<h1>$1</h1>")
let $line = ($line | str replace "^## (.*)$" "<h2>$1</h2>")
let $line = ($line | str replace "^### (.*)$" "<h3>$1</h3>")
$line
} else if $line != "" {
# Otherwise, it's a paragraph
# Convert images
let $line = ($line | str replace --all '!\[(.+)\]\((.+)\)' '<img src="$2" alt="$1"/>')
let $line = ($line | str replace --all 'src="../assets' 'src="assets')
# Convert links
let $line = ($line | str replace --all '\[(.+?)\]\((.+?)\)' '<a href="$2">$1</a>')
# Convert code
let $line = ($line | str replace --all '`(.+?)`' '<code>$1</code>')
$"<p>($line)</p>"
}
})
let $html_post = ($html_post | str join "\n")
let $html_post = $"<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>($header.title)</title>
<style>
img {
max-width: 600px
}
p {
display: block;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
body {
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 16px;
color: #2c3e50;
line-height: 1.7;
max-width: 740px;
margin: 0 auto;
padding: 2rem 2.5rem;
}
code {
color: #476582;
padding: .25rem .5rem;
margin: 0;
font-size: .85em;
background-color: #eeeeee;
border-radius: 3px;
}
</style>
</head>
<body>
($html_post)
</body>
</html>"
# print $html_post
let $name = ($markdown.name | path parse | update extension "html" | path join)
$html_post | save --raw $name
}
}
docs/nushell/nu_scripts/sourced/github/branch-protections/README.md:
# Branch Protections
### Definition
Do you have hundreds or thousands of GitHub repositories in your organization? Are you tired of manually managing their branch protection rules? Don't! Let nushell do it for you!
### Setup
1. Replace placeholder data in .nu script with your own (or remove the appropriate fields if you don't need to i.e push to repo from action)
1. Create a repo in your organization account to store the github action
1. Push both the attached script and the github action to the repo
### Possible future improvements
* Instead of cron run the script on repository creation event (once org level actions become a thing in GitHub)docs/nushell/nu_scripts/sourced/github/branch-protections/branch-protections.nu:
#!/usr/bin/env nu
let protections = {
required_status_checks: {
strict: true
checks: [
{
context: 'YOUR CHECK HERE'
app_id: 'YOUR APP ID HERE'
}
]
}
required_pull_request_reviews: {
dismiss_stale_reviews: true
require_code_owner_reviews: true
bypass_pull_request_allowances: {
apps: [
YOUR APP HERE
]
}
}
restrictions: {
users: []
teams: []
apps: [
YOUR APP HERE
]
}
enforce_admins: true
required_linear_history: true
require_conversation_resolution: true
allow_deletions: false
allow_force_pushes: false
}
gh api $"orgs/($env.OWNER)/repos"
|from json
|select name default_branch
|each {|repo|
echo $"Setting branch restrictions for ($repo.name)"
$protections
|to json
|gh api -X PUT $"repos/($env.OWNER)/($repo.name)/branches/($repo.default_branch)/protection" --input -
}
docs/nushell/nu_scripts/sourced/github/branch-protections/branch-protections.yml:
---
name: Add branch protections to all repositories
'on':
schedule:
- cron: '0 * * * *'
workflow_dispatch:
defaults:
run:
shell: nu {0}
jobs:
set-branch-restrictions:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Nu
uses: hustcer/setup-nu@main
with:
version: '0.64.0'
- run: ./branch-protections.nu
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
docs/nushell/nu_scripts/sourced/github/merged-branches/README.md:
# Merged Branches
### Definition
Do your developers often forget to delete their branches after merging PRs? Are you tired of manually going into every repository and deleting them? Don't! Let nushell do it for you!
### Setup
1. Create a repo in your organization account to store the github action
1. Push both the attached script and the github action to the repodocs/nushell/nu_scripts/sourced/github/merged-branches/merged-branches.nu:
#!/usr/bin/env nu
gh api $"orgs/($env.OWNER)/repos"
|from json
|each {|repo|
gh api $"repos/($env.OWNER)/($repo.name)/pulls?state=closed"
|from json
|if ($in|length) > 0 {
each {|pull|
print $"Removing branch ($pull.head.ref) from repo ($repo.name)"
gh api -X DELETE $"repos/($env.OWNER)/($repo.name)/git/refs/heads/($pull.head.ref)"
}
} else {
print $"Repo ($repo.name) has no branches to delete"
}
}docs/nushell/nu_scripts/sourced/github/merged-branches/merged-branches.yml:
---
name: Delete merged branches from all repositories
'on':
schedule:
- cron: '0 * * * *'
workflow_dispatch:
defaults:
run:
shell: nu {0}
jobs:
delete-merged-branches:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Nu
uses: hustcer/setup-nu@main
with:
version: '0.64.0'
- run: ./merged-branches.nu
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OWNER: ${{ github.repository_owner }}
docs/nushell/nushell.github.io/book/3rdpartyprompts.md:
# How to Configure 3rd Party Prompts
## Nerd Fonts
Nerd Fonts are not required, but they can improve the prompt presentation through additional glyphs and iconography.
> Nerd Fonts patches developer targeted fonts with a high number of glyphs (icons).
> Specifically to add a high number of extra glyphs from popular ‘iconic fonts’ such as Font Awesome, Devicons, Octicons, and others.
- [Nerd Fonts website](https://www.nerdfonts.com)
- [Source Repository](https://github.com/ryanoasis/nerd-fonts)
## oh-my-posh
[site](https://ohmyposh.dev/)
[repo](https://github.com/JanDeDobbeleer/oh-my-posh)
If you like [oh-my-posh](https://ohmyposh.dev/), you can use oh-my-posh with Nushell with a few steps. It works great with Nushell. How to setup oh-my-posh with Nushell:
1. Install Oh My Posh and download oh-my-posh's themes following [guide](https://ohmyposh.dev/docs/installation/linux).
2. Download and install a [nerd font](https://github.com/ryanoasis/nerd-fonts).
3. Generate the .oh-my-posh.nu file. By default it will be generated to your home directory. You can use `--config` to specify a theme, other wise, oh-my-posh comes with a default theme.
4. Initialize oh-my-posh prompt by adding in ~/.config/nushell/config.nu(or the path output by `$nu.config-path`) to source ~/.oh-my-posh.nu.
```nu
# Generate the .oh-my-posh.nu file
oh-my-posh init nu --config ~/.poshthemes/M365Princess.omp.json
# Initialize oh-my-posh.nu at shell startup by adding this line in your config.nu file
source ~/.oh-my-posh.nuFor MacOS users:
- You can install oh-my-posh using
brew, just following the guide here - Download and install a nerd font.
- Set the PROMPT_COMMAND in the file output by
$nu.config-path, here is a code snippet:
let posh_dir = (brew --prefix oh-my-posh | str trim)
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' }
# Optional
$env.PROMPT_INDICATOR = $"(ansi y)$> (ansi reset)"- Follow the links above and install Starship.
- Install nerdfonts depending on your preferences.
- Use the config example below. Make sure to set the
STARSHIP_SHELLenvironment variable.
::: tip An alternate way to enable Starship is described in the Starship Quick Install instructions.
The link above is the official integration of Starship and Nushell and is the simplest way to get Starship running without doing anything manual:
- Starship will create its own configuration / environment setup script
- you simply have to create it in
env.nuanduseit inconfig.nu
:::
Here's an example config section for Starship:
$env.STARSHIP_SHELL = "nu"
def create_left_prompt [] {
starship prompt --cmd-duration $env.CMD_DURATION_MS $'--status=($env.LAST_EXIT_CODE)'
}
# Use nushell functions to define your right and left prompt
$env.PROMPT_COMMAND = { || create_left_prompt }
$env.PROMPT_COMMAND_RIGHT = ""
# The prompt indicators are environmental variables that represent
# the state of the prompt
$env.PROMPT_INDICATOR = ""
$env.PROMPT_INDICATOR_VI_INSERT = ": "
$env.PROMPT_INDICATOR_VI_NORMAL = "〉"
$env.PROMPT_MULTILINE_INDICATOR = "::: "Now restart Nu.
nushell on 📙 main is 📦 v0.60.0 via 🦀 v1.59.0
❯
`docs/nushell/nushell.github.io/book/README.md`:
```md
# Introduction
Hello, and welcome to the Nushell project.
The goal of this project is to take the Unix philosophy of shells, where pipes connect simple commands together, and bring it to the modern style of development.
Thus, rather than being either a shell, or a programming language, Nushell connects both by bringing a rich programming language and a full-featured shell together into one package.
Nu takes cues from a lot of familiar territory: traditional shells like bash, object based shells like PowerShell, gradually typed languages like TypeScript, functional programming, systems programming, and more. But rather than trying to be a jack of all trades, Nu focuses its energy on doing a few things well:
- Being a flexible cross-platform shell with a modern feel
- Solving problems as a modern programming language that works with the structure of your data
- Giving clear error messages and clean IDE support
## This Book
The book is split into chapters which are further broken down into sections.
You can click on the chapter headers to get more information about it.
- [Installation](installation.md), of course, helps you get Nushell onto your system.
- [Getting Started](getting_started.md) shows you the ropes. It also explains some of the design principles where Nushell differs from typical shells, such as Bash.
- [Nu Fundamentals](nu_fundamentals.md) explains basic concepts of the Nushell language.
- [Programming in Nu](programming_in_nu.md) dives more deeply into the language features and shows several ways how to organize and structure your code.
- [Nu as a Shell](nu_as_a_shell.md) focuses on the shell features, most notably the configuration and environment.
- [Coming to Nu](coming_to_nu.md) is intended to give a quick start for users coming from other shells or languages.
- [Design Notes](design_notes.md) has in-depth explanation of some of the Nushell's design choices.
- [(Not So) Advanced](advanced.md) includes some more advanced topics (they are not _so_ advanced, make sure to check them out, too!).
## The Many Parts of Nushell
The Nushell project consists of multiple different repositories and subprojects.
You can find all of them under [our organization on GitHub](https://github.com/nushell).
- The main Nushell repository can be found [here](https://github.com/nushell/nushell). It is broken into multiple crates that can be used as independent libraries in your own project, if you wish so.
- The repository of our [nushell.sh](https://www.nushell.sh) page, including this book, can be found [here](https://github.com/nushell/nushell.github.io).
- Nushell has its own line editor which [has its own repository](https://github.com/nushell/reedline)
- [`nu_scripts`](https://github.com/nushell/nu_scripts) is a place to share scripts and modules with other users until we have some sort of package manager.
- [Nana](https://github.com/nushell/nana) is an experimental effort to explore graphical user interface for Nushell.
- [Awesome Nu](https://github.com/nushell/awesome-nu) contains a list of tools that work with the Nushell ecosystem: plugins, scripts, editor extension, 3rd party integrations, etc.
- [Nu Showcase](https://github.com/nushell/showcase) is a place to share works about Nushell, be it blogs, artwork or something else.
- [Request for Comment (RFC)](https://github.com/nushell/rfcs) serves as a place to propose and discuss major design changes. While currently under-utilized, we expect to use it more as we get closer to and beyond 1.0.
## Contributing
We welcome contributions!
[As you can see](#the-many-parts-of-nushell), there are a lot of places to contribute to.
Most repositories contain `CONTRIBUTING.md` file with tips and details that should help you get started (if not, consider contributing a fix!).
Nushell itself is written in [Rust](https://www.rust-lang.org).
However, you do not have to be a Rust programmer to help.
If you know some web development, you can contribute to improving this website or the Nana project.
[Dataframes](dataframes.md) can use your data processing expertise.
If you wrote a cool script, plugin or integrated Nushell somewhere, we'd welcome your contribution to `nu_scripts` or Awesome Nu.
Discovering bugs with reproduction steps and filing GitHub issues for them is a valuable help, too!
You can contribute to Nushell just by using Nushell!
Since Nushell evolves fast, this book is in a constant need of updating.
Contributing to this book does not require any special skills aside from a basic familiarity with Markdown.
Furthermore, you can consider translating parts of it to your language.
## Community
The main place to discuss anything Nushell is our [Discord](https://discord.com/invite/NtAbbGn).
You can also follow our [blog](https://www.nushell.sh/blog) for news and updates.
Finally, you can use the GitHub discussions or file GitHub issues.
docs/nushell/nushell.github.io/book/advanced.md:
---
prev:
text: How Nushell Code Gets Run
link: /book/how_nushell_code_gets_run.md
next:
text: Standard Library (Preview)
link: /book/standard_library.md
---
# (Not so) Advanced
While the "Advanced" title might sound daunting and you might be tempted to skip this chapter, in fact, some of the most interesting and powerful features can be found here.
Besides the built-in commands, Nushell has a [standard library](standard_library.md).
Nushell operates on _structured data_.
You could say that Nushell is a "data-first" shell and a programming language.
To further explore the data-centric direction, Nushell includes a full-featured dataframe processing engine using [Polars](https://github.com/pola-rs/polars) as the backend.
Make sure to check the [Dataframes documentation](dataframes.md) if you want to process large data efficiently directly in your shell.
Values in Nushell contain some extra [metadata](metadata.md).
This metadata can be used, for example, to [create custom errors](creating_errors.md).
Thanks to Nushell's strict scoping rules, it is very easy to [iterate over collections in parallel](parallelism.md) which can help you speed up long-running scripts by just typing a few characters.
You can [interactively explore data](explore.md) with the [`explore`](/commands/docs/explore.md) command.
Finally, you can extend Nushell's functionality with [plugins](plugins.md).
Almost anything can be a plugin as long as it communicates with Nushell in a protocol that Nushell understands.
docs/nushell/nushell.github.io/book/aliases.md:
# Aliases
Aliases in Nushell offer a way of doing a simple replacement of command calls (both external and internal commands). This allows you to create a shorthand name for a longer command, including its default arguments.
For example, let's create an alias called `ll` which will expand to `ls -l`.
```nu
alias ll = ls -lWe can now call this alias:
llOnce we do, it's as if we typed ls -l. This also allows us to pass in flags or positional parameters. For example, we can now also write:
ll -aAnd get the equivalent to having typed ls -l -a.
Your useable aliases can be seen in scope aliases and help aliases.
To make your aliases persistent they must be added to your config.nu file by running config nu to open an editor and inserting them, and then restarting nushell.
e.g. with the above ll alias, you can add alias ll = ls -l anywhere in config.nu
$env.config = {
# main configuration
}
alias ll = ls -l
# some other config and script loadingNote that alias uuidgen = uuidgen | tr A-F a-f (to make uuidgen on mac behave like linux) won't work.
The solution is to define a command without parameters that calls the system program uuidgen via ^.
def uuidgen [] { ^uuidgen | tr A-F a-f }See more in the custom commands section of this book.
Or a more idiomatic example with nushell internal commands
def lsg [] { ls | sort-by type name -i | grid -c | str trim }displaying all listed files and folders in a grid.
::: warning Caution! When replacing commands it is best to "back up" the command first and avoid recursion error. :::
How to back up a command like ls:
alias core-ls = ls # This will create a new alias core-ls for lsNow you can use core-ls as ls in your nu-programming. You will see further down how to use core-ls.
The reason you need to use alias is because, unlike def, aliases are position-dependent. So, you need to "back up" the old command first with an alias, before re-defining it.
If you do not backup the command and you replace the command using def you get a recursion error.
def ls [] { ls }; ls # Do *NOT* do this! This will throw a recursion error
#output:
#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
# ╰────The recommended way to replace an existing command is to shadow the command.
Here is an example shadowing the ls command.
# alias the built-in ls command to ls-builtins
alias ls-builtin = ls
# List the filenames, sizes, and modification times of items in a directory.
def ls [
--all (-a), # Show hidden files
--long (-l), # Get all available columns for each entry (slower; columns are platform-dependent)
--short-names (-s), # Only print the file names, and not the path
--full-paths (-f), # display paths as absolute paths
--du (-d), # Display the apparent directory size ("disk usage") in place of the directory metadata size
--directory (-D), # List the specified directory itself instead of its contents
--mime-type (-m), # Show mime-type in type column instead of 'file' (based on filenames only; files' contents are not examined)
--threads (-t), # Use multiple threads to list contents. Output will be non-deterministic.
...pattern: glob, # The glob pattern to use.
]: [ 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
}
`docs/nushell/nushell.github.io/book/background_jobs.md`:
```md
---
next:
text: Coming to Nu
link: /book/coming_to_nu.md
---
# Background Jobs
Nushell currently has experimental support for thread-based background jobs.
## Spawning Jobs
Jobs can be can be spawned using [`job spawn`](/commands/docs/job_spawn.md), which receives a closure and starts its execution in a background thread, returning
an unique integer id for the spawned job:
```nu
'i am' | save status.txt
job spawn { sleep 10sec; ' inevitable' | save --append status.txt }
# => 1
open status.txt
# => i am
# wait for 10 seconds
sleep 10sec
open status.txt
# => i am inevitable
Active jobs can be queried with the job list command, which returns a table with the information of the jobs which are currently executing.
Jobs can also be killed/interrupted by using the job kill command, which interrupts the job's thread and kills all of the job's child processes:
let id = job spawn { sleep 1day }
job list
# => ┏━━━┳━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━┓
# => ┃ # ┃ id ┃ type ┃ pids ┃
# => ┣━━━╋━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━┫
# => ┃ 0 ┃ 1 ┃ thread ┃ [list 0 items] ┃
# => ┗━━━┻━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━┛
job kill $id
job list
# => ╭────────────╮
# => │ empty list │
# => ╰────────────╯On Unix targets, such as Linux and macOS, Nushell also supports suspending external commands using Ctrl+Z. When a running process is suspended, it is turned into a "frozen" background job:
long_running_process # this starts running, then Ctrl+Z is pressed
# => Job 1 is frozen
job list
# => ┏━━━┳━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━┓
# => ┃ # ┃ id ┃ type ┃ pids ┃
# => ┣━━━╋━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━┫
# => ┃ 0 ┃ 1 ┃ frozen ┃ [list 1 items] ┃
# => ┗━━━┻━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━┛A frozen job can be brought back into foreground with the job unfreeze command:
job unfreeze
# process is brought back where it stopped::: tip Tip
For those familiar with other Unix shells, you can create an alias to emulate the behavior of the fg command:
alias fg = job unfreeze:::
By default, job unfreeze will unfreeze the most recently frozen job. However, you can also specify the id of a specific job to unfreeze:
vim
# => Job 1 is frozen
long_running_process
# => Job 2 is frozen
job unfreeze 1
# we're back in vimData can be sent to a job using job send <id>, and the job can receive it using job recv:
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 threadThe main thread has a job ID of 0, so you can also send data in the other direction:
job spawn {
sleep 1sec
'Hello from a background job' | job send 0
}
job recv
# => Hello from a background jobUnlike many other shells, Nushell jobs are not separate processes, and are instead implemented as background threads.
An important side effect of this, is that all background jobs will terminate once the shell
process exits.
For this reason, Nushell has no UNIX-like disown command to prevent jobs from terminating once the shell exits.
To account for that, there are plans for a job dispatch implementation in the future,
for spawning independent background processes (see #15201 for progress).
Additionally, if the user is running an interactive Nushell session and runs
exit while there are background jobs running,
the shell will warn about them before prompting the user to confirm exit.
`docs/nushell/nushell.github.io/book/cheat_sheet.md`:
```md
---
next:
text: Nu Fundamentals
link: /book/nu_fundamentals.md
---
# Nushell Cheat Sheet
## Data Types
convert string to integer:
```nu
"12" | into int
convert present date to provided time zone:
date now | date to-timezone "Europe/London"update a record's language and if none is specified insert provided value:
{'name': 'nu', 'stars': 5, 'language': 'Python'} | upsert language 'Rust'convert list of strings to yaml:
[one two three] | to yamlprint table data:
[[framework, language]; [Django, Python] [Laravel, PHP]]select two named columns from the table and print their values:
[{name: 'Robert' age: 34 position: 'Designer'}
{name: 'Margaret' age: 30 position: 'Software Developer'}
{name: 'Natalie' age: 50 position: 'Accountant'}
] | select name positioninterpolate text:
let name = "Alice"
$"greetings, ($name)!"
# => greetings, Alice!split text on comma delimiter and save the list to string_list variable:
let string_list = "one,two,three" | split row ","
$string_list
# => ╭───┬───────╮
# => │ 0 │ one │
# => │ 1 │ two │
# => │ 2 │ three │
# => ╰───┴───────╯check if a string contains a substring:
"Hello, world!" | str contains "o, w"
# => truejoin multiple strings with delimiter:
let str_list = [zero one two]
$str_list | str join ','
# => zero,one,twoslice text by indices:
'Hello World!' | str substring 4..8
# => o Worparse string into named columns:
'Nushell 0.80' | parse '{shell} {version}'
# => ╭───┬─────────┬─────────╮
# => │ # │ shell │ version │
# => ├───┼─────────┼─────────┤
# => │ 0 │ Nushell │ 0.80 │
# => ╰───┴─────────┴─────────╯parse comma separated values (csv):
"acronym,long\nAPL,A Programming Language" | from csv
# => ╭───┬─────────┬────────────────────────╮
# => │ # │ acronym │ long │
# => ├───┼─────────┼────────────────────────┤
# => │ 0 │ APL │ A Programming Language │
# => ╰───┴─────────┴────────────────────────╯color text in command-line terminal:
$'(ansi purple_bold)This text is a bold purple!(ansi reset)'
# => This text is a bold purple!insert list value at index:
[foo bar baz] | insert 1 'beeze'
# => ╭───┬───────╮
# => │ 0 │ foo │
# => │ 1 │ beeze │
# => │ 2 │ bar │
# => │ 3 │ baz │
# => ╰───┴───────╯update list value by index:
[1, 2, 3, 4] | update 1 10
# => ╭───┬────╮
# => │ 0 │ 1 │
# => │ 1 │ 10 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => ╰───┴────╯prepend list value:
let numbers = [1, 2, 3]
$numbers | prepend 0
# => ╭───┬───╮
# => │ 0 │ 0 │
# => │ 1 │ 1 │
# => │ 2 │ 2 │
# => │ 3 │ 3 │
# => ╰───┴───╯append list value:
let numbers = [1, 2, 3]
$numbers | append 4
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => ╰───┴───╯slice first list values:
[cammomile marigold rose forget-me-not] | first 2
# => ╭───┬───────────╮
# => │ 0 │ cammomile │
# => │ 1 │ marigold │
# => ╰───┴───────────╯iterate over a list; elt is current list value:
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 │
# => ╰───┴─────────────────────────────────────────╯iterate over a list with an index and value:
$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 the list to a single value; reduce gives access to accumulator that is applied to each element in the list:
let scores = [3 8 4]
$"total = ($scores | reduce { |elt, acc| $acc + $elt })"
# => total = 15reduce with an initial value (--fold):
let scores = [3 8 4]
$"total = ($scores | reduce --fold 1 { |elt, acc| $acc * $elt })"
# => total = 96give access to the 3rd item in the list:
let planets = [Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune]
$planets.2
# => Earthcheck if any string in the list starts with E:
let planets = [Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune]
$planets | any {|elt| $elt | str starts-with "E" }
# => trueslice items that satisfy provided condition:
let cond = {|x| $x < 0 }; [-1 -2 9 1] | take while $cond
# => ╭───┬────╮
# => │ 0 │ -1 │
# => │ 1 │ -2 │
# => ╰───┴────╯sort table:
ls | sort-by sizesort table, get first rows:
ls | sort-by size | first 5concatenate two tables with same columns:
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 │
# => ╰───┴──────────────┴───────────────┴──────────────╯remove the last column of a table:
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 │
# => ╰───┴───────────────────────┴───────╯open a text file with the default text editor:
start file.txtsave a string to text file:
'lorem ipsum ' | save file.txtappend a string to the end of a text file:
'dolor sit amet' | save --append file.txtsave a record to file.json:
{ a: 1, b: 2 } | save file.jsonrecursively search for files by file name:
glob **/*.{rs,toml} --depth 2watch a file, run command whenever it changes:
watch . --glob=**/*.rs {|| cargo test }custom command with parameter type set to string:
def greet [name: string] {
$"hello ($name)"
}custom command with default parameter set to nushell:
def greet [name = "nushell"] {
$"hello ($name)"
}passing named parameter by defining flag for custom commands:
def greet [
name: string
--age: int
] {
[$name $age]
}
greet world --age 10using flag as a switch with a shorthand flag (-a) for the age:
def greet [
name: string
--age (-a): int
--twice
] {
if $twice {
[$name $age $name $age]
} else {
[$name $age]
}
}
greet -a 10 --twice hellocustom command which takes any number of positional arguments using rest params:
def greet [...name: string] {
print "hello all:"
for $n in $name {
print $n
}
}
greet earth mars jupiter venus
# => hello all:
# => earth
# => mars
# => jupiter
# => venusan immutable variable cannot change its value after declaration:
let val = 42
print $val
# => 42shadowing variable (declaring variable with the same name in a different scope):
let val = 42
do { let val = 101; $val }
# => 101
$val
# => 42declaring a mutable variable with mut key word:
mut val = 42
$val += 27
$val
# => 69closures and nested defs cannot capture mutable variables from their environment (errors):
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
# => ╰────a constant variable is immutable and is fully evaluated at parse-time:
const file = 'path/to/file.nu'
source $fileuse question mark operator ? to return null instead of error if provided path is incorrect:
let files = (ls)
$files.name?.0?assign the result of a pipeline to a variable:
let big_files = (ls | where size > 10kb)
$big_filesuse an inline module:
module greetings {
export def hello [name: string] {
$"hello ($name)!"
}
export def hi [where: string] {
$"hi ($where)!"
}
}
use greetings hello
hello "world"import module from file and use its environment in current scope:
# 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!use main command in module:
# 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!
`docs/nushell/nushell.github.io/book/coloring_and_theming.md`:
```md
# Coloring and Theming in Nu
Many parts of Nushell's interface can have their color customized. All of these can be set in the `config.nu` configuration file. If you see the `#` outside of a text value in the config file it means the text after it is commented out.
## Table Borders
Table borders are controlled by the `$env.config.table.mode` setting. It can be changed at run time, or in the `config.nu` file:
```nu
$env.config.table.mode = 'rounded'
The options for $env.config.table.mode can be listed with table --list:
ascii_roundedbasic_compactbasiccompact_doublecompactdefaultdotsdoubleheavylightmarkdownnonepsqlreinforcedrestructuredroundedsinglethinwith_love
Examples:
$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 | heavyThe color configuration is defined in $env.config.color_config. The current configuration can be printed with:
$env.config.color_config | sortThe color and style-attributes can be declared in multiple alternative formats.
r- normal color red's abbreviationrb- normal color red's abbreviation with bold attributered- normal color redred_bold- normal color red with bold attribute"#ff0000"- "#hex" format foreground color red (quotes are required){ fg: "#ff0000" bg: "#0000ff" attr: b }- "full #hex" format foreground red in "#hex" format with a background of blue in "#hex" format with an attribute of bold abbreviated.{|x| 'yellow' }- closure returning a string with one of the color representations listed above{|x| { fg: "#ff0000" bg: "#0000ff" attr: b } }- closure returning a valid record
| code | meaning |
|---|---|
| l | blink |
| b | bold |
| d | dimmed |
| h | hidden |
| i | italic |
| r | reverse |
| s | strikethrough |
| u | underline |
| n | nothing |
| defaults to nothing |
| code | name |
|---|---|
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 |
The "#hex" format is one way you typically see colors represented. It's simply the # character followed by 6 characters. The first two are for red, the second two are for green, and the third two are for blue. It's important that this string be surrounded in quotes, otherwise Nushell thinks it's a commented out string.
Example: The primary red color is "#ff0000" or "#FF0000". Upper and lower case in letters shouldn't make a difference.
This "#hex" format allows us to specify 24-bit truecolor tones to different parts of Nushell.
The full "#hex" format is a take on the "#hex" format but allows one to specify the foreground, background, and attributes in one line.
Example: { fg: "#ff0000" bg: "#0000ff" attr: b }
- foreground of red in "#hex" format
- background of blue in "#hex" format
- attribute of bold abbreviated
Note: Closures are only executed for table output. They do not work in other contexts like for shape_ configurations, when printing a value directly, or as a value in a list.
For example:
$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}prints
╭───┬───────────╮
│ a │ true │
│ b │ false │
│ c │ 0 B │
│ d │ 488.3 KiB │
│ e │ 10.0 MiB │
╰───┴───────────╯with a green true, a light red false, a dark grey 0 B, a cyan 488.3 KiB, and a blue 10.0 MiB.
Primitive values are things like int and string. Primitive values and shapes can be set with a variety of color symbologies seen above.
This is the current list of primitives. Not all of these are configurable. The configurable ones are marked with *.
| primitive | default color | configurable |
|---|---|---|
any |
||
binary |
Color::White.normal() | * |
block |
Color::White.normal() | * |
bool |
Color::White.normal() | * |
cellpath |
Color::White.normal() | * |
condition |
||
custom |
||
date |
Color::White.normal() | * |
duration |
Color::White.normal() | * |
expression |
||
filesize |
Color::White.normal() | * |
float |
Color::White.normal() | * |
glob |
||
import |
||
int |
Color::White.normal() | * |
list |
Color::White.normal() | * |
nothing |
Color::White.normal() | * |
number |
||
operator |
||
path |
||
range |
Color::White.normal() | * |
record |
Color::White.normal() | * |
signature |
||
string |
Color::White.normal() | * |
table |
||
var |
||
vardecl |
||
variable |
| primitive | default color | configurable |
|---|---|---|
leading_trailing_space_bg |
Color::Rgb(128, 128, 128)) | * |
header |
Color::Green.bold() | * |
empty |
Color::Blue.normal() | * |
row_index |
Color::Green.bold() | * |
hints |
Color::DarkGray.normal() | * |
Here's a small example of changing some of these values.
$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_grayHere's another small example using multiple color syntaxes with some comments.
$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"
}As mentioned above, shape is a term used to indicate the syntax coloring.
Here's the current list of flat shapes.
| shape | default style | configurable |
|---|---|---|
shape_block |
fg(Color::Blue).bold() | * |
shape_bool |
fg(Color::LightCyan) | * |
shape_custom |
bold() | * |
shape_external |
fg(Color::Cyan) | * |
shape_externalarg |
fg(Color::Green).bold() | * |
shape_filepath |
fg(Color::Cyan) | * |
shape_flag |
fg(Color::Blue).bold() | * |
shape_float |
fg(Color::Purple).bold() | * |
shape_garbage |
fg(Color::White).on(Color::Red).bold() | * |
shape_globpattern |
fg(Color::Cyan).bold() | * |
shape_int |
fg(Color::Purple).bold() | * |
shape_internalcall |
fg(Color::Cyan).bold() | * |
shape_list |
fg(Color::Cyan).bold() | * |
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() | * |
shape_string |
fg(Color::Green) | * |
shape_string_interpolation |
fg(Color::Cyan).bold() | * |
shape_table |
fg(Color::Blue).bold() | * |
shape_variable |
fg(Color::Purple) | * |
Here's a small example of how to apply color to these items. Anything not overridden will receive its default color.
$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}The Nushell prompt is configurable through these environment variables and config items:
PROMPT_COMMAND: Code to execute for setting up the prompt (block)PROMPT_COMMAND_RIGHT: Code to execute for setting up the RIGHT prompt (block) (see oh-my.nu in nu_scripts)PROMPT_INDICATOR= "〉": The indicator printed after the prompt (by default ">"-like Unicode symbol)PROMPT_INDICATOR_VI_INSERT= ": "PROMPT_INDICATOR_VI_NORMAL= "v "PROMPT_MULTILINE_INDICATOR= "::: "render_right_prompt_on_last_line: Bool value to enable or disable the right prompt to be rendered on the last line of the prompt
Example: For a simple prompt one could do this. Note that PROMPT_COMMAND requires a block whereas the others require a string.
$env.PROMPT_COMMAND = { $"(date now | format date '%m/%d/%Y %I:%M:%S%.3f'): (pwd | path basename)" }If you don't like the default PROMPT_INDICATOR you could change it like this.
$env.PROMPT_INDICATOR = "> "If you're using starship, you'll most likely want to show the right prompt on the last line of the prompt, just like zsh or fish. You could modify the config.nu file, just set render_right_prompt_on_last_line to true:
$env.config.render_right_prompt_on_last_line = trueColoring of the prompt is controlled by the block in PROMPT_COMMAND where you can write your own custom prompt. We've written a slightly fancy one that has git statuses located in the nu_scripts repo.
If you want a different prompt displayed for previously entered commands, you can use Nushell's transient prompt feature. This can be useful if your prompt has lots of information that is unnecessary to show for previous lines (e.g. time and Git status), since you can make it so that previous lines show with a shorter prompt.
Each of the PROMPT_* variables has a corresponding TRANSIENT_PROMPT_* variable to be used for changing that segment when displaying past prompts: TRANSIENT_PROMPT_COMMAND, TRANSIENT_PROMPT_COMMAND_RIGHT, TRANSIENT_PROMPT_INDICATOR, TRANSIENT_PROMPT_INDICATOR_VI_INSERT, TRANSIENT_PROMPT_INDICATOR_VI_NORMAL, TRANSIENT_PROMPT_MULTILINE_INDICATOR. By default, the PROMPT_* variables are used for displaying past prompts.
For example, if you want to make past prompts show up without a left prompt entirely and leave only the indicator, you can use:
$env.TRANSIENT_PROMPT_COMMAND = ""If you want to go back to the normal left prompt, you'll have to unset TRANSIENT_PROMPT_COMMAND:
hide-env TRANSIENT_PROMPT_COMMANDLS_COLORS Colors for the ls Command
Nushell will respect and use the LS_COLORS environment variable setting on Mac, Linux, and Windows. This setting allows you to define the coloring of file types when you do a ls. For instance, you can make directories one color, .md Markdown files another color, .toml files yet another color, etc. There are a variety of ways to color and style your file types.
If LS_COLORS is not set, nushell will default to a built-in LS_COLORS setting, based on 8-bit (extended) ANSI colors.
LS_COLORS contains a colon (:) separated list of records that map file types and file names to styling attributes (selector=attributes).
The selector can be a file type specified like di for "directory identifier", or *.nu for files with the .nu file extension.
The attributes are a list of semicolon (;) separated numbers. Note that which attributes and attribute formats are supported depends on the terminal you are using.
- Style attributes like
0normal,1bold,3italic,5blink, etc - Foreground colors
30-37and90-97 - Background colors
40-47and100-107 - RGB foreground prefixed with
38;2, optionally followed by additional attributes - RGB background prefixed with
48;2, optionally followed by additional attributes
For example:
$env.LS_COLORS = "di=1;34:*.nu=3;33;46": Bold directories, italic yellow foreground cyan background *.nu files
$env.LS_COLORS = "di=48;2;200;0;0;5": Red background blinking directories
For example, you can use the third-party tool vivid, which runs on multiple platforms, has many themes defined, and generates a LS_COLORS configuration from it.
After downloading and extracting the binary, you can use it with:
$env.LS_COLORS = (vivid generate molokai)or with an alternative theme:
$env.LS_COLORS = (vivid generate ayu)You can put this command into your Nushell configuration for it to become the default coloring.
Theming combines all the coloring above. Here's a quick example of one we put together quickly to demonstrate the ability to theme. This is a spin on the base16 themes that we see so widespread on the web.
The key to making theming work is to make sure you specify all themes and colors you're going to use in the config.nu file before you declare the let config = line.
# let's define some colors
let base00 = "#181818" # Default Background
let base01 = "#282828" # Lighter Background (Used for status bars, line number and folding marks)
let base02 = "#383838" # Selection Background
let base03 = "#585858" # Comments, Invisibles, Line Highlighting
let base04 = "#b8b8b8" # Dark Foreground (Used for status bars)
let base05 = "#d8d8d8" # Default Foreground, Caret, Delimiters, Operators
let base06 = "#e8e8e8" # Light Foreground (Not often used)
let base07 = "#f8f8f8" # Light Background (Not often used)
let base08 = "#ab4642" # Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted
let base09 = "#dc9656" # Integers, Boolean, Constants, XML Attributes, Markup Link Url
let base0a = "#f7ca88" # Classes, Markup Bold, Search Text Background
let base0b = "#a1b56c" # Strings, Inherited Class, Markup Code, Diff Inserted
let base0c = "#86c1b9" # Support, Regular Expressions, Escape Characters, Markup Quotes
let base0d = "#7cafc2" # Functions, Methods, Attribute IDs, Headings
let base0e = "#ba8baf" # Keywords, Storage, Selector, Markup Italic, Diff Changed
let base0f = "#a16946" # Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?>
# we're creating a theme here that uses the colors we defined above.
let base16_theme = {
separator: $base03
leading_trailing_space_bg: $base04
header: $base0b
date: $base0e
filesize: $base0d
row_index: $base0c
bool: $base08
int: $base0b
duration: $base08
range: $base08
float: $base08
string: $base04
nothing: $base08
binary: $base08
cellpath: $base08
hints: dark_gray
# 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_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_external: $base0c
shape_externalarg: { fg: $base0b attr: b }
shape_literal: $base0d
shape_operator: $base0a
shape_signature: { fg: $base0b attr: b }
shape_string: $base0b
shape_filepath: $base0d
shape_globpattern: { fg: $base0d attr: b }
shape_variable: $base0e
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.
$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: trueif you want to go full-tilt on theming, you'll want to theme all the items I mentioned at the very beginning, including LS_COLORS, and the prompt. Good luck!
Nushell's standard library contains a config module with default light and dark themes.
If you are working on a light background terminal, you can apply the light theme easily.
# in $nu.config-path
use std/config light-theme # add this line to load the theme into scope
$env.config = {
# ...
color_config: (light-theme) # after using dark-theme or light-theme from std, you can change this with `(dark-theme)` in place of `(light-theme)`.
# ...
}You can also load the dark theme.
# in $nu.config-path
use std/config dark-theme
$env.config = {
# ...
color_config: (dark-theme)
# ...
}It's often desired to have the minimum amount of decorations when using a screen reader. In those cases, it's possible to disable borders and other decorations for both table and errors with the following options:
# in $nu.config-path
$env.config = {
...
table: {
...
mode: "none"
...
}
error_style: "plain"
...
}Reedline (Nu’s line editor) style is not using the color_config key.
Instead, each menu has its own style to be configured separately.
See the section dedicated to Reedline’s menus configuration to learn more on this.
`docs/nushell/nushell.github.io/book/coming_from_bash.md`:
```md
---
prev:
text: Coming to Nu
link: /book/coming_to_nu.md
---
# Coming from Bash
::: tip
If you're coming from `Git Bash` on Windows, then the external commands you're used to (e.g, `ln`, `grep`, `vi`, etc) will not be available in Nushell by default unless you have already explicitly made them available in the Windows Path environment variable.
To make these commands available in Nushell as well, add the following line to your `config.nu` with either `append` or `prepend`.
```nu
$env.Path = ($env.Path | prepend 'C:\Program Files\Git\usr\bin')
:::
| Bash | Nu | Task |
|---|---|---|
ls |
ls |
Lists the files in the current directory |
ls <dir> |
ls <dir> |
Lists the files in the given directory |
ls pattern* |
ls pattern* |
Lists files that match a given pattern |
ls -la |
ls --long --all or ls -la |
List files with all available information, including hidden files |
ls -d */ |
ls | where type == dir |
List directories |
find . -name *.rs |
ls **/*.rs |
Find recursively all files that match a given pattern |
find . -name Makefile | xargs vim |
ls **/Makefile | get name | vim ...$in |
Pass values as command parameters |
cd <directory> |
cd <directory> |
Change to the given directory |
cd |
cd |
Change to the home directory |
cd - |
cd - |
Change to the previous directory |
mkdir <path> |
mkdir <path> |
Creates the given path |
mkdir -p <path> |
mkdir <path> |
Creates the given path, creating parents as necessary |
touch test.txt |
touch test.txt |
Create a file |
> <path> |
out> <path> or o> <path> |
Save command output to a file |
| save <path> |
Save command output to a file as structured data | |
>> <path> |
out>> <path> or o>> <path> |
Append command output to a file |
| save --append <path> |
Append command output to a file as structured data | |
> /dev/null |
| ignore |
Discard command output |
> /dev/null 2>&1 |
out+err>| ignore or o+e>| ignore |
Discard command output, including stderr |
command 2>&1 | less |
command out+err>| less or command o+e>| less |
Pipe stdout and stderr of an external command into less (NOTE: use explore for paging output of internal commands) |
cmd1 | tee log.txt | cmd2 |
cmd1 | tee { save log.txt } | cmd2 |
Tee command output to a log file |
command | head -5 |
command | first 5 |
Limit the output to the first 5 rows of an internal command (see also last and skip) |
cat <path> |
open --raw <path> |
Display the contents of the given file |
open <path> |
Read a file as structured data | |
cat <(<command1>) <(<command2>) |
[(command1), (command2)] | str join |
Concatenate the outputs of command1 and command2 |
cat <path> <(<command>) |
[(open --raw <path>), (command)] | str join |
Concatenate the contents of the given file and output of command |
mv <source> <dest> |
mv <source> <dest> |
Move file to new location |
for f in *.md; do echo $f; done |
ls *.md | each { $in.name } |
Iterate over a list and return results |
for i in $(seq 1 10); do echo $i; done |
for i in 1..10 { print $i } |
Iterate over a list and run a command on results |
cp <source> <dest> |
cp <source> <dest> |
Copy file to new location |
cp -r <source> <dest> |
cp -r <source> <dest> |
Copy directory to a new location, recursively |
rm <path> |
rm <path> |
Remove the given file |
rm -t <path> |
Move the given file to the system trash | |
rm -rf <path> |
rm -r <path> |
Recursively removes the given path |
date -d <date> |
"<date>" | into datetime -f <format> |
Parse a date (format documentation) |
sed |
str replace |
Find and replace a pattern in a string |
grep <pattern> |
where $it =~ <substring> or find <substring> |
Filter strings that contain the substring |
man <command> |
help <command> |
Get the help for a given command |
help commands |
List all available commands | |
help --find <string> |
Search for match in all available commands | |
command1 && command2 |
command1; command2 |
Run a command, and if it's successful run a second |
stat $(which git) |
stat ...(which git).path |
Use command output as argument for other command |
echo /tmp/$RANDOM |
$"/tmp/(random int)" |
Use command output in a string |
cargo b --jobs=$(nproc) |
cargo b $"--jobs=(sys cpu | length)" |
Use command output in an option |
echo $PATH |
$env.PATH (Non-Windows) or $env.Path (Windows) |
See the current path |
echo $? |
$env.LAST_EXIT_CODE |
See the exit status of the last executed command |
<update ~/.bashrc> |
vim $nu.config-path |
Update PATH permanently |
export PATH = $PATH:/usr/other/bin |
$env.PATH = ($env.PATH | append /usr/other/bin) |
Update PATH temporarily |
export |
$env |
List the current environment variables |
<update ~/.bashrc> |
vim $nu.config-path |
Update environment variables permanently |
FOO=BAR ./bin |
FOO=BAR ./bin |
Update environment temporarily |
export FOO=BAR |
$env.FOO = BAR |
Set environment variable for current session |
echo $FOO |
$env.FOO |
Use environment variables |
echo ${FOO:-fallback} |
$env.FOO? | default "ABC" |
Use a fallback in place of an unset variable |
unset FOO |
hide-env FOO |
Unset environment variable for current session |
alias s="git status -sb" |
alias s = git status -sb |
Define an alias temporarily |
type FOO |
which FOO |
Display information about a command (builtin, alias, or executable) |
<update ~/.bashrc> |
vim $nu.config-path |
Add and edit alias permanently (for new shells) |
bash -c <commands> |
nu -c <commands> |
Run a pipeline of commands |
bash <script file> |
nu <script file> |
Run a script file |
\ |
( <command> ) |
A command can span multiple lines when wrapped with ( and ) |
pwd or echo $PWD |
pwd or $env.PWD |
Display the current directory |
read var |
let var = input |
Get input from the user |
read -s secret |
let secret = input -s |
Get a secret value from the user without printing keystrokes |
| Bash | Nu | Task |
|---|---|---|
!! |
!! |
Insert last command-line from history |
!$ |
!$ |
Insert last spatially separated token from history |
!<n> (e.g., !5) |
!<n> |
Insert <n>th command from the beginning of the history |
Tip: history | enumerate | last 10 to show recent positions |
||
!<-n> (e.g., !-5) |
!<-n> |
Insert <n>th command from the end of the history |
!<string> (e.g., !ls) |
!<string> |
Insert the most recent history item that begins with the string |
| Ctrl/Cmd+R | Ctrl/Cmd+R | Reverse history search |
| (Emacs Mode) Ctrl+XCtrl+E | Ctrl/Cmd+O | Edit the command-line in the editor defined by $env.EDITOR |
| (Vi Command Mode) V | Ctrl/Cmd+O | Edit the command-line in the editor defined by $env.EDITOR |
Most common Emacs-mode and Vi-mode keybindings are also available. See the Reedline Chapter.
::: tip In Bash, history substitution occurs immediately after pressing Enter to execute the command-line. Nushell, however, inserts the substitution into the command-line after pressing Enter. This allows you to confirm the substitution and, if needed, make additional edits before execution.
This behavior extends to "Edit command-line in Editor" as well. While Bash immediately executes the command after exiting the editor, Nushell (like other, more modern shells such as Fish and Zsh) inserts the editor contents into the command-line, allowing you to review and make changes before committing it to execution. :::
`docs/nushell/nushell.github.io/book/coming_from_cmd.md`:
```md
# Coming from CMD.EXE
This table was last updated for Nu 0.67.0.
| CMD.EXE | Nu | Task |
| ------------------------------------ | ----------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
| `ASSOC` | | Displays or modifies file extension associations |
| `BREAK` | | Trigger debugger breakpoint |
| `CALL <filename.bat>` | `<filename.bat>` | Run a batch program |
| | `nu <filename>` | Run a nu script in a fresh context |
| | `source <filename>` | Run a nu script in this context |
| | `use <filename>` | Run a nu script as a module |
| `CD` or `CHDIR` | `$env.PWD` | Get the present working directory |
| `CD <directory>` | `cd <directory>` | Change the current directory |
| `CD /D <drive:directory>` | `cd <drive:directory>` | Change the current directory |
| `CLS` | `clear` | Clear the screen |
| `COLOR` | | Set the console default foreground/background colors |
| | `ansi {flags} (code)` | Output ANSI codes to change color |
| `COPY <source> <destination>` | `cp <source> <destination>` | Copy files |
| `COPY <file1>+<file2> <destination>` | `[<file1>, <file2>] \| each { open --raw } \| str join \| save --raw <destination>` | Append multiple files into one |
| `DATE /T` | `date now` | Get the current date |
| `DATE` | | Set the date |
| `DEL <file>` or `ERASE <file>` | `rm <file>` | Delete files |
| `DIR` | `ls` | List files in the current directory |
| `ECHO <message>` | `print <message>` | Print the given values to stdout |
| `ECHO ON` | | Echo executed commands to stdout |
| `ENDLOCAL` | `export-env` | Change env in the caller |
| `EXIT` | `exit` | Close the prompt or script |
| `FOR %<var> IN (<set>) DO <command>` | `for $<var> in <set> { <command> }` | Run a command for each item in a set |
| `FTYPE` | | Displays or modifies file types used in file extension associations |
| `GOTO` | | Jump to a label |
| `IF ERRORLEVEL <number> <command>` | `if $env.LAST_EXIT_CODE >= <number> { <command> }` | Run a command if the last command returned an error code >= specified |
| `IF <string> EQU <string> <command>` | `if <string> == <string> { <command> }` | Run a command if strings match |
| `IF EXIST <filename> <command>` | `if (<filename> \| path exists) { <command> }` | Run a command if the file exists |
| `IF DEFINED <variable> <command>` | `if '$<variable>' in (scope variables).name { <command> }` | Run a command if the variable is defined |
| `MD` or `MKDIR` | `mkdir` | Create directories |
| `MKLINK` | | Create symbolic links |
| `MOVE` | `mv` | Move files |
| `PATH` | `$env.Path` | Display the current path variable |
| `PATH <path>;%PATH%` | `$env.Path = ($env.Path \| append <path>`) | Edit the path variable |
| `PATH %PATH%;<path>` | `$env.Path = ($env.Path \| prepend <path>`) | Edit the path variable |
| `PAUSE` | `input "Press any key to continue . . ."` | Pause script execution |
| `PROMPT <template>` | `$env.PROMPT_COMMAND = { <command> }` | Change the terminal prompt |
| `PUSHD <path>`/`POPD` | `enter <path>`/`dexit` | Change working directory temporarily |
| `REM` | `#` | Comments |
| `REN` or `RENAME` | `mv` | Rename files |
| `RD` or `RMDIR` | `rm` | Remove directory |
| `SET <var>=<string>` | `$env.<var> = <string>` | Set environment variables |
| `SETLOCAL` | (default behavior) | Localize environment changes to a script |
| `START <path>` | Partially covered by `start <path>` | Open the path in the system-configured default application |
| `START <internal command>` | | Start a separate window to run a specified internal command |
| `START <batch file>` | | Start a separate window to run a specified batch file |
| `TIME /T` | `date now \| format date "%H:%M:%S"` | Get the current time |
| `TIME` | | Set the current time |
| `TITLE` | | Set the cmd.exe window name |
| `TYPE` | `open --raw` | Display the contents of a text file |
| | `open` | Open a file as structured data |
| `VER` | | Display the OS version |
| `VERIFY` | | Verify that file writes happen |
| `VOL` | | Show drive information |
## Forwarded CMD.EXE commands
Nu accepts and runs *some* of CMD.EXE's internal commands through `cmd.exe`.
The internal commands are: `ASSOC`, `CLS`, `ECHO`, `FTYPE`, `MKLINK`, `PAUSE`, `START`, `VER`, `VOL`
These internal commands take precedence over external commands.
For example, with a `ver.bat` file in the current working directory, executing `^ver` executes CMD.EXE's internal `VER` command, *NOT* the `ver.bat` file.
Executing `./ver` or `ver.bat` *will* execute the local bat file though.
Note that Nushell has its own [`start` command](/commands/docs/start.md) which takes precedence.
You can call the CMD.EXE's internal `START` command with the external command syntax `^start`.
docs/nushell/nushell.github.io/book/coming_to_nu.md:
---
prev:
text: Background Jobs
link: /book/background_jobs.md
next:
text: Coming from Bash
link: /book/coming_from_bash.md
---
# Coming to Nu
If you are familiar with other shells or programming languages, you might find this chapter useful to get up to speed.
[Coming from Bash](coming_from_bash.md) shows how some patterns typical for Bash, or POSIX shells in general, can be mapped to Nushell.
Similarly, [Coming from CMD.EXE](coming_from_cmd.md) shows how built-in commands in the Windows Command Prompt can be mapped to Nushell.
Similar comparisons are made for some [other shells and domain-specific languages](nushell_map.md), [imperative languages](nushell_map_imperative.md), and [functional languages](nushell_map_functional.md).
A separate comparison is made specifically for [operators](nushell_operator_map.md).
docs/nushell/nushell.github.io/book/configuration.md:
---
prev:
text: Nu as a Shell
link: /book/nu_as_a_shell.md
---
# Configuration
## Quickstart
While Nushell provides many options for managing its startup and configuration, new users
can get started with just a few simple steps:
1. Tell Nushell what editor to use:
```nu
$env.config.buffer_editor = <path_to_your_preferred_editor>For example:
$env.config.buffer_editor = "code"
# or
$env.config.buffer_editor = "nano"
# or
$env.config.buffer_editor = "hx"
# or
$env.config.buffer_editor = "vi"
# with args
$env.config.buffer_editor = ["emacsclient", "-s", "light", "-t"]
# etc.-
Edit
config.nuusing:config nu
This will open the current
config.nuin the editor defined above. -
Add commands to this file that should run each time Nushell starts. A good first example might be the
buffer_editorsetting above.You can find a detailed list of available settings using:
config nu --doc | nu-highlight | less -R
-
Save, exit the editor, and start a new Nushell session to load these settings.
That's it! More details are below when you need them ...
[[toc]]
::: tip To view a simplified version of this documentation from inside Nushell, run:
config nu --doc | nu-highlight | less -R:::
Nushell uses multiple, optional configuration files. These files are loaded in the following order:
-
The first file loaded is
env.nu, which was historically used to override environment variables. However, the current "best-practice" recommendation is to set all environment variables (and other configuration) usingconfig.nuand the autoload directories below. -
config.nuis typically used to override default Nushell settings, define (or import) custom commands, or run any other startup tasks. -
*.nufiles in$nu.vendor-autoload-dirsare loaded. These directories are intended for vendors' and package managers' startup files. -
*.nufiles in$nu.user-autoload-dirsare loaded. These files may be used for any startup tasks and are a good way to modularize the configuration. -
login.nuruns commands or handles configuration that should only take place when Nushell is running as a login shell.
By default, env.nu, config.nu, and login.nu are read from the $nu.default-config-dir directory. For example:
$nu.default-config-dir
# macOS
# => /Users/me/Library/Application Support/nushell
# Linux
# => /home/me/.config/nushell
# Windows
# => C:\Users\me\AppData\Roaming\nushellThe first time Nushell is launched, it will create the configuration directory and an empty (other than comments) env.nu and config.nu.
::: tip
You can quickly open config.nu in your default text editor using the config nu command. Likewise, the config env command will open env.nu.
This requires that you have configured a default editor using either:
- Nushell's
$env.config.buffer_editorsetting - The
$env.VISUALor$env.EDITORenvironment variables
For example, place this in your config.nu to edit your files in Visual Studio Code:
$env.config.buffer_editor = 'code':::
::: tip
Some users will prefer a "monolithic" configuration file with most or all startup tasks in one place. config.nu can be used for this purpose.
Other users may prefer a "modular" configuration where each file handles a smaller, more focused set of tasks. Files in the autoload dirs can be used to create this experience. :::
config.nu is commonly used to:
- Set environment variables for Nushell and other applications
- Set Nushell settings in
$env.config - Load modules or source files so that their commands are readily available
- Run any other applications or commands at startup
::: tip See Also The Environment Chapter covers additional information on how to set and access environment variables. :::
As with most shells, Nushell searches the environment variable named PATH (or variants).
:::tip
Unlike some shells, Nushell attempts to be "case agnostic" with environment variables. Path, path, PATH, and even pAtH are all allowed variants of the same environment variable. See Environment - Case Sensitivity for details.
:::
When Nushell is launched, it usually inherits the PATH as a string. However, Nushell automatically converts this to a Nushell list for easy access. This means that you can append to the path using, for example:
$env.path ++= ["~/.local/bin"]The Standard Library also includes a helper command. The default path add behavior is to prepend
a directory so that it has higher precedence than the rest of the path. For example, the following can be
added to your startup config:
use std/util "path add"
path add "~/.local/bin"
path add ($env.CARGO_HOME | path join "bin")::: tip
Notice the use of path join in the example above. This command properly joins two path
components regardless of whether or not the path separator is present. See help path for
more commands in this category.
:::
Nushell provides a number of prompt configuration options. By default, Nushell includes:
- A prompt which includes the current directory, abbreviated using
~if it is (or is under) the home directory. - A prompt indicator which appears immediately to the right of the prompt. This defaults to
>when in normal editing mode, or a:when in Vi-insert mode. Note extra space after the character to provide separation of the command from the prompt. - A right-prompt with the date and time
- An indicator which is displayed when the current commandline extends over multiple lines -
:::by default
The environment variables which control these prompt components are:
$env.PROMPT_COMMAND: The prompt itself$env.PROMPT_COMMAND_RIGHT: A prompt which can appear on the right side of the terminal$env.PROMPT_INDICATOR: Emacs mode indicator$env.PROMPT_INDICATOR_VI_NORMAL: Vi-normal mode indicator$env.PROMPT_INDICATOR_VI_INSERT: Vi-insert mode indicator$env.PROMPT_MULTILINE_INDICATOR: The multi-line indicator
Each of these variables accepts either:
- A string, in which case the component will be statically displayed as that string.
- A closure (with no parameters), in which case the component will be dynamically displayed based on the closure's code.
null, in which case the component will revert to its internal default value.
::: tip To disable the right-prompt, for instance, add the following to your startup config:
$env.PROMPT_COMMAND_RIGHT = ""
# or
$env.PROMPT_COMMAND_RIGHT = {||}:::
Nushell also supports transient prompts, which allow a different prompt to be shown after a commandline has been executed. This can be useful in several situations:
- When using a multi-line prompt, the transient prompt can be a more condensed version.
- Removing the transient multiline indicator and right-prompt can simplify copying from the terminal.
As with the normal prompt commands above, each transient prompt can accept a (static) string, a (dynamic) closure, or a null to use the Nushell internal defaults.
The environment variables which control the transient prompt components are:
$env.TRANSIENT_PROMPT_COMMAND: The prompt itself after the commandline has been executed$env.TRANSIENT_PROMPT_COMMAND_RIGHT: A prompt which can appear on the right side of the terminal$env.TRANSIENT_PROMPT_INDICATOR: Emacs mode indicator$env.TRANSIENT_PROMPT_INDICATOR_VI_NORMAL: Vi-normal mode indicator$env.TRANSIENT_PROMPT_INDICATOR_VI_INSERT: Vi-insert mode indicator$env.TRANSIENT_PROMPT_MULTILINE_INDICATOR: The multi-line indicator
::: tip
Nushell sets TRANSIENT_PROMPT_COMMAND_RIGHT and TRANSIENT_PROMPT_MULTILINE_INDICATOR to an empty string ("") so that each disappears after the previous command is entered. This simplifies copying and pasting from the terminal.
To disable this feature and always show those items, set:
$env.TRANSIENT_PROMPT_COMMAND_RIGHT = null
$env.TRANSIENT_PROMPT_MULTILINE_INDICATOR = null:::
Certain variables, such as those containing multiple paths, are often stored as a colon-separated string in other shells. Nushell can convert these automatically to a more convenient Nushell list. The ENV_CONVERSIONS variable specifies how environment variables are:
- converted from a string to a value on Nushell startup (from_string)
- converted from a value back to a string when running external commands (to_string)
ENV_CONVERSIONS is a record, where:
- each key is an environment variable to be converted
- each value is another record containing a:
{ from_string: <closure> to_string: <closure> }
::: tip
As mentioned above, the OS Path variable is automatically converted by Nushell. As a result, it can be treated as a list within your startup config without needing to be present in ENV_CONVERSIONS. Other colon-separated paths, like XDG_DATA_DIRS, are not automatically converted.
:::
To add an additional conversion, merge it into the $env.ENV_CONVERSIONS record. For example, to add a conversion for the XDG_DATA_DIRS variable:
$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) }
}
}As with many ls-like utilities, Nushell's directory listings make use of the LS_COLORS environment variable for defining styles/colors to apply to certain file types and patterns.
The primary mechanism for changing Nushell's behavior is the $env.config record. While this record is accessed as an environment variable, unlike most other variables it is:
- Not inherited from the parent process. Instead, it is populated by Nushell itself with certain defaults.
- Not exported to child processes started by Nushell.
To examine the current settings in $env.config, just type the variable name:
$env.config::: tip Since Nushell provides so many customization options, it may be better to send this to a pager like:
$env.config | table -e | less -R
# or, if bat is installed:
$env.config | table -e | bat -p:::
An appendix documenting each setting will be available soon. In the meantime, abbreviated documentation on each setting can be viewed in Nushell using:
config nu --doc | nu-highlight | bat
# or
config nu --doc | nu-highlight | less -RTo avoid overwriting existing settings, it's best to simply assign updated values to the desired configuration keys, rather than the entire config record. In other words:
::: warning Wrong
$env.config = {
show_banner: false
}This would reset any other settings that had been changed, since the entire record would be overwritten. :::
::: tip Right
$env.config.show_banner = falseThis changes only the show_banner key/value pair, leaving all other keys with their existing values.
:::
Certain keys are themselves also records. It's okay to overwrite these records, but it's best-practice to set all values when doing so. For example:
$env.config.history = {
file_format: sqlite
max_size: 1_000_000
sync_on_enter: true
isolation: true
}:::note This section is linked directly from the banner message, so it repeats some information from above. :::
To remove the welcome message that displays each time Nushell starts:
-
Type
config nuto edit your configuration file. -
If you receive an error regarding the editor not being defined:
$env.config.buffer_editor = <path to your preferred editor> # Such as: $env.config.buffer_editor = "code" $env.config.buffer_editor = "vi" # Or with editor arguments: $env.config.buffer_editor = ["emacsclient", "-s", "light", "-t"]
Then repeat step 1.
-
Add the following line to the end of the file:
$env.config.show_banner = false
-
Save and exit your editor.
-
Restart Nushell to test the change.
::: warning Important As discussed below, variables in this section must be set before Nushell is launched. :::
Some variables that control Nushell startup file locations must be set before Nushell is loaded. This is often done by a parent process such as:
-
The terminal application in which Nushell is run
-
The operating system or window manager. When running Nushell as a login shell, this will likely be the only mechanism available.
For example, on Windows, you can set environment variables through the Control Panel. Choose the Start Menu and search for "environment variables".
On Linux systems using PAM,
/etc/environment(and other system-specific mechanisms) can be used. -
A parent shell. For example, exporting the value from
bashbefore runningnu.
The variables that affect Nushell file locations are:
-
$env.XDG_CONFIG_HOME: If this environment variable is set, it is used to change the directory that Nushell searches for its configuration files such asenv.nu,config.nu,login.nu, and the<config>/autoloaddirectory. The history and plugin files are also stored in this directory by default.Once Nushell starts, this value is stored in the
$nu.default-config-dirconstant. See Using Constants below. -
$env.XDG_DATA_HOME: If this environment variable is set, Nushell sets the$nu.data-dirconstant to this value. Thedata-diris used in several startup tasks:($nu.data-dir)/completionsis added to the$env.NU_LIB_DIRSsearch path.($nu.data-dir)/vendor/autoloadis added as the last path innu.vendor-autoload-dirs. Files in this directory will be read after the other vendor-auto-load directories, thus overriding any of their settings.
Note that the directory represented by
$nu.data-dir, nor any of its subdirectories, are created by default. Creation and use of these directories is up to the user. -
$env.XDG_DATA_DIRS(Unix Platforms Only): If this environment variable is set, it is used to populate the$nu.vendor-auto-loaddirectories in the order listed. The first directory in the list is processed first, meaning the last one read will have the ability to override previous definitions.
::: warning
The XDG_* variables are not Nushell-specific and should not be set to a directory with only Nushell files. Instead, set the environment variable to the directory above the one with the nushell directory.
For example, if you set $env.XDG_CONFIG_HOME to:
/users/username/dotfiles/nushell
... Nushell will look for config files in /Users/username/dotfiles/nushell/nushell. The proper setting would be:
/users/username/dotfiles
Also keep in mind that if the system has already set XDG variables, then there may already be files in use in those directories. Changing the location may require that you move other application's files to the new directory.
:::
::: tip You can easily test out config changes in a "fresh" environment using the following recipe. The following is run from inside Nushell, but can be adapted to other shells as well:
# Create an empty temporary directory
let temp_home = (mktemp -d)
# Set the configuration path to this directory
$env.XDG_CONFIG_HOME = $temp_home
# Set the data-dir to this directory
$env.XDG_DATA_HOME = $temp_home
# Remove other potential autoload directories
$env.XDG_DATA_HOME = ""
# Run Nushell in this environment
nu
# Edit config
config nu
# Exit the subshell
exit
# Run the temporary config
nuWhen done testing the configuration:
# Remove the temporary config directory, if desired
rm $temp_homeImportant: Then exit the parent shell so that the XDG changes are not accidentally propagated to other processes.
:::
Some important commands, like source and use, that help define custom commands (and other definitions) are parse-time keywords. Among other things, this means that all arguments must be known at parse-time.
In other words, variable arguments are not allowed for parser keywords.
However, Nushell creates some convenience constants that can be used to help identify common file locations. For instance, you can source a file in the default configuration directory using:
source ($nu.default-config-dir | path join "myfile.nu")Because the constant value is known at parse-time, it can be used with parse-time keywords like source and use.
:::tip See Also See Parse-time Constant Evaluation for more details on this process. :::
To see a list of the built-in Nushell constants, examine the record constant using $nu (including the dollar sign).
Nushell can also make use of a NU_LIB_DIRS constant which can act like the $env.NU_LIB_DIRS variable mentioned above. However, unlike $env.NU_LIB_DIRS, it can be defined and used in config.nu. For example:
# Define module and source search path
const NU_LIB_DIRS = [
'~/myscripts'
]
# Load myscript.nu from the ~/myscripts directory
source myscript.nuIf both the variable $env.NU_LIB_DIRS and the const NU_LIB_DIRS are defined, both sets
of paths will be searched. The constant NU_LIB_DIRS will be searched first and have
precedence. If a file matching the name is found in one of those directories, the search will
stop. Otherwise, it will continue into the $env.NU_LIB_DIRS search path.
const NU_PLUGIN_DIRS works in the same way for the plugin search path.
The following NU_PLUGIN_DIRS configuration will allow plugins to be loaded from;
- The directory where the
nuexecutable is located. This is typically where plugins are located in release packages. - A directory in
$nu.data-dirsnamed after the version of Nushell running (e.g.0.100.0). - A
pluginsdirectory in your$nu.config-path.
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')
]You can learn more about setting up colors and theming in the associated chapter.
The login shell is often responsible for setting certain environment variables which will be inherited by subshells and other processes. When setting Nushell as a user's default login shell, you'll want to make sure that the login.nu handles this task.
Many applications will assume a POSIX or PowerShell login shell, and will either provide instructions for modifying the system or user profile that is loaded by POSIX login-shells (or .ps1 file on PowerShell systems).
As you may have noticed by now, Nushell is not a POSIX shell, nor is it PowerShell, and it won't be able to process a profile written for these. You'll need to set these values in login.nu instead.
To find environment variables that may need to be set through login.nu, examine the inherited environment from your login shell by running nu from within your previous login shell. Run:
$env | reject config | transpose key val | each {|r| echo $"$env.($r.key) = '($r.val)'"} | str join (char nl)Look for any values that may be needed by third-party applications and copy these to your login.nu. Many of these will not be needed. For instance, the PS1 setting is the current prompt in POSIX shells and won't be useful in Nushell.
When ready, add Nushell to your /etc/shells (Unix) and chsh as discussed in the Installation Chapter.
Some tools such as Emacs rely on an open command to open files on Mac.
Since Nushell has its own open command with a different meaning which shadows (overrides) /usr/bin/open, these tools will generate an error when trying to use the command.
One way to work around this is to define a custom command for Nushell's open and create an alias for the system's open in your config.nu file like this:
alias nu-open = open
alias open = ^openPlace this in your config.nu to make it permanent.
The ^ symbol tells Nushell to run the following command as an external command, rather than as a Nushell built-in. After running these commands, nu-open will be the Nushell internal version, and the open alias will call the Mac, external open instead.
For more information, see Running System (External) Commands.
This section contains a more detailed description of how different configuration (and flag) options can be used to change Nushell's startup behavior.
The following stages and their steps may occur during startup, based on the flags that are passed to nu. See Flag Behavior immediately following this table for how each flag impacts the process:
| Step | Stage | Nushell Action |
|---|---|---|
| 0. | (misc) | Sets internal defaults via its internal Rust implementation. In practice, this may not take place until "first use" of the setting or variable, but there will typically be a Rust default for most (but not all) settings and variables that control Nushell's behavior. These defaults can then be superseded by the steps below. |
| 1. | (main) | Inherits its initial environment from the calling process. These will initially be converted to Nushell strings, but can be converted to other structures later using ENV_CONVERSIONS (see below). |
| 2. | (main) | Gets the configuration directory. This is OS-dependent (see dirs::config_dir), but can be overridden using XDG_CONFIG_HOME on all platforms as discussed above. |
| 3. | (main) | Creates the initial $env.NU_LIB_DIRS variable. By default, it is an empty list. |
| 4. | (main) | Creates the initial $NU_LIB_DIRS variable. By default, it includes (1) the scripts directory under the configuration directory, and (2) nushell/completions under the default data directory (either $env.XDG_DATA_HOME or the default provided by the dirs crate). These directories are not created by default. |
| 5. | (main) | Creates the initial $env.NU_PLUGIN_DIRS variable. By default, it is an empty list. |
| 6. | (main) | Creates the initial $NU_PLUGIN_DIRS variable. By default, this will include (1) the plugins directory under the configuration directory, and (2) the directory where the currently running nu/nu.exe is located. |
| 7. | (main) | Initializes the in-memory SQLite database. This allows the stor family of commands to be used in the following configuration files. |
| 8. | (main) | Processes commandline arguments such as --plugin-config <file>, --plugins <list>, and others. See nu --help for a complete list. |
| 9. | (main) | Gets the path to env.nu and config.nu. By default, these are located in the config directory, but either or both can be overridden using the --env-config <path> and --config <path> flags. |
| 10. | (main) | If the --include-path (-I) flag was used, it overrides the default $env.NU_LIB_DIRS that was obtained above. |
| 11. | (main) | Loads the initial $env.config values from the internal defaults. |
| 12. | (main) | Converts the search path from the inherited string to a Nushell list. |
| 13. | (stdlib) | Loads the Standard Library and std-rfc into the virtual filesystem. It is not parsed or evaluated at this point. |
| 14. | (stdlib) | Parses and evaluates std/prelude, which brings the banner and pwd commands into scope. |
| 15. | (main) | Generates the initial $nu record constant so that items such as $nu.default-config-dir can be used in the following config files. |
| 16. | (main) | Loads any plugins that were specified using the --plugin flag. |
| 17. | (repl) | Sets several default environment variables that only apply in the REPL (prompt-related and SHLVL). Note that prompt-related variables using closures are set in default_env.nu. |
| 18. | (config files) (plugin) | Processes the signatures in the user's plugin.msgpackz (located in the configuration directory) so that added plugins can be used in the following config files. |
| 19. | (config files) | If this is the first time Nushell has been launched, then it creates the configuration directory. "First launch" is determined by whether or not the configuration directory exists. |
| 20. | (config files) | Also, if this is the first time Nushell has been launched, creates a mostly empty (other than a few comments) env.nu and config .nu in that directory. |
| 21. | (config files) (default_env.nu) | Loads default environment variables from the internal default_env.nu. This file can be viewed with: config env --default | nu-highlight | less -R. |
| 22. | (config files) (env.nu) | Converts the PATH variable into a list so that it can be accessed more easily in the next step. |
| 23. | (config files) (env.nu) | Loads (parses and evaluates) the user's env.nu (the path to which was determined above). |
| 24. | (config files) (config.nu) | Loads a minimal $env.config record from the internal default_config.nu. This file can be viewed with: config nu --default | nu-highlight | less -R. Most values that are not defined in default_config.nu will be auto-populated into $env.config using their internal defaults as well. |
| 25. | (config files) (config.nu) | Loads (parses and evaluates) the user's config.nu (the path to which was determined above). |
| 26. | (config files) (login) | When Nushell is running as a login shell, loads the user's login.nu. |
| 27. | (config files) | Loops through the vendor autoload directories and loads any .nu files found. The directories are processed in the order found in $nu.vendor-autoload-dirs, and files in those directories are processed in alphabetical order. |
| 28. | (config files) | Loops through the user autoload directories and loads any .nu files found. The directories are processed in the order found in $nu.user-autoload-dirs, and files in those directories are processed in alphabetical order. |
| 29. | (repl) and (stdlib) | Shows the banner if configured. |
| 29. | (repl) | Nushell enters the normal commandline (REPL). |
| Mode | Command/Flags | Behavior |
|---|---|---|
| Normal Shell | nu (no flags) |
All launch steps except those marked with (login) occur. |
| Login Shell | nu --login/-l |
All launch steps occur. |
| Command-string | nu --commands <command-string> (or -c) |
All Launch stages except those marked with (config files) or (repl) occur. However, (default_env) and (plugin) do occur. The first allows the path ENV_CONVERSIONS defined there can take place. The second allows plugins to be used in the command-string. |
| Script file | nu <script_file> |
Same as with Command-string. |
| No config | nu -n |
(config files) stages do not occur, regardless of other flags. |
| No Standard Library | nu --no-std-lib |
Regardless of other flags, the steps marked (stdlib) will not occur. |
| Force config file | nu --config <file> |
Forces steps marked with (config.nu) above to run with the provided config <file>, unless -n was also specified |
| Force env file | nu --env-config <file> |
Forces steps marked with (default_env.nu) and (env.nu) above to run with the specified env <file>, unless -n was also specified |
-
nu:- ✅ Makes the Standard Library available
- ✅ Reads user's
plugin.msgpackzfile if it exists in the config directory - ✅ Sources the
default_env.nufile internally - ✅ Sources the user's
env.nufile if it exists in the config directory - ✅ Sources the
default_config.nufile internally - ✅ Sources user's
config.nufile if it exists if it exists in the config directory - ❌ Does not read
personal login.nufile - ✅ Enters the REPL
-
nu -c "ls":- ✅ Makes the Standard Library available
- ✅ Reads user's
plugin.msgpackzfile if it exists in the config directory - ✅ Sources the
default_env.nufile internally - ❌ Does not source the user's
env.nu - ❌ Does not read the internal
default_config.nufile - ❌ Does not read the user's
config.nufile - ❌ Does not read the user's
login.nufile - ✅ Runs the
lscommand and exits - ❌ Does not enter the REPL
-
nu -l -c "ls":- ✅ Makes the Standard Library available
- ✅ Reads user's
plugin.msgpackzfile if it exists in the config directory - ✅ Sources the
default_env.nufile internally - ✅ Sources the user's
env.nufile if it exists in the config directory - ✅ Sources the
default_config.nufile internally - ✅ Sources user's
config.nufile if it exists in the config directory - ✅ Sources the user's
login.nufile if it exists in the config directory - ✅ Runs the
lscommand and exits - ❌ Does not enter the REPL
-
nu -l -c "ls" --config foo_config.nu- Same as above, but reads an alternative config file named
foo_config.nufrom the config directory
- Same as above, but reads an alternative config file named
-
nu -n -l -c "ls":- ✅ Makes the Standard Library available
- ❌ Does not read user's
plugin.msgpackz - ❌ Does not read the internal
default_env.nu - ❌ Does not source the user's
env.nu - ❌ Does not read the internal
default_config.nufile - ❌ Does not read the user's
config.nufile - ❌ Does not read the user's
login.nufile - ✅ Runs the
lscommand and exits - ❌ Does not enter the REPL
-
nu test.nu:- ✅ Makes the Standard Library available
- ✅ Reads user's
plugin.msgpackzfile if it exists in the config directory - ✅ Sources the
default_env.nufile internally - ❌ Does not source the user's
env.nu - ❌ Does not read the internal
default_config.nufile - ❌ Does not read the user's
config.nufile - ❌ Does not read the user's
login.nufile - ✅ Runs
test.nufile as a script - ❌ Does not enter the REPL
-
nu --config foo_config.nu test.nu- ✅ Makes the Standard Library available
- ✅ Reads user's
plugin.msgpackzfile if it exists in the config directory - ✅ Sources the
default_env.nufile internally - ❌ Does not source the user's
env.nu(no--env-configwas specified) - ✅ Sources the
default_config.nufile internally. Note thatdefault_config.nuis always handled before a user's config - ✅ Sources user's
config.nufile if it exists in the config directory - ❌ Does not read the user's
login.nufile - ✅ Runs
test.nufile as a script - ❌ Does not enter the REPL
-
nu -n --no-std-lib(fastest REPL startup):- ❌ Does not make the Standard Library available
- ❌ Does not read user's
plugin.msgpackz - ❌ Does not read the internal
default_env.nu - ❌ Does not source the user's
env.nu - ❌ Does not read the internal
default_config.nufile - ❌ Does not read the user's
config.nufile - ❌ Does not read the user's
login.nufile - ✅ Enters the REPL
-
nu -n --no-std-lib -c "ls"(fastest command-string invocation):- ❌ Does not make the Standard Library available
- ❌ Does not read user's
plugin.msgpackz - ❌ Does not read the internal
default_env.nu - ❌ Does not source the user's
env.nu - ❌ Does not read the internal
default_config.nufile - ❌ Does not read the user's
config.nufile - ❌ Does not read the user's
login.nufile - ✅ Runs the
lscommand and exits - ❌ Does not enter the REPL
`docs/nushell/nushell.github.io/book/control_flow.md`:
```md
# Control Flow
Nushell provides several commands that help determine how different groups of code are executed. In programming languages this functionality is often referred to as _control flow_.
::: tip
One thing to note is that all of the commands discussed on this page use [blocks](/book/types_of_data.html#blocks). This means you can mutate [environmental variables](/book/environment.html) and other [mutable variables](/book/variables.html#mutable-variables) in them.
:::
## Already covered
Below we cover some commands related to control flow, but before we get to them, it's worthwhile to note there are several pieces of functionality and concepts that have already been covered in other sections that are also related to control flow or that can be used in the same situations. These include:
- Pipes on the [pipelines](/book/pipelines.html) page.
- Closures on the [types of data](/book/types_of_data.html) page.
- Iteration commands on the [working with lists](/book/working_with_lists.html) page. Such as:
- [`each`](/commands/docs/each.html)
- [`where`](/commands/docs/where.html)
- [`reduce`](/commands/docs/reduce.html)
## Choice (Conditionals)
The following commands execute code based on some given condition.
::: tip
The choice/conditional commands are expressions so they return values, unlike the other commands on this page. This means the following works.
```nu
'foo' | if $in == 'foo' { 1 } else { 0 } | $in + 2
# => 3
:::
if evaluates branching blocks of code based on the results of one or more conditions similar to the "if" functionality in other programming languages. For example:
if $x > 0 { 'positive' }Returns 'positive' when the condition is true ($x is greater than zero) and null when the condition is false ($x is less than or equal to zero).
We can add an else branch to the if after the first block which executes and returns the resulting value from the else block when the condition is false. For example:
if $x > 0 { 'positive' } else { 'non-positive' }This time it returns 'positive' when the condition is true ($x is greater than zero) and 'non-positive' when the condition is false ($x is less than or equal to zero).
We can also chain multiple ifs together like the following:
if $x > 0 { 'positive' } else if $x == 0 { 'zero' } else { "negative" }When the first condition is true ($x is greater than zero) it will return 'positive', when the first condition is false and the next condition is true ($x equals zero) it will return 'zero', otherwise it will return 'negative' (when $x is less than zero).
match executes one of several conditional branches based on the value given to match. You can also do some pattern matching to unpack values in composite types like lists and records.
Basic usage of match can conditionally run different code like a "switch" statement common in other languages. match checks if the value after the word match is equal to the value at the start of each branch before the => and if it does, it executes the code after that branch's =>.
match 3 {
1 => 'one',
2 => {
let w = 'w'
't' + $w + 'o'
},
3 => 'three',
4 => 'four'
}
# => threeThe branches can either return a single value or, as shown in the second branch, can return the results of a block.
You can also have a catch all condition for when the given value doesn't match any of the other conditions by having a branch whose matching value is _.
let foo = match 7 {
1 => 'one',
2 => 'two',
3 => 'three',
_ => 'other number'
}
$foo
# => other number(Reminder, match is an expression which is why we can assign the result to $foo here).
You can "unpack" values from types like lists and records with pattern matching. You can then assign variables to the parts you want to unpack and use them in the matched expressions.
let foo = { name: 'bar', count: 7 }
match $foo {
{ name: 'bar', count: $it } => ($it + 3),
{ name: _, count: $it } => ($it + 7),
_ => 1
}
# => 10The _ in the second branch means it matches any record with field name and count, not just ones where name is 'bar'.
You can also add an additional condition to each branch called a "guard" to determine if the branch should be matched. To do so, after the matched pattern put if and then the condition before the =>.
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
}
# => 14You can find more details about match in the pattern matching cookbook page.
The loop commands allow you to repeat a block of code multiple times.
The functionality of the loop commands is similar to commands that apply a closure over elements in a list or table like each or where and many times you can accomplish the same thing with either. For example:
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 │
# => ╰───┴───╯While it may be tempting to use loops if you're familiar with them in other languages, it is considered more in the Nushell-style (idiomatic) to use commands that apply closures when you can solve a problem either way. The reason for this is because of a pretty big downside with using loops.
The biggest downside of loops is that they are statements, unlike each which is an expression. Expressions, like each always result in some output value, however statements do not.
This means that they don't work well with immutable variables and using immutable variables is considered a more Nushell-style. Without a mutable variable declared beforehand in the example in the previous section, it would be impossible to use for to get the list of numbers with incremented numbers, or any value at all.
Statements also don't work in Nushell pipelines which require some output. In fact Nushell will give an error if you try:
[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.Because Nushell is very pipeline oriented, this means using expression commands like each is typically more natural than loop statements.
If loops have such a big disadvantage, why do they exist? Well, one reason is that closures, like each uses, can't modify mutable variables in the surrounding environment. If you try to modify a mutable variable in a closure you will get an error:
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
# => ╰────If you modify an environmental variable in a closure, you can, but it will only modify it within the scope of the closure, leaving it unchanged everywhere else. Loops, however, use blocks which means they can modify a regular mutable variable or an environmental variable within the larger scope.
mut result = []
for $it in [1 2 3] { $result = ($result | append ($it + 1)) }
$result
# => ╭───┬───╮
# => │ 0 │ 2 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => ╰───┴───╯When creating custom errors, loops have an advantage over pipelines because they don't fail evaluation.
Creating an error in a pipeline yields a pipeline evaluation error and the custom error:
[0] | each { error make { msg: "Custom error" } | default '' }
# => Error: nu::shell::eval_block_with_input
# =>
# => × Eval block failed with pipeline input
# => ╭─[entry #20:1:2]
# => 1 │ [0] | each { error make { msg: "Custom error" } | default '' }
# => · ┬
# => · ╰── source value
# => ╰────
# =>
# => Error:
# => × Custom error
# => ╭─[entry #20:1:14]
# => 1 │ [0] | each { error make { msg: "Custom error" } | default '' }
# => · ─────┬────
# => · ╰── originates from here
# => ╰────When using a loop, a for loop in this example, evaluation succeeds and only the created error occurs:
for x in [0] { error make { msg: "Custom error" } }
# => Error: nu::shell::error
# =>
# => × Custom error
# => ╭─[entry #18:1:16]
# => 1 │ for x in [0] { error make { msg: "Custom error" } }
# => · ─────┬────
# => · ╰── originates from here
# => ╰────for loops over a range or collection like a list or a table.
for x in [1 2 3] { $x * $x | print }
# => 1
# => 4
# => 9while loops the same block of code until the given condition is false.
mut x = 0; while $x < 10 { $x = $x + 1 }; $x
# => 10The "until" and other "while" commands
loop loops a block infinitely. You can use break (as described in the next section) to limit how many times it loops. It can also be handy for continuously running scripts, like an interactive prompt.
mut x = 0; loop { if $x > 10 { break }; $x = $x + 1 }; $x
# => 11break will stop executing the code in a loop and resume execution after the loop. Effectively "break"ing out of the loop.
for x in 1..10 { if $x > 3 { break }; print $x }
# => 1
# => 2
# => 3continue will stop execution of the current loop, skipping the rest of the code in the loop, and will go to the next loop. If the loop would normally end, like if for has iterated through all the given elements, or if while's condition is now false, it won't loop again and execution will continue after the loop block.
mut x = -1; while $x <= 6 { $x = $x + 1; if $x mod 3 == 0 { continue }; print $x }
# => 1
# => 2
# => 4
# => 5
# => 7error make creates an error that stops execution of the code and any code that called it, until either it is handled by a try block, or it ends the script and outputs the error message. This functionality is the same as "exceptions" in other languages.
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
# => ╰────The record passed to it provides some information to the code that catches it or the resulting error message.
You can find more information about error make and error concepts on the Creating your own errors page.
try will catch errors created anywhere in the try's code block and resume execution of the code after the block.
try { error make { msg: 'Some error info' }}; print 'Resuming'
# => ResumingThis includes catching built in errors.
try { 1 / 0 }; print 'Resuming'
# => ResumingThe resulting value will be nothing if an error occurs and the returned value of the block if an error did not occur.
If you include a catch block after the try block, it will execute the code in the catch block if an error occurred in the try block.
try { 1 / 0 } catch { 'An error happened!' } | $in ++ ' And now I am resuming.'
# => An error happened! And now I am resuming.It will not execute the catch block if an error did not occur.
try also works for external commands:
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 commandreturn Ends a closure or command early where it is called, without running the rest of the command/closure, and returns the given value. Not often necessary since the last value in a closure or command is also returned, but it can sometimes be convenient.
def 'positive-check' [it] {
if $it > 0 {
return 'positive'
};
'non-positive'
}positive-check 3
# => positive
positive-check (-3)
# => non-positive
let positive_check = {|elt| if $elt > 0 { return 'positive' }; 'non-positive' }
do $positive_check 3
# => positive
do $positive_check (-3)
# => non-positive
`docs/nushell/nushell.github.io/book/creating_errors.md`:
```md
# Creating Your Own Errors
Using the [metadata](metadata.md) information, you can create your own custom error messages. Error messages are built of multiple parts:
- The title of the error
- The label of error message, which includes both the text of the label and the span to underline
You can use the [`error make`](/commands/docs/error_make.md) command to create your own error messages. For example, let's say you had your own command called `my-command` and you wanted to give an error back to the caller about something wrong with a parameter that was passed in.
First, you can take the span of where the argument is coming from:
```nu
let span = (metadata $x).span;
Next, you can create an error using the error make command. This command takes in a record that describes the error to create:
error make {msg: "this is fishy", label: {text: "fish right here", span: $span } }Together with your custom command, it might look like this:
def my-command [x] {
let span = (metadata $x).span;
error make {
msg: "this is fishy",
label: {
text: "fish right here",
span: $span
}
}
}When called with a value, we'll now see an error message returned:
my-command 100
# => Error:
# => × this is fishy
# => ╭─[entry #5:1:1]
# => 1 │ my-command 100
# => · ─┬─
# => · ╰── fish right here
# => ╰────
`docs/nushell/nushell.github.io/book/custom_commands.md`:
```md
---
prev:
text: Programming in Nu
link: /book/programming_in_nu.md
---
# Custom Commands
As with any programming language, you'll quickly want to save longer pipelines and expressions so that you can call them again easily when needed.
This is where custom commands come in.
::: tip Note
Custom commands are similar to functions in many languages, but in Nushell, custom commands _act as first-class commands themselves_. As you'll see below, they are included in the Help system along with built-in commands, can be a part of a pipeline, are parsed in real-time for type errors, and much more.
:::
[[toc]]
## Creating and Running a Custom Command
Let's start with a simple `greet` custom command:
```nu
def greet [name] {
$"Hello, ($name)!"
}
Here, we define the greet command, which takes a single parameter name. Following this parameter is the block that represents what will happen when the custom command runs. When called, the custom command will set the value passed for name as the $name variable, which will be available to the block.
To run this command, we can call it just as we would call built-in commands:
greet "World"
# => Hello, World!You might notice that there isn't a return or echo statement in the example above.
Like some other languages, such as PowerShell and JavaScript (with arrow functions), Nushell features an implicit return, where the value of the final expression in the command becomes its return value.
In the above example, there is only one expression—The string. This string becomes the return value of the command.
greet "World" | describe
# => stringA typical command, of course, will be made up of multiple expressions. For demonstration purposes, here's a non-sensical command that has 3 expressions:
def eight [] {
1 + 1
2 + 2
4 + 4
}
eight
# => 8The return value, again, is simply the result of the final expression in the command, which is 4 + 4 (8).
Additional examples:
::: details Early return
Commands that need to exit early due to some condition can still return a value using the return statement.
def process-list [] {
let input_length = length
if $input_length > 10_000 {
print "Input list is too long"
return null
}
$in | each {|i|
# Process the list
$i * 4.25
}
}:::
::: details Suppressing the return value You'll often want to create a custom command that acts as a statement rather than an expression, and doesn't return a value.
You can use the ignore keyword in this case:
def create-three-files [] {
[ file1 file2 file3 ] | each {|filename|
touch $filename
} | ignore
}Without the ignore at the end of the pipeline, the command will return an empty list from the each statement.
You could also return a null as the final expression. Or, in this contrived example, use a for statement, which doesn't return a value (see next example).
:::
::: details Statements which don't return a value
Some keywords in Nushell are statements which don't return a value. If you use one of these statements as the final expression of a custom command, the return value will be null. This may be unexpected in some cases. For example:
def exponents-of-three [] {
for x in [ 0 1 2 3 4 5 ] {
3 ** $x
}
}
exponents-of-threeThe above command will not display anything, and the return value is empty, or null because for is a statement which doesn't return a value.
To return a value from an input list, use a filter such as the each command:
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 expression
```nu
# Return a random file in the current directory
def "random file" [] {
let files = (ls)
let num_files = ($files | length)
match $num_files {
0 => null # Return null for empty directory
_ => {
let random_file = (random int 0..($num_files - 1))
($files | get $random_file)
}
}
}In this case, the final expression is the match statement which can return:
nullif the directory is empty- Otherwise, a
recordrepresenting the randomly chosen file :::
Just as with built-in commands, the return value of a custom command can be passed into the next command in a pipeline. Custom commands can also accept pipeline input. In addition, whenever possible, pipeline input and output is streamed as it becomes available.
::: tip Important! See also: Pipelines :::
ls | get nameLet's move ls into a command that we've written:
def my-ls [] { ls }We can use the output from this command just as we would ls.
my-ls | get name
# => ╭───┬───────────────────────╮
# => │ 0 │ myscript.nu │
# => │ 1 │ myscript2.nu │
# => │ 2 │ welcome_to_nushell.md │
# => ╰───┴───────────────────────╯This lets us easily build custom commands and process their output. Remember that we don't use return statements like other languages. Instead, the implicit return allows us to build pipelines that output streams of data that can be connected to other pipelines.
::: tip Note
The ls content is still streamed in this case, even though it is in a separate command. Running this command against a long-directory on a slow (e.g., networked) filesystem would return rows as they became available.
:::
Custom commands can also take input from the pipeline, just like other commands. This input is automatically passed to the custom command's block.
Let's make our own command that doubles every value it receives as input:
def double [] {
each { |num| 2 * $num }
}Now, if we call the above command later in a pipeline, we can see what it does with the input:
[1 2 3] | double
# => ╭───┬───╮
# => │ 0 │ 2 │
# => │ 1 │ 4 │
# => │ 2 │ 6 │
# => ╰───┴───╯::: tip Cool! This command demonstrates both input and output streaming. Try running it with an infinite input:
1.. | each {||} | doubleEven though the input command hasn't ended, the double command can still receive and output values as they become available.
Press Ctrl+C to stop the command. :::
We can also store the input for later use using the $in variable:
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 │
# => ╰───┴──────┴──────┴──────┴───────────────╯In Nushell, a command name can be a string of characters. Here are some examples of valid command names: greet, get-size, mycommand123, my command, 命令 (English translation: "command"), and even 😊.
Strings which might be confused with other parser patterns should be avoided. For instance, the following command names might not be callable:
1,"1", or"1.5": Nushell will not allow numbers to be used as command names4MiBor"4MiB": Nushell will not allow filesizes to be used as command names"number#four"or"number^four": Carets and hash symbols are not allowed in command names-a,"{foo}","(bar)": Will not be callable, as Nushell will interpret them as flags, closures, or expressions.
While names like "+foo" might work, they are best avoided as the parser rules might change over time. When in doubt, keep command names as simple as possible.
::: tip
It's common practice in Nushell to separate the words of the command with - for better readability. For example get-size instead of getsize or get_size.
:::
::: tip
Because def is a parser keyword, the command name must be known at parse time. This means that command names may not be a variable or constant. For example, the following is not allowed:
let name = "foo"
def $name [] { foo }:::
You can also define subcommands of commands using a space. For example, if we wanted to add a new subcommand to str, we can create it by naming our subcommand starting with "str ". For example:
def "str mycommand" [] {
"hello"
}Now we can call our custom command as if it were a built-in subcommand of str:
str mycommandOf course, commands with spaces in their names are defined in the same way:
def "custom command" [] {
"This is a custom command with a space in the name!"
}In the def command, the parameters are defined in a list. This means that multiple parameters can be separated with spaces, commas, or line-breaks.
For example, here's a version of greet that accepts two names. Any of these three definitions will work:
# Spaces
def greet [name1 name2] {
$"Hello, ($name1) and ($name2)!"
}
# Commas
def greet [name1, name2] {
$"Hello, ($name1) and ($name2)!"
}
# Linebreaks
def greet [
name1
name2
] {
$"Hello, ($name1) and ($name2)!"
}The basic argument definitions used above are positional. The first argument passed into the greet command above is assigned to the name1 parameter (and, as mentioned above, the $name1 variable). The second argument becomes the name2 parameter and the $name2 variable.
By default, positional parameters are required. Using our previous definition of greet with two required, positional parameters:
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 <name1> <name2> . Use `--help` for more information.::: tip
Try typing a third name after this version of greet. Notice that the parser automatically detects the error and highlights the third argument as an error even before execution.
:::
We can define a positional parameter as optional by putting a question mark (?) after its name. For example:
def greet [name?: string] {
$"Hello, ($name | default 'You')"
}
greet
# => Hello, You::: tip
Notice that the name used to access the variable does not include the ?; only its definition in the command signature.
:::
When an optional parameter is not passed, its value in the command body is equal to null. The above example uses the default command to provide a default of "You" when name is null.
You could also compare the value directly:
def greet [name?: string] {
match $name {
null => "Hello! I don't know your name!"
_ => $"Hello, ($name)!"
}
}
greet
# => Hello! I don't know your name!If required and optional positional parameters are used together, then the required parameters must appear in the definition first.
You can also set a default value for the parameter when it is missing. Parameters with a default value are also optional when calling the command.
def greet [name = "Nushell"] {
$"Hello, ($name)!"
}You can call this command either without the parameter or with a value to override the default value:
greet
# => Hello, Nushell!
greet world
# => Hello, World!You can also combine a default value with a type annotation:
def congratulate [age: int = 18] {
$"Happy birthday! You are ($age) years old now!"
}For each parameter, you can optionally define its type. For example, you can write the basic greet command as:
def greet [name: string] {
$"Hello, ($name)"
}If a parameter is not type-annotated, Nushell will treat it as an any type. If you annotate a type on a parameter, Nushell will check its type when you call the function.
For example, let's say you wanted to only accept an int instead of a string:
def greet [name: int] {
$"hello ($name)"
}
greet WorldIf we try to run the above, Nushell will tell us that the types don't match:
Error: nu::parser::parse_mismatch
× Parse mismatch during operation.
╭─[entry #1:1:7]
1 │ greet World
· ──┬──
· ╰── expected int
╰────::: tip Cool! Type checks are a parser feature. When entering a custom command at the command-line, the Nushell parser can even detect invalid argument types in real-time and highlight them before executing the command.
The highlight style can be changed using a theme or manually using $env.config.color_config.shape_garbage.
:::
::: details List of Type Annotations Most types can be used as type-annotations. In addition, there are a few "shapes" which can be used. For instance:
number: Accepts either anintor afloatpath: A string where the~and.characters have special meaning and will automatically be expanded to the full-path equivalent. See Path in the Language Reference Guide for example usage.directory: A subset ofpath(above). Only directories will be offered when using tab-completion for the parameter. Expansions take place just as withpath.error: Available, but currently no known valid usage. See Error in the Language Reference Guide for more information.
The following types can be used for parameter annotations:
anybinaryboolcell-pathclosuredatetimedurationfilesizefloatglobintlistnothingrangerecordstringtable
:::
In addition to positional parameters, you can also define named flags.
For example:
def greet [
name: string
--age: int
] {
{
name: $name
age: $age
}
}In this version of greet, we define the name positional parameter as well as an age flag. The positional parameter (since it doesn't have a ?) is required. The named flag is optional. Calling the command without the --age flag will set $age to null.
The --age flag can go before or after the positional name. Examples:
greet Lucia --age 23
# => ╭──────┬───────╮
# => │ name │ Lucia │
# => │ age │ 23 │
# => ╰──────┴───────╯
greet --age 39 Ali
# => ╭──────┬─────╮
# => │ name │ Ali │
# => │ age │ 39 │
# => ╰──────┴─────╯
greet World
# => ╭──────┬───────╮
# => │ name │ World │
# => │ age │ │
# => ╰──────┴───────╯Flags can also be defined with a shorthand version. This allows you to pass a simpler flag as well as a longhand, easier-to-read flag.
Let's extend the previous example to use a shorthand flag for the age value:
def greet [
name: string
--age (-a): int
] {
{
name: $name
age: $age
}
}::: tip
The resulting variable is always based on the long flag name. In the above example, the variable continues to be $age. $a would not be valid.
:::
Now, we can call this updated definition using the shorthand flag:
greet Akosua -a 35
# => ╭──────┬────────╮
# => │ name │ Akosua │
# => │ age │ 35 │
# => ╰──────┴────────╯Flags can also be used as basic switches. When present, the variable based on the switch is true. When absent, it is false.
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!You can also assign it to true/false to enable/disable the flag:
greet Giulia --caps=false
# => Hello, Giulia!
greet Hiroshi --caps=true
# => HELLO, HIROSHI!::: tip Be careful of the following mistake:
greet Gabriel --caps trueTyping a space instead of an equals sign will pass true as a positional argument, which is likely not the desired result!
To avoid confusion, annotating a boolean type on a flag is not allowed:
def greet [
--caps: bool # Not allowed
] { ... }:::
Flags can contain dashes. They can be accessed by replacing the dash with an underscore in the resulting variable name:
def greet [
name: string
--all-caps
] {
let greeting = $"Hello, ($name)!"
if $all_caps {
$greeting | str upcase
} else {
$greeting
}
}There may be cases when you want to define a command which takes any number of positional arguments. We can do this with a "rest" parameter, using the following ... syntax:
def multi-greet [...names: string] {
for $name in $names {
print $"Hello, ($name)!"
}
}
multi-greet Elin Lars Erik
# => Hello, Elin!
# => Hello, Lars!
# => Hello, Erik!We could call the above definition of the greet command with any number of arguments, including none at all. All of the arguments are collected into $names as a list.
Rest parameters can be used together with positional parameters:
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
# ----- -------------------------
vip-greet Rahul Priya Arjun Anjali Vikram
# => Hello, Priya!
# => Hello, Arjun!
# => Hello, Anjali!
# => Hello, Vikram!
# => And a special welcome to our VIP today, Rahul!To pass a list to a rest parameter, you can use the spread operator (...). Using the vip-greet command definition above:
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!Custom commands defined with def --wrapped will collect any unknown flags and arguments into a
rest-parameter which can then be passed, via list-spreading, to an external command. This allows
a custom command to "wrap" and extend the external command while still accepting all of its original
parameters. For example, the external eza command displays a directory listing. By default, it displays
a grid arrangement:
eza commands
# => categories docs README.mdWe can define a new command ezal which will always display a long-listing, adding icons:
def --wrapped ezal [...rest] {
eza -l ...$rest
}:::note
You could also add --icons. We're omitting that in this example simply because those icons don't
display well in this guide.
:::
Notice that --wrapped forces any additional parameters into the rest parameter, so the command
can be called with any parameter that eza supports. Those additional parameters will be expanded via
the list-spreading operation ...$rest.
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 commandsThe custom command can check for certain parameters and change its behavior accordingly. For instance,
when using the -G option to force a grid, we can omit passing a -l to eza:
def --wrapped ezal [...rest] {
if '-G' in $rest {
eza ...$rest
} else {
eza -l --icons ...$rest
}
}
ezal -G commands
# => categories docs README.mdBy default, custom commands accept <any> type as pipeline input and likewise can output <any> type. But custom commands can also be given explicit signatures to narrow the types allowed.
For example, the signature for str stats looks like this:
def "str stats" []: string -> record { }Here, string -> record defines the allowed types of the pipeline input and output of the command:
- It accepts a
stringas pipeline input - It outputs a
record
If there are multiple input/output types, they can be placed within brackets and separated with commas or newlines, as in str join:
def "str join" [separator?: string]: [
list -> string
string -> string
] { }This indicates that str join can accept either a list<any> or a string as pipeline input. In either case, it will output a string.
Some commands don't accept or require data as pipeline input. In this case, the input type will be <nothing>. The same is true for the output type if the command returns null (e.g., rm or hide):
def xhide [module: string, members?]: nothing -> nothing { }::: tip Note
The example above is renamed xhide so that copying it to the REPL will not shadow the built-in hide command.
:::
Input-output signatures are shown in the help for a command (both built-in and custom) and can also be introspected through:
help commands | where name == <command_name>
scope commands | where name == <command_name>:::tip Cool! Input-Output signatures allow Nushell to catch two additional categories of errors at parse-time:
-
Attempting to return the wrong type from a command. For example:
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 # => ╰────
-
And attempting to pass an invalid type into a command:
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 # => ╰────
:::
In order to best help users understand how to use your custom commands, you can also document them with additional descriptions for the commands and parameters.
Run help vip-greet to examine our most recent command defined above:
Usage:
> vip-greet <vip> ...(names)
Flags:
-h, --help - Display the help message for this command
Parameters:
vip <string>
...names <string>
Input/output types:
╭───┬───────┬────────╮
│ # │ input │ output │
├───┼───────┼────────┤
│ 0 │ any │ any │
╰───┴───────┴────────╯
::: tip Cool!
You can see that Nushell automatically created some basic help for the command simply based on our definition so far. Nushell also automatically adds a --help/-h flag to the command, so users can also access the help using vip-greet --help.
:::
We can extend the help further with some simple comments describing the command and its parameters:
# Greet guests along with a VIP
#
# Use for birthdays, graduation parties,
# retirements, and any other event which
# celebrates an event # for a particular
# person.
def vip-greet [
vip: string # The special guest
...names: string # The other guests
] {
for $name in $names {
print $"Hello, ($name)!"
}
print $"And a special welcome to our VIP today, ($vip)!"
}Now run help vip-greet again to see the difference:
Greet guests along with a VIP
Use for birthdays, graduation parties,
retirements, and any other event which
celebrates an event # for a particular
person.
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 <vip>
Flags:
-h, --help - Display the help message for this command
Signatures:
<any> | vip-greet[ <string>] -> <any>
Parameters:
vip: <string> The special guest
...rest: <string> The other guests
Notice that the comments on the lines immediately before the def statement become a description of the command in the help system. Multiple lines of comments can be used. The first line (before the blank-comment line) becomes the Help description. This information is also shown when tab-completing commands.
The remaining comment lines become its extra_description in the help data.
::: tip Run:
scope commands
| where name == 'vip-greet'
| wrap helpThis will show the Help record that Nushell creates. :::
The comments following the parameters become their description. Only a single-line comment is valid for parameters.
::: tip Note
A Nushell comment that continues on the same line for argument documentation purposes requires a space before the # pound sign.
:::
Normally, environment variable definitions and changes are scoped within a block. This means that changes to those variables are lost when they go out of scope at the end of the block, including the block of a custom command.
def foo [] {
$env.FOO = 'After'
}
$env.FOO = "Before"
foo
$env.FOO
# => BeforeHowever, a command defined using def --env or export def --env (for a Module) will preserve the environment on the caller's side:
def --env foo [] {
$env.FOO = 'After'
}
$env.FOO = "Before"
foo
$env.FOO
# => AfterLikewise, changing the directory using the cd command results in a change of the $env.PWD environment variable. This means that directory changes (the $env.PWD variable) will also be reset when a custom command ends. The solution, as above, is to use def --env or export def --env.
def --env go-home [] {
cd ~
}
cd /
go-home
pwd
# => Your home directoryTo make custom commands available in future Nushell sessions, you'll want to add them to your startup configuration. You can add command definitions:
- Directly in your
config.nu - To a file sourced by your
config.nu - To a module imported by your
config.nu
See the configuration chapter for more details.
`docs/nushell/nushell.github.io/book/custom_completions.md`:
```md
# Custom Completions
Custom completions allow you to mix together two features of Nushell: custom commands and completions. With them, you're able to create commands that handle the completions for positional parameters and flag parameters. These custom completions work both for [custom commands](custom_commands.md) and [known external, or `extern`, commands](externs.md).
A completion is defined in two steps:
- Define a completion command (a.k.a. completer) that returns the possible values to suggest
- Attach the completer to the type annotation (shape) of another command's argument using `<shape>@<completer>`
Here's a simple example:
```nu
# Completion command
def animals [] { ["cat", "dog", "eel" ] }
# Command to be completed
def my-command [animal: string@animals] { print $animal }
my-command
# => cat dog eel
The first line defines a custom command which returns a list of three different animals. These are the possible values for the completion.
::: tip
To suppress completions for an argument (for example, an int that can accept any integer), define a completer that returns an empty list ([ ]).
:::
In the second line, string@animals tells Nushell two things—the shape of the argument for type-checking and the completer which will suggest possible values for the argument.
The third line is demonstration of the completion. Type the name of the custom command my-command, followed by a space, and then the Tab key. This displays a menu with the possible completions. Custom completions work the same as other completions in the system, allowing you to type e followed by the Tab key to complete "eel" automatically.
::: tip
When the completion menu is displayed, the prompt changes to include the | character by default. To change the prompt marker, modify the marker value of the record, where the name key is completion_menu, in the $env.config.menus list. See also the completion menu configuration.
:::
::: tip
To fall back to Nushell's built-in file completions, return null rather than a list of suggestions.
:::
If you want to choose how your completions are filtered and sorted, you can also return a record rather than a list. The list of completion suggestions should be under the completions key of this record. Optionally, it can also have, under the options key, a record containing the following optional settings:
sort- Set this tofalseto stop Nushell from sorting your completions. By default, this istrue, and completions are sorted according to$env.config.completions.sort.case_sensitive- Set totruefor the custom completions to be matched case sensitively,falseotherwise. Used for overriding$env.config.completions.case_sensitive.completion_algorithm- Set this toprefix,substring, orfuzzyto choose how your completions are matched against the typed text. Used for overriding$env.config.completions.algorithm.
Here's an example demonstrating how to set these options:
def animals [] {
{
options: {
case_sensitive: false,
completion_algorithm: substring,
sort: false,
},
completions: [cat, rat, bat]
}
}
def my-command [animal: string@animals] { print $animal }Now, if you try to complete A, you get the following completions:
>| my-command A
cat rat batBecause we made matching case-insensitive, Nushell will find the substring "a" in all of the completion suggestions. Additionally, because we set sort: false, the completions will be left in their original order. This is useful if your completions are already sorted in a particular order unrelated to their text (e.g. by date).
Since completion commands aren't meant to be called directly, it's common to define them in modules.
Extending the above example with a module:
module commands {
def animals [] {
["cat", "dog", "eel" ]
}
export def my-command [animal: string@animals] {
print $animal
}
}In this module, only the the custom command my-command is exported. The animals completion is not exported. This allows users of this module to call the command, and even use the custom completion logic, without having access to the completion command itself. This results in a cleaner and more maintainable API.
::: tip
Completers are attached to custom commands using @ at parse time. This means that, in order for a change to the completion command to take effect, the public custom command must be reparsed as well. Importing a module satisfies both of these requirements at the same time with a single use statement.
:::
It is possible to pass the context to the completion command. This is useful in situations where it is necessary to know previous arguments or flags to generate accurate completions.
Applying this concept to the previous example:
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)."
}
}Here, the command animal-names returns the appropriate list of names. $context is a string where the value is the command-line that has been typed so far.
>| my-command
cat dog eel
>| my-command dog
Lulu Enzo
>my-command dog enzo
The dog is named EnzoOn the second line, after pressing the tab key, the argument "my-command dog" is passed to the animal-names completer as context.
::: tip Completers can also obtain the current cursor position on the command-line using:
def completer [context:string, position:int] {}:::
Custom Completion and extern
A powerful combination is adding custom completions to known extern commands. These work the same way as adding a custom completion to a custom command: by creating the custom completion and then attaching it with a @ to the type of one of the positional or flag arguments of the extern.
If you look closely at the examples in the default config, you'll see this:
export extern "git push" [
remote?: string@"nu-complete git remotes", # the name of the remote
refspec?: string@"nu-complete git branches" # the branch / refspec
...
]Custom completions will serve the same role in this example as in the previous examples. The examples above call into two different custom completions, based on the position the user is currently in.
As an alternative to returning a list of strings, a completion function can also return a list of records with a value field as well as optional description and style fields. The style can be one of the following:
- A string with the foreground color, either a hex code or a color name such as
yellow. For a list of valid color names, seeansi --list. - A record with the fields
fg(foreground color),bg(background color), andattr(attributes such as underline and bold). This record is in the same format thatansi --escapeaccepts. See theansicommand reference for a list of possible values for theattrfield. - The same record, but converted to a JSON string.
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 Note With the following snippet:
def my-command [commit: string@my_commits] {
print $commit
}... be aware that, even though the completion menu will show you something like
>_ �[36mmy-command�[0m <TAB>
�[1;31m5c2464�[0m �[33mAdd .gitignore�[0m
�[1;4;48;2;102;7;140;32mf3a377 �[0m�[33mInitial commit�[0m
... only the value (i.e., "5c2464" or "f3a377") will be used in the command arguments! :::
External completers can also be integrated, instead of relying solely on Nushell ones.
For this, set the external_completer field in config.nu to a closure which will be evaluated if no Nushell completions were found.
$env.config.completions.external = {
enable: true
max_results: 100
completer: $completer
}You can configure the closure to run an external completer, such as carapace.
An external completer is a function that takes the current command as a string list, and outputs a list of records with value and description keys, like custom completion functions. When the closure returns null, it defaults to file completion.
::: tip Note
This closure will accept the current command as a list. For example, typing my-command --arg1 <tab> will receive [my-command --arg1 " "].
:::
This example will enable carapace external completions:
let carapace_completer = {|spans|
carapace $spans.0 nushell ...$spans | from json
}More examples of external completers can be found in the cookbook.
`docs/nushell/nushell.github.io/book/dataframes.md`:
```md
# Dataframes
::: warning Important!
This feature requires the Polars plugin. See the
[Plugins Chapter](plugins.md) to learn how to install it.
To test that this plugin is properly installed, run `help polars`.
:::
As we have seen so far, Nushell makes working with data its main priority.
`Lists` and `Tables` are there to help you cycle through values in order to
perform multiple operations or find data in a breeze. However, there are
certain operations where a row-based data layout is not the most efficient way
to process data, especially when working with extremely large files. Operations
like group-by or join using large datasets can be costly memory-wise, and may
lead to large computation times if they are not done using the appropriate
data format.
For this reason, the `DataFrame` structure was introduced to Nushell. A
`DataFrame` stores its data in a columnar format using as its base the [Apache
Arrow](https://arrow.apache.org/) specification, and uses
[Polars](https://github.com/pola-rs/polars) as the motor for performing
extremely [fast columnar operations](https://h2oai.github.io/db-benchmark/).
You may be wondering now how fast this combo could be, and how could it make
working with data easier and more reliable. For this reason, we'll start this
chapter by presenting benchmarks on common operations that are done when
processing data.
[[toc]]
## Benchmark Comparisons
For this little benchmark exercise we will be comparing native Nushell
commands, dataframe Nushell commands and [Python
Pandas](https://pandas.pydata.org/) commands. For the time being don't pay too
much attention to the [`Dataframe` commands](/commands/categories/dataframe.md). They will be explained in later
sections of this page.
::: tip System Details
The benchmarks presented in this section were run using a Macbook with a processor M1 pro and 32gb of ram. All examples were run on Nushell version 0.97 using `nu_plugin_polars 0.97`.
:::
### File Information
The file that we will be using for the benchmarks is the
[New Zealand business demography](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) dataset.
Feel free to download it if you want to follow these tests.
The dataset has 5 columns and 5,429,252 rows. We can check that by using the
`polars store-ls` command:
```nu
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
As of nushell 0.97, polars open will open as a lazy dataframe instead of a eager dataframe.
To open as an eager dataframe, use the --eager flag.
:::
We can have a look at the first lines of the file using first:
$df_0 | polars first
# => ╭───┬──────────┬─────────┬──────┬───────────┬──────────╮
# => │ # │ anzsic06 │ Area │ year │ geo_count │ ec_count │
# => ├───┼──────────┼─────────┼──────┼───────────┼──────────┤
# => │ 0 │ A │ A100100 │ 2000 │ 96 │ 130 │
# => ╰───┴──────────┴─────────┴──────┴───────────┴──────────╯...and finally, we can get an idea of the inferred data types:
$df_0 | polars schema
# => ╭───────────┬─────╮
# => │ anzsic06 │ str │
# => │ Area │ str │
# => │ year │ i64 │
# => │ geo_count │ i64 │
# => │ ec_count │ i64 │
# => ╰───────────┴─────╯To output more statistically correct timings, let's load and use the std bench command.
use std/benchWe are going to group the data by year, and sum the column geo_count.
First, let's measure the performance of a Nushell native commands pipeline.
bench -n 10 --pretty {
open 'Data7602DescendingYearOrder.csv'
| group-by year --to-table
| update items {|i|
$i.items.geo_count
| math sum
}
}
# => 3sec 268ms +/- 50msSo, 3.3 seconds to perform this aggregation.
Let's try the same operation in pandas:
('import pandas as pd
df = pd.read_csv("Data7602DescendingYearOrder.csv")
res = df.groupby("year")["geo_count"].sum()
print(res)'
| save load.py -f)And the result from the benchmark is:
bench -n 10 --pretty {
python load.py | complete | null
}
# => 1sec 322ms +/- 6msNot bad at all. Pandas managed to get it 2.6 times faster than Nushell. And with bigger files, the superiority of Pandas should increase here.
To finish the comparison, let's try Nushell dataframes. We are going to put
all the operations in one nu file, to make sure we are doing the correct
comparison:
( 'polars open Data7602DescendingYearOrder.csv
| polars group-by year
| polars agg (polars col geo_count | polars sum)
| polars collect'
| save load.nu -f )and the benchmark with dataframes (together with loading a new nushell and polars
instance for each test in order of honest comparison) is:
bench -n 10 --pretty {
nu load.nu | complete | null
}
# => 135ms +/- 4msThe polars dataframes plugin managed to finish operation 10 times
faster than pandas with python. Isn't that great?
As you can see, the Nushell's polars plugin is performant like polars itself.
Coupled with Nushell commands and pipelines, it is capable of conducting sophisticated
analysis without leaving the terminal.
Let's clean up the cache from the dataframes that we used during benchmarking.
To do that, let's stop the polars.
When we execute our next commands, we will start a new instance of plugin.
plugin stop polarsAfter seeing a glimpse of the things that can be done with Dataframe commands,
now it is time to start testing them. To begin let's create a sample
CSV file that will become our sample dataframe that we will be using along with
the examples. In your favorite file editor paste the next lines to create out
sample csv file.
("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
4,14,0.4,3.0,b,a,c,second
0,15,0.5,4.0,b,a,a,third
6,16,0.6,5.0,b,a,a,second
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"
| save --raw --force test_small.csv)Save the file and name it however you want to, for the sake of these examples
the file will be called test_small.csv.
Now, to read that file as a dataframe use the polars open command like
this:
let df_1 = polars open --eager test_small.csvThis should create the value $df_1 in memory which holds the data we just
created.
::: tip
The polars open command can read files in formats: csv, tsv, parquet, json(l), arrow, and avro.
:::
To see all the dataframes that are stored in memory you can use
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 │
# => ╰──────────────────────────────────────┴───────────┴─────────┴──────┴────────────────╯As you can see, the command shows the created dataframes together with basic information about them.
And if you want to see a preview of the loaded dataframe you can send the dataframe variable to the stream
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯With the dataframe in memory we can start doing column operations with the
DataFrame
::: tip
If you want to see all the dataframe commands that are available you
can use scope commands | where category =~ dataframe
:::
Let's start with basic aggregations on the dataframe. Let's sum all the columns
that exist in df by using the aggregate command
$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 │ │ │ │ │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴──────╯As you can see, the aggregate function computes the sum for those columns where
a sum makes sense. If you want to filter out the text column, you can select
the columns you want by using the polars select command
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────╯You can even store the result from this aggregation as you would store any other Nushell variable
let res = $df_1 | polars sum | polars select int_1 int_2 float_1 float_2::: tip
Type let res = !! and press enter. This will auto complete the previously
executed command. Note the space between = and !!.
:::
And now we have two dataframes stored in memory
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 │
╰──────────────────────────────────────┴───────────┴─────────┴──────┴────────────────╯Pretty neat, isn't it?
You can perform several aggregations on the dataframe in order to extract basic information from the dataframe and do basic data analysis on your brand new dataframe.
It is also possible to join two dataframes using a column as reference. We are
going to join our mini dataframe with another mini dataframe. Copy these lines
in another file and create the corresponding dataframe (for these examples we
are going to call it test_small_a.csv)
"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"
| save --raw --force test_small_a.csvWe use the polars open command to create the new variable
let df_2 = polars open --eager test_small_a.csvNow, with the second dataframe loaded in memory we can join them using the
column called int_1 from the left dataframe and the column int_1 from the
right dataframe
$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
In Nu when a command has multiple arguments that are expecting
multiple values we use brackets [] to enclose those values. In the case of
polars join we can join on multiple columns
as long as they have the same type.
:::
For example:
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────┴─────────┴───────────┴───────────╯By default, the join command does an inner join, meaning that it will keep the rows where both dataframes share the same value. You can select a left join to keep the missing rows from the left dataframe. You can also save this result in order to use it for further operations.
One of the most powerful operations that can be performed with a DataFrame is
the polars group-by. This command will allow you to perform aggregation operations
based on a grouping criteria. In Nushell, a GroupBy is a type of object that
can be stored and reused for multiple aggregations. This is quite handy, since
the creation of the grouped pairs is the most expensive operation while doing
group-by and there is no need to repeat it if you are planning to do multiple
operations with the same group condition.
To create a GroupBy object you only need to use the polars_group-by command
let group = $df_1 | polars group-by first
$group
# => ╭─────────────┬──────────────────────────────────────────────╮
# => │ LazyGroupBy │ apply aggregation to complete execution plan │
# => ╰─────────────┴──────────────────────────────────────────────╯When printing the GroupBy object we can see that it is in the background a
lazy operation waiting to be completed by adding an aggregation. Using the
GroupBy we can create aggregations on a column
$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" │
# => ╰────────────────┴───────────────────────────────────────────────────────────────────────────────────────╯or we can define multiple aggregations on the same or different columns
$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" │
# => ╰────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────╯As you can see, the GroupBy object is a very powerful variable and it is
worth keeping in memory while you explore your dataset.
It is also possible to construct dataframes from basic Nushell primitives, such
as integers, decimals, or strings. Let's create a small dataframe using the
command polars into-df.
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 For the time being, not all of Nushell primitives can be converted into a dataframe. This will change in the future, as the dataframe feature matures :::
We can append columns to a dataframe in order to create a new variable. As an
example, let's append two columns to our mini dataframe $df_3
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's powerful piping syntax allows us to create new dataframes by taking data from other dataframes and appending it to them. Now, if you list your dataframes you will see in total five dataframes
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 │
# => ╰──────────────────────────────────────┴─────────────┴─────────┴──────┴────────────────╯One thing that is important to mention is how the memory is being optimized
while working with dataframes, and this is thanks to Apache Arrow and
Polars. In a very simple representation, each column in a DataFrame is an
Arrow Array, which is using several memory specifications in order to maintain
the data as packed as possible (check Arrow columnar
format). The other
optimization trick is the fact that whenever possible, the columns from the
dataframes are shared between dataframes, avoiding memory duplication for the
same data. This means that dataframes $df_3 and $df_4 are sharing the same two
columns we created using the polars into-df command. For this reason, it isn't
possible to change the value of a column in a dataframe. However, you can
create new columns based on data from other columns or dataframes.
A Series is the building block of a DataFrame. Each Series represents a
column with the same data type, and we can create multiple Series of different
types, such as float, int or string.
Let's start our exploration with Series by creating one using the polars into-df
command:
let df_5 = [9 8 4] | polars into-df
$df_5
# => ╭───┬───╮
# => │ # │ 0 │
# => ├───┼───┤
# => │ 0 │ 9 │
# => │ 1 │ 8 │
# => │ 2 │ 4 │
# => ╰───┴───╯We have created a new series from a list of integers (we could have done the same using floats or strings)
Series have their own basic operations defined, and they can be used to create other Series. Let's create a new Series by doing some arithmetic on the previously created column.
let df_6 = $df_5 * 3 + 10
$df_6
# => ╭───┬────╮
# => │ # │ 0 │
# => ├───┼────┤
# => │ 0 │ 37 │
# => │ 1 │ 34 │
# => │ 2 │ 22 │
# => ╰───┴────╯Now we have a new Series that was constructed by doing basic operations on the previous variable.
::: tip
If you want to see how many variables you have stored in memory you can
use scope variables
:::
Let's rename our previous Series so it has a memorable name
let df_7 = $df_6 | polars rename "0" memorable
$df_7
# => ╭───┬───────────╮
# => │ # │ memorable │
# => ├───┼───────────┤
# => │ 0 │ 37 │
# => │ 1 │ 34 │
# => │ 2 │ 22 │
# => ╰───┴───────────╯We can also do basic operations with two Series as long as they have the same data type
$df_5 - $df_7
# => ╭───┬─────────────────╮
# => │ # │ sub_0_memorable │
# => ├───┼─────────────────┤
# => │ 0 │ -28 │
# => │ 1 │ -26 │
# => │ 2 │ -18 │
# => ╰───┴─────────────────╯And we can add them to previously defined dataframes
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 │
# => ╰───┴───┴───┴─────────╯The Series stored in a Dataframe can also be used directly, for example,
we can multiply columns a and b to create a new Series
$df_8.a * $df_8.b
# => ╭───┬─────────╮
# => │ # │ mul_a_b │
# => ├───┼─────────┤
# => │ 0 │ 2 │
# => │ 1 │ 12 │
# => │ 2 │ 30 │
# => ╰───┴─────────╯and we can start piping things in order to create new columns and dataframes
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's piping system can help you create very interesting workflows.
Series have another key use in when working with DataFrames, and it is the fact
that we can build boolean masks out of them. Let's start by creating a simple
mask using the equality operator
let mask_0 = $df_5 == 8
$mask_0
# => ╭───┬───────╮
# => │ # │ 0 │
# => ├───┼───────┤
# => │ 0 │ false │
# => │ 1 │ true │
# => │ 2 │ false │
# => ╰───┴───────╯and with this mask we can now filter a dataframe, like this
$df_9 | polars filter-with $mask_0
# => ╭───┬───┬───┬─────────┬────────╮
# => │ # │ a │ b │ new_col │ my_sum │
# => ├───┼───┼───┼─────────┼────────┤
# => │ 0 │ 3 │ 4 │ 8 │ 1 │
# => ╰───┴───┴───┴─────────┴────────╯Now we have a new dataframe with only the values where the mask was true.
The masks can also be created from Nushell lists, for example:
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 │
# => ╰───┴───┴───┴─────────┴────────╯To create complex masks, we have the AND
$mask_0 and $mask_1
# => ╭───┬─────────╮
# => │ # │ and_0_0 │
# => ├───┼─────────┤
# => │ 0 │ false │
# => │ 1 │ true │
# => │ 2 │ false │
# => ╰───┴─────────╯and OR operations
$mask_0 or $mask_1
# => ╭───┬────────╮
# => │ # │ or_0_0 │
# => ├───┼────────┤
# => │ 0 │ true │
# => │ 1 │ true │
# => │ 2 │ false │
# => ╰───┴────────╯We can also create a mask by checking if some values exist in other Series. Using the first dataframe that we created we can do something like this
let mask_2 = $df_1 | polars col first | polars is-in [b c]
$mask_2
# => ╭──────────┬─────────────────────────╮
# => │ input │ [table 2 rows] │
# => │ function │ Boolean(IsIn) │
# => │ options │ FunctionOptions { ... } │
# => ╰──────────┴─────────────────────────╯and this new mask can be used to filter the dataframe
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯Another operation that can be done with masks is setting or replacing a value
from a series. For example, we can change the value in the column first where
the value is equal to a
$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 │
# => ╰───┴────────╯Series can be also used as a way of filtering a dataframe by using them as a list of indices. For example, let's say that we want to get rows 1, 4, and 6 from our original dataframe. With that in mind, we can use the next command to extract that information
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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯The command polars take is very handy, especially if we mix it with other commands.
Let's say that we want to extract all rows for the first duplicated element for
column first. In order to do that, we can use the command polars arg-unique as
shown in the next example
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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯Or what if we want to create a new sorted dataframe using a column in specific.
We can use the arg-sort to accomplish that. In the next example we
can sort the dataframe by the column word
::: tip
The same result could be accomplished using the command sort
:::
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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯And finally, we can create new Series by setting a new value in the marked indices. Have a look at the next command
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 │
# => ╰───┴───────╯Another operation that can be done with Series is to search for unique values
in a list or column. Lets use again the first dataframe we created to test
these operations.
The first and most common operation that we have is value_counts. This
command calculates a count of the unique values that exist in a Series. For
example, we can use it to count how many occurrences we have in the column
first
$df_1 | polars get first | polars value-counts
# => ╭───┬───────┬───────╮
# => │ # │ first │ count │
# => ├───┼───────┼───────┤
# => │ 0 │ a │ 3 │
# => │ 1 │ b │ 4 │
# => │ 2 │ c │ 3 │
# => ╰───┴───────┴───────╯As expected, the command returns a new dataframe that can be used to do more queries.
Continuing with our exploration of Series, the next thing that we can do is
to only get the unique unique values from a series, like this
$df_1 | polars get first | polars unique
# => ╭───┬───────╮
# => │ # │ first │
# => ├───┼───────┤
# => │ 0 │ a │
# => │ 1 │ b │
# => │ 2 │ c │
# => ╰───┴───────╯Or we can get a mask that we can use to filter out the rows where data is
unique or duplicated. For example, we can select the rows for unique values
in column word
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴───────╯Or all the duplicated ones
$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 │
# => ╰───┴───────┴───────┴─────────┴─────────┴───────┴────────┴───────┴────────╯Lazy dataframes are a way to query data by creating a logical plan. The advantage of this approach is that the plan never gets evaluated until you need to extract data. This way you could chain together aggregations, joins and selections and collect the data once you are happy with the selected operations.
Let's create a small example of a lazy dataframe
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" │
# => ╰────────────────┴───────────────────────────────────────────────────────╯As you can see, the resulting dataframe is not yet evaluated, it stays as a set of instructions that can be done on the data. If you were to collect that dataframe you would get the next result
$lf_0 | polars collect
# => ╭───┬───┬───╮
# => │ # │ a │ b │
# => ├───┼───┼───┤
# => │ 0 │ 1 │ a │
# => │ 1 │ 2 │ b │
# => │ 2 │ 3 │ c │
# => │ 3 │ 4 │ d │
# => ╰───┴───┴───╯as you can see, the collect command executes the plan and creates a nushell table for you.
All dataframes operations should work with eager or lazy dataframes. They are converted in the background for compatibility. However, to take advantage of lazy operations if is recommended to only use lazy operations with lazy dataframes.
To find all lazy dataframe operations you can use
scope commands | where category =~ lazyframe | select name category usageWith your lazy frame defined we can start chaining operations on it. For example this
$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
You can use the line buffer editor to format your queries (ctr + o) easily
:::
This query uses the lazy reverse command to invert the dataframe and the
polars with-column command to create new two columns using expressions.
An expression is used to define an operation that is executed on the lazy
frame. When put together they create the whole set of instructions used by the
lazy commands to query the data. To list all the commands that generate an
expression you can use
scope commands | where category =~ expression | select name category usageIn our previous example, we use the polars col command to indicate that column a
will be multiplied by 2 and then it will be aliased to the name double_a.
In some cases the use of the polars col command can be inferred. For example,
using the polars select command we can use only a string
$lf_0 | polars select a | polars collect
# => ╭───┬───╮
# => │ # │ a │
# => ├───┼───┤
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => ╰───┴───╯or the polars col command
$lf_0 | polars select (polars col a) | polars collect
# => ╭───┬───╮
# => │ # │ a │
# => ├───┼───┤
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => ╰───┴───╯Let's try something more complicated and create aggregations from a lazy dataframe
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 │
# => ╰───┴──────┴─────┴──────╯And we could join on a lazy dataframe that hasn't being collected. Let's join the resulting group by to the original lazy frame
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 │
# => ╰───┴──────┴───────┴─────┴──────╯As you can see lazy frames are a powerful construct that will let you query data using a flexible syntax, resulting in blazing fast results.
So far we have seen quite a few operations that can be done using DataFrames
commands. However, the commands we have used so far are not all the commands
available to work with data and be assured that there will be more as the
feature becomes more stable.
The next list shows the available dataframe commands with their descriptions, and whenever possible, their analogous Nushell command.
::: warning This list may be outdated. To get the up-to-date command list, see Dataframe, Lazyframe, Dataframe Or Lazyframe, Expressions command categories. :::
| Command Name | Applies To | Description | Nushell Equivalent |
|---|---|---|---|
| polars agg | dataframe | Performs a series of aggregations from a group-by. | math |
| polars agg-groups | expression | Creates an agg_groups expression. | |
| polars all-false | dataframe | Returns true if all values are false. | |
| polars all-true | dataframe | Returns true if all values are true. | all |
| polars append | dataframe | Appends a new dataframe. | |
| polars arg-max | dataframe | Return index for max value in series. | |
| polars arg-min | dataframe | Return index for min value in series. | |
| polars arg-sort | dataframe | Returns indexes for a sorted series. | |
| polars arg-true | dataframe | Returns indexes where values are true. | |
| polars arg-unique | dataframe | Returns indexes for unique values. | |
| polars arg-where | any | Creates an expression that returns the arguments where expression is true. | |
| polars as | expression | Creates an alias expression. | |
| polars as-date | dataframe | Converts string to date. | |
| polars as-datetime | dataframe | Converts string to datetime. | |
| polars cache | dataframe | Caches operations in a new LazyFrame. | |
| polars cast | expression, dataframe | Cast a column to a different dtype. | |
| polars col | any | Creates a named column expression. | |
| polars collect | dataframe | Collect lazy dataframe into eager dataframe. | |
| polars columns | dataframe | Show dataframe columns. | |
| polars concat-str | any | Creates a concat string expression. | |
| polars concatenate | dataframe | Concatenates strings with other array. | |
| polars contains | dataframe | Checks if a pattern is contained in a string. | |
| polars count | expression | Creates a count expression. | |
| polars count-null | dataframe | Counts null values. | |
| polars cumulative | dataframe | Cumulative calculation for a series. | |
| polars datepart | expression | Creates an expression for capturing the specified datepart in a column. | |
| polars drop | dataframe | Creates a new dataframe by dropping the selected columns. | drop |
| polars drop-duplicates | dataframe | Drops duplicate values in dataframe. | |
| polars drop-nulls | dataframe | Drops null values in dataframe. | |
| polars dummies | dataframe | Creates a new dataframe with dummy variables. | |
| polars explode | expression, dataframe | Explodes a dataframe or creates a explode expression. | |
| polars expr-not | expression | Creates a not expression. | |
| polars fetch | dataframe | Collects the lazyframe to the selected rows. | |
| polars fill-nan | dataframe | Replaces NaN values with the given expression. | |
| polars fill-null | dataframe | Replaces NULL values with the given expression. | |
| polars filter | dataframe | Filter dataframe based in expression. | |
| polars filter-with | dataframe | Filters dataframe using a mask or expression as reference. | |
| polars first | expression, dataframe | Show only the first number of rows or create a first expression | first |
| polars flatten | expression, dataframe | An alias for polars explode. | |
| polars get | dataframe | Creates dataframe with the selected columns. | get |
| polars get-day | dataframe | Gets day from date. | |
| polars get-hour | dataframe | Gets hour from date. | |
| polars get-minute | dataframe | Gets minute from date. | |
| polars get-month | dataframe | Gets month from date. | |
| polars get-nanosecond | dataframe | Gets nanosecond from date. | |
| polars get-ordinal | dataframe | Gets ordinal from date. | |
| polars get-second | dataframe | Gets second from date. | |
| polars get-week | dataframe | Gets week from date. | |
| polars get-weekday | dataframe | Gets weekday from date. | |
| polars get-year | dataframe | Gets year from date. | |
| polars group-by | dataframe | Creates a group-by object that can be used for other aggregations. | group-by |
| polars implode | expression | Aggregates a group to a Series. | |
| polars into-df | any | Converts a list, table or record into a dataframe. | |
| polars into-lazy | any | Converts a dataframe into a lazy dataframe. | |
| polars into-nu | expression, dataframe | Converts a dataframe or an expression into into nushell value for access and exploration. | |
| polars is-duplicated | dataframe | Creates mask indicating duplicated values. | |
| polars is-in | expression, dataframe | Creates an is-in expression or checks to see if the elements are contained in the right series | in |
| polars is-not-null | expression, dataframe | Creates mask where value is not null. | |
| polars is-null | expression, dataframe | Creates mask where value is null. | <column_name> == null |
| polars is-unique | dataframe | Creates mask indicating unique values. | |
| polars join | dataframe | Joins a lazy frame with other lazy frame. | |
| polars last | expression, dataframe | Creates new dataframe with tail rows or creates a last expression. | last |
| polars lit | any | Creates a literal expression. | |
| polars lowercase | dataframe | Lowercase the strings in the column. | |
| polars max | expression, dataframe | Creates a max expression or aggregates columns to their max value. | |
| polars mean | expression, dataframe | Creates a mean expression for an aggregation or aggregates columns to their mean value. | |
| polars median | expression, dataframe | Median value from columns in a dataframe or creates expression for an aggregation | |
| polars melt | dataframe | Unpivot a DataFrame from wide to long format. | |
| polars min | expression, dataframe | Creates a min expression or aggregates columns to their min value. | |
| polars n-unique | expression, dataframe | Counts unique values. | |
| polars not | dataframe | Inverts boolean mask. | |
| polars open | any | Opens CSV, JSON, JSON lines, arrow, avro, or parquet file to create dataframe. | open |
| polars otherwise | any | Completes a when expression. | |
| polars quantile | expression, dataframe | Aggregates the columns to the selected quantile. | |
| polars query | dataframe | Query dataframe using SQL. Note: The dataframe is always named 'df' in your query's from clause. | |
| polars rename | dataframe | Rename a dataframe column. | rename |
| polars replace | dataframe | Replace the leftmost (sub)string by a regex pattern. | |
| polars replace-all | dataframe | Replace all (sub)strings by a regex pattern. | |
| polars reverse | dataframe | Reverses the LazyFrame | |
| polars rolling | dataframe | Rolling calculation for a series. | |
| polars sample | dataframe | Create sample dataframe. | |
| polars save | dataframe | Saves a dataframe to disk. For lazy dataframes a sink operation will be used if the file type supports it (parquet, ipc/arrow, csv, and ndjson). | |
| polars schema | dataframe | Show schema for a dataframe. | |
| polars select | dataframe | Selects columns from lazyframe. | select |
| polars set | dataframe | Sets value where given mask is true. | |
| polars set-with-idx | dataframe | Sets value in the given index. | |
| polars shape | dataframe | Shows column and row size for a dataframe. | |
| polars shift | dataframe | Shifts the values by a given period. | |
| polars slice | dataframe | Creates new dataframe from a slice of rows. | |
| polars sort-by | dataframe | Sorts a lazy dataframe based on expression(s). | sort |
| polars std | expression, dataframe | Creates a std expression for an aggregation of std value from columns in a dataframe. | |
| polars store-get | any, any | Gets a Dataframe or other object from the plugin cache. | |
| polars store-ls | Lists stored dataframes. | ||
| polars store-rm | any | Removes a stored Dataframe or other object from the plugin cache. | |
| polars str-lengths | dataframe | Get lengths of all strings. | |
| polars str-slice | dataframe | Slices the string from the start position until the selected length. | |
| polars strftime | dataframe | Formats date based on string rule. | |
| polars sum | expression, dataframe | Creates a sum expression for an aggregation or aggregates columns to their sum value. | |
| polars summary | dataframe | For a dataframe, produces descriptive statistics (summary statistics) for its numeric columns. | |
| polars take | dataframe | Creates new dataframe using the given indices. | |
| polars unique | dataframe | Returns unique values from a dataframe. | uniq |
| polars uppercase | dataframe | Uppercase the strings in the column. | |
| polars value-counts | dataframe | Returns a dataframe with the counts for unique values in series. | |
| polars var | expression, dataframe | Create a var expression for an aggregation. | |
| polars when | expression | Creates and modifies a when expression. | |
| polars with-column | dataframe | Adds a series to the dataframe. | insert <column_name> <value> | upsert <column_name> { <new_value> } |
We hope that by the end of this page you have a solid grasp of how to use the dataframe commands. As you can see they offer powerful operations that can help you process data faster and natively.
However, the future of these dataframes is still very experimental. New commands and tools that take advantage of these commands will be added as they mature.
Check this chapter, as well as our Blog, regularly to learn about new dataframes features and how they can help you process data faster and efficiently.
`docs/nushell/nushell.github.io/book/default_shell.md`:
```md
---
prev:
text: Installing Nu
link: /book/installation.md
next:
text: Getting Started
link: /book/getting_started.md
---
# Default Shell
## Setting Nu as default shell on your terminal
| Terminal | Platform | Instructions |
| :--------------: | ------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| GNOME Terminal | Linux & BSDs | Open `Edit > Preferences`. In the right-hand panel, select the `Command` tab, tick `Run a custom command instead of my shell`, and set `Custom command` to the path to Nu. |
| GNOME Console | Linux & BSDs | Type the command `gsettings set org.gnome.Console shell "['/usr/bin/nu']"` (replace `/usr/bin/nu` with the path to Nu). Equivalently, use [dconf Editor](https://apps.gnome.org/DconfEditor/) to edit the `/org/gnome/Console/shell` key. |
| Kitty | Linux & BSDs | Press `Ctrl`+`Shift`+`F2` to open `kitty.conf`. Go to `shell` variable, uncomment the line and replace the `.` with the path to Nu. |
| Konsole | Linux & BSDs | Open `Settings > Edit Current Profile`. Set `Command` to the path to Nu. |
| XFCE Terminal | Linux & BSDs | Open `Edit > Preferences`. Check `Run a custom command instead of my shell`, and set `Custom command` to the path to Nu. |
| Terminal.app | macOS | Open `Terminal > Preferences`. Ensure you are on the `Profiles` tab, which should be the default tab. In the right-hand panel, select the `Shell` tab. Tick `Run command`, put the path to Nu in the textbox, and untick `Run inside shell`. |
| iTerm2 | macOS | Open `iTerm > Preferences`. Select the `Profiles` tab. In the right-hand panel under `Command`, change the dropdown from `Login Shell` to `Custom Shell`, and put the path to Nu in the textbox. |
| Windows Terminal | Windows | Press `Ctrl`+`,` to open `Settings`. Go to `Add a new profile > New empty profile`. Fill in the 'Name' and enter path to Nu in the 'Command line' textbox. Go to `Startup` option and select Nu as the 'Default profile'. Hit `Save`. |
## Setting Nu as login shell (Linux, BSD & macOS)
::: warning
Nu is not intended to be POSIX compliant.
Be aware that some programs on your system (or their documentation) might assume that your login shell is [POSIX](https://en.wikipedia.org/wiki/POSIX) compatible.
Breaking that assumption can lead to unexpected issues. See [Configuration - Login Shell](./configuration.md#configuring-nu-as-a-login-shell) for more details.
:::
To set the login shell you can use the [`chsh`](https://linux.die.net/man/1/chsh) command.
Some Linux distributions have a list of valid shells located in `/etc/shells` and will disallow changing the shell until Nu is in the whitelist.
You may see an error similar to the one below if you haven't updated the `shells` file:
@[code](@snippets/installation/chsh_invalid_shell_error.sh)
You can add Nu to the list of allowed shells by appending your Nu binary to the `shells` file.
The path to add can be found with the command `which nu`, usually it is `$HOME/.cargo/bin/nu`.
docs/nushell/nushell.github.io/book/design_notes.md:
---
prev:
text: Nushell operator map
link: /book/nushell_operator_map.md
next:
text: How Nushell Code Gets Run
link: /book/how_nushell_code_gets_run.md
---
# Design Notes
This chapter intends to give more in-depth overview of certain aspects of Nushell's design. The topics are not necessary for a basic usage, but reading them will help you understand how Nushell works and why.
We intend to expand this chapter in the future. If there is some topic that you find confusing and hard to understand, let us know. It might be a good candidate for a page here.
[How Nushell Code Gets Run](how_nushell_code_gets_run.md) explains what happens when you run Nushell source code. It explains how Nushell is in many ways closer to classic compiled languages, like C or Rust, than to other shells and dynamic languages and hopefully clears some confusion that stems from that.
docs/nushell/nushell.github.io/book/directory_stack.md:
# Directory Stack
Like some other shells, Nushell provides a Directory Stack feature for easily switching between multiple directories. In Nushell, this feature is part of the [Standard Library](./standard_library.md) and can be accessed in several ways.
::: note
In Nushell, the "stack" is represented as a `list`, but the overall functionality is similar to that of other shells.
:::
[[toc]]
## `dirs` Module and Commands
To use the `dirs` command and its subcommands, first import the module using:
```nu
use std/dirs::: tip To make the feature available whenever you start Nushell, add the above command to your startup configuration. :::
This makes several new commands available:
| Command | Description |
|---|---|
dirs |
Lists the directories on the stack |
dirs add |
Adds one or more directories to the list. The first directory listed becomes the new active directory. Similar to the pushd command in some other shells. |
dirs drop |
Drops the current directory from the list. The previous directory in the list becomes the new active directory. Similar to the popd command in some other shells. |
dirs goto |
Jumps to directory by using its index in the list |
dirs next |
Makes the next directory on the list the active directory. If the current active directory is the last in the list, then cycle to the start of the list. |
dirs prev |
Makes the previous directory on the list the active directory. If the current active directory is the first in the list, then cycle to the end of the list. |
When we start using dirs, there is only one directory in the list, the active one. You can, as always, change this directory using the cd command.
cd ~
use std/dirs
dirs
# => ╭───┬────────┬─────────────────────────────────╮
# => │ # │ active │ path │
# => ├───┼────────┼─────────────────────────────────┤
# => │ 0 │ true │ /home/myuser │
# => ╰───┴────────┴─────────────────────────────────╯
cd ~/src/repo/nushell
dirs
# => ╭───┬────────┬─────────────────────────────────╮
# => │ # │ active │ path │
# => ├───┼────────┼─────────────────────────────────┤
# => │ 0 │ true │ /home/myuser/repo/nushell │
# => ╰───┴────────┴─────────────────────────────────╯Notice that cd only changes the Active directory.
To add the current directory to the list, change to a new active directory using the dirs add command:
dirs add ../reedline
dirs
# => ╭───┬────────┬──────────────────────────────────╮
# => │ # │ active │ path │
# => ├───┼────────┼──────────────────────────────────┤
# => │ 0 │ false │ /home/myuser/src/repo/nushell │
# => │ 1 │ true │ /home/myuser/src/repo/reedline │
# => ╰───┴────────┴──────────────────────────────────╯Let's go ahead and add a few more commonly used directories to the list:
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 │
# => ╰───┴────────┴────────────────────────────────────╯We can now switch between them easily using dirs next, dirs prev or dirs goto:
dirs next
# Active was 3, is now 0
pwd
# => /home/myuser/src/repo/nushell
dirs goto 2
# => /home/myuser/src/repo/nu_scriptsWhen you have finished your work in a directory, you can drop it from the list using:
dirs drop
dirs
# => ╭───┬────────┬──────────────────────────────────╮
# => │ # │ active │ path │
# => ├───┼────────┼──────────────────────────────────┤
# => │ 0 │ false │ /home/myuser/src/repo/nushell │
# => │ 1 │ true │ /home/myuser/src/repo/reedline │
# => │ 2 │ false │ /home/myuser │
# => ╰───┴────────┴──────────────────────────────────╯When we drop nu_scripts from the list, the previous directory (reedline) becomes active.
Some users may prefer to think of this feature as multiple "shells within shells", where each has its own directory.
The Standard Library provides a set of aliases that can be used in place of the dirs commands above.
Import them using:
use std/dirs shells-aliases *The built-in aliases are:
| Alias | Description |
|---|---|
shells |
in place of dirs to list current "shells"/directories. |
enter |
in place of dirs add to enter a new "shell"/dir. |
dexit |
in place of dirs drop to exit a "shell"/dir. |
g |
as an alias for dirs goto. |
n |
for dirs next |
p |
for dirs prev |
Of course, you can also define your own aliases if desired.
`docs/nushell/nushell.github.io/book/environment.md`:
```md
# Environment
A common task in a shell is to control the environment that external applications will use. This is often done automatically, as the environment is packaged up and given to the external application as it launches. Sometimes, though, we want to have more precise control over what environment variables an application sees.
You can see the current environment variables in the $env variable:
```nu
$env | table -e
# => ╭──────────────────────────────────┬───────────────────────────────────────────╮
# => │ │ ╭──────┬────────────────────────────────╮ │
# => │ ENV_CONVERSIONS │ │ │ ╭─────────────┬──────────────╮ │ │
# => │ │ │ PATH │ │ from_string │ <Closure 32> │ │ │
# => │ │ │ │ │ to_string │ <Closure 34> │ │ │
# => │ │ │ │ ╰─────────────┴──────────────╯ │ │
# => │ │ │ │ ╭─────────────┬──────────────╮ │ │
# => │ │ │ Path │ │ from_string │ <Closure 36> │ │ │
# => │ │ │ │ │ to_string │ <Closure 38> │ │ │
# => │ │ │ │ ╰─────────────┴──────────────╯ │ │
# => │ │ ╰──────┴────────────────────────────────╯ │
# => │ HOME │ /Users/jelle │
# => │ LSCOLORS │ GxFxCxDxBxegedabagaced │
# => | ... | ... |
# => ╰──────────────────────────────────┴───────────────────────────────────────────╯
In Nushell, environment variables can be any value and have any type. You can see the type of an env variable with the describe command, for example: $env.PROMPT_COMMAND | describe.
To send environment variables to external applications, the values will need to be converted to strings. See Environment variable conversions on how this works.
The environment is initially created from the Nu configuration files and from the environment that Nu is run inside of.
There are several ways to set an environment variable:
Using the $env.VAR = "val" is the most straightforward method
$env.FOO = 'BAR'So, if you want to extend the Windows Path variable, for example, you could do that as follows.
$env.Path = ($env.Path | prepend 'C:\path\you\want\to\add')Here we've prepended our folder to the existing folders in the Path, so it will have the highest priority.
If you want to give it the lowest priority instead, you can use the append command.
If you have more than one environment variable you'd like to set, you can use load-env to create a table of name/value pairs and load multiple variables at the same time:
load-env { "BOB": "FOO", "JAY": "BAR" }These are defined to be active only temporarily for a duration of executing a code block. See Single-use environment variables for details.
Calling a Command Defined with def --env
See Defining environment from custom commands for details.
See Modules for details.
Individual environment variables are fields of a record that is stored in the $env variable and can be read with $env.VARIABLE:
$env.FOO
# => BARSometimes, you may want to access an environmental variable which might be unset. Consider using the question mark operator to avoid an error:
$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"
# => BARAlternatively, you can check for the presence of an environmental variable with in:
$env.FOO
# => BAR
if "FOO" in $env {
echo $env.FOO
}
# => BARNushell's $env is case-insensitive, regardless of the OS. Although $env behaves mostly like a record, it is special in that it ignores the case when reading or updating. This means, for example, you can use any of $env.PATH, $env.Path, or $env.path, and they all work the same on any OS.
If you want to read $env in a case-sensitive manner, use $env | get --sensitive.
When you set an environment variable, it will be available only in the current scope (the block you're in and any block inside of it).
Here is a small example to demonstrate the environment scoping:
$env.FOO = "BAR"
do {
$env.FOO = "BAZ"
$env.FOO == "BAZ"
}
# => true
$env.FOO == "BAR"
# => trueSee also: Changing the Environment in a Custom Command.
A common task in a shell is to change the directory using the cd command. In Nushell, calling cd is equivalent to setting the PWD environment variable. Therefore, it follows the same rules as other environment variables (for example, scoping).
A common shorthand to set an environment variable once is available, inspired by Bash and others:
FOO=BAR $env.FOO
# => BARYou can also use with-env to do the same thing more explicitly:
with-env { FOO: BAR } { $env.FOO }
# => BARThe with-env command will temporarily set the environment variable to the value given (here: the variable "FOO" is given the value "BAR"). Once this is done, the block will run with this new environment variable set.
You can also set environment variables at startup so they are available for the duration of Nushell running. To do this, set an environment variable inside the Nu configuration file.
For example:
# In config.nu
$env.FOO = 'BAR'You can set the ENV_CONVERSIONS environment variable to convert other environment variables between a string and a value.
For example, the default environment config includes conversion of PATH (and Path used on Windows) environment variables from a string to a list.
After both env.nu and config.nu are loaded, any existing environment variable specified inside ENV_CONVERSIONS will be translated according to its from_string field into a value of any type.
External tools require environment variables to be strings, therefore, any non-string environment variable needs to be converted first.
The conversion of value -> string is set by the to_string field of ENV_CONVERSIONS and is done every time an external command is run.
Let's illustrate the conversions with an example. Put the following in your config.nu:
$env.ENV_CONVERSIONS = {
# ... you might have Path and PATH already there, add:
FOO : {
from_string: { |s| $s | split row '-' }
to_string: { |v| $v | str join '-' }
}
}Now, within a Nushell instance:
with-env { FOO : 'a-b-c' } { nu } # runs Nushell with FOO env. var. set to 'a-b-c'
$env.FOO
# => 0 a
# => 1 b
# => 2 cYou can see the $env.FOO is now a list in a new Nushell instance with the updated config.
You can also test the conversion manually by
do $env.ENV_CONVERSIONS.FOO.from_string 'a-b-c'Now, to test the conversion list -> string, run:
nu -c '$env.FOO'
# => a-b-cBecause nu is an external program, Nushell translated the [ a b c ] list according to ENV_CONVERSIONS.FOO.to_string and passed it to the nu process.
Running commands with nu -c does not load the config file, therefore the env conversion for FOO is missing and it is displayed as a plain string -- this way we can verify the translation was successful.
You can also run this step manually by do $env.ENV_CONVERSIONS.FOO.to_string [a b c]
(Important! The environment conversion string -> value happens after the env.nu and config.nu are evaluated. All environment variables in env.nu and config.nu are still strings unless you set them manually to some other values.)
You can remove an environment variable only if it was set in the current scope via hide-env:
$env.FOO = 'BAR'
# => ...
hide-env FOOThe hiding is also scoped which both allows you to remove an environment variable temporarily and prevents you from modifying a parent environment from within a child scope:
$env.FOO = 'BAR'
do {
hide-env FOO
# $env.FOO does not exist
}
$env.FOO
# => BAR
`docs/nushell/nushell.github.io/book/explore.md`:
```md
# `explore`
Explore is a table pager, just like `less` but for table structured data.
## Signature
`> explore --head --index --reverse --peek`
### Parameters
- `--head {bool}`: Show or hide column headers (default true)
- `--index, -i`: Show row indexes when viewing a list
- `--tail, -t`: Start with the viewport scrolled to the bottom
- `--peek, -p`: When quitting, output the value of the cell the cursor was on
## Get Started
```nu
ls | explore -i
So the main point of explore is :table (Which you see on the above screenshot).
You can interact with it via <Left>, <Right>, <Up>, <Down> arrow keys. It also supports the Vim keybindings <h>, <j>, <k>, and <l>, <Ctrl-f> and <Ctrl-b>, and it supports the Emacs keybindings <Ctrl-v>, <Alt-v>, <Ctrl-p>, and <Ctrl-n>.
You can inspect a underlying values by entering into cursor mode. You can press either <i> or <Enter> to do so.
Then using arrow keys you can choose a necessary cell.
And you'll be able to see it's underlying structure.
You can obtain more information about the various aspects of it by :help.
explore has a list of built in commands you can use. Commands are run through pressing <:> and then a command name.
To find out the comprehensive list of commands you can type :help.
You can configure many things (including styles and colors), via config.
You can find an example configuration in default-config.nu.
$nu | explore --peekThere's an interactive environment which you can use to navigate through data using nu.
Remember you can combine it with --peek.
`docs/nushell/nushell.github.io/book/externs.md`:
```md
# Externs
Using external commands (a.k.a. binaries or applications) is a fundamental feature of any shell. Nushell allows custom commands to take advantage of many of its features, such as:
- Parse-time type checking
- Completions
- Syntax highlighting
Support for these features is provided using the `extern` keyword, which allows a full signature to be defined for external commands.
Here's a short example for the `ssh` command:
```nu
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 # Destination Host
-p: int # Destination Port
-i: string@complete_ssh_identity # Identity File
]
}
use "ssh extern" ssh
Notice that the syntax here is similar to that of the def keyword when defining a custom command. You can describe flags, positional parameters, types, completers, and more.
This implementation:
-
Will provide
-pand-i(with descriptions) as possible completions forssh -. -
Will perform parse-time type checking. Attempting to use a non-
intfor the port number will result in an error (and error-condition syntax highlighting). -
Will offer parse-time syntax highlighting based on the shapes of the arguments.
-
Will offer any private key files in
~/.sshas completion values for the-i(identity) option -
Will not offer completions for the destination host. Without a completer that returns an empty list, Nushell would attempt to use the default "File" completer.
See the Nu_scripts Repository for an implementation that retrieves hosts from the SSH config files.
::: tip Note
A Nushell comment that continues on the same line for argument documentation purposes requires a space before the # pound sign.
:::
Positional parameters can be made optional with a ? (as seen above). The remaining (rest) parameters can be matched with ... before the parameter name. For example:
export extern "git add" [
...pathspecs: path
# …
]There are a few limitations to the current extern syntax. In Nushell, flags and positional arguments are very flexible—flags can precede positional arguments, flags can be mixed into positional arguments, and flags can follow positional arguments. Many external commands are not this flexible. There is not yet a way to require a particular ordering of flags and positional arguments to the style required by the external.
The second limitation is that some externals require flags to be passed using = to separate the flag and the value. In Nushell, the = is a convenient optional syntax and there's currently no way to require its use.
In addition, externals called via the caret sigil (e.g., ^ssh) are not recognized by extern.
Finally, some external commands support -long arguments using a single leading hyphen. Nushell extern syntax can not yet represent these arguments.
`docs/nushell/nushell.github.io/book/getting_started.md`:
```md
---
prev:
text: Default Shell
link: /book/default_shell.md
next:
text: Quick Tour
link: /book/quick_tour.md
---
# Getting Started
Let's get started! :elephant:
The next sections will give you a [short tour of Nushell by example](quick_tour.md) (including how to get help from within Nushell) and show you how to [move around your file system](moving_around.md).
Then, because Nushell takes some design decisions that are quite different from typical shells or dynamic scripting languages, make sure to check out [Thinking in Nu](thinking_in_nu.md) where we explain some of these concepts.
docs/nushell/nushell.github.io/book/hooks.md:
# Hooks
Hooks allow you to run a code snippet at some predefined situations.
They are only available in the interactive mode ([REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)), they do not work if you run Nushell with a script (`nu script.nu`) or command (`nu -c "print foo"`) argument.
Currently, we support these types of hooks:
- `pre_prompt` : Triggered before the prompt is drawn
- `pre_execution` : Triggered before the line input starts executing
- `env_change` : Triggered when an environment variable changes
- `display_output` : A block that the output is passed to
- `command_not_found` : Triggered when a command is not found
To make it clearer, we can break down Nushell's execution cycle.
The steps to evaluate one line in the REPL mode are as follows:
1. Check for `pre_prompt` hooks and run them
1. Check for `env_change` hooks and run them
1. Display prompt and wait for user input
1. After user typed something and pressed "Enter": Check for `pre_execution` hooks and run them
1. Parse and evaluate user input
1. If a command is not found: Run the `command_not_found` hook. If it returns a string, show it.
1. If `display_output` is defined, use it to print command output
1. Return to 1.
## Basic Hooks
To enable hooks, define them in your [config](configuration.md):
```nu
$env.config.hooks = {
pre_prompt: [{ print "pre prompt hook" }]
pre_execution: [{ print "pre exec hook" }]
env_change: {
PWD: [{|before, after| print $"changing directory from ($before) to ($after)" }]
}
}Try putting the above into your config, running Nushell and moving around your filesystem.
When you change a directory, the PWD environment variable changes and the change triggers the hook with the previous and the current values stored in before and after variables, respectively.
Instead of defining just a single hook per trigger, it is possible to define a list of hooks which will run in sequence:
$env.config.hooks = {
pre_prompt: [
{ print "pre prompt hook" }
{ print "pre prompt hook2" }
]
pre_execution: [
{ print "pre exec hook" }
{ print "pre exec hook2" }
]
env_change: {
PWD: [
{|before, after| print $"changing directory from ($before) to ($after)" }
{|before, after| print $"changing directory from ($before) to ($after) 2" }
]
}
}Instead of replacing all hooks, you can append a new hook to existing configuration:
$env.config.hooks.pre_execution = $env.config.hooks.pre_execution | append { print "pre exec hook3" }One feature of the hooks is that they preserve the environment.
Environment variables defined inside the hook block will be preserved in a similar way as def --env.
You can test it with the following example:
$env.config = ($env.config | upsert hooks {
pre_prompt: { $env.SPAM = "eggs" }
})
$env.SPAM
# => eggsThe hook blocks otherwise follow the general scoping rules, i.e., commands, aliases, etc. defined within the block will be thrown away once the block ends.
pre_execution hooks can inspect the to-be-executed command through the commandline command.
For example, to print the command being executed:
$env.config = (
$env.config
| upsert hooks.pre_execution [ {||
$env.repl_commandline = (commandline)
print $"Command: ($env.repl_commandline)"
} ]
)
print (1 + 3)
# => Command: print (1 + 3)
# => 4One thing you might be tempted to do is to activate an environment whenever you enter a directory:
$env.config = ($env.config | upsert hooks {
env_change: {
PWD: [
{|before, after|
if $after == /some/path/to/directory {
load-env { SPAM: eggs }
}
}
]
}
})This won't work because the environment will be active only within the if block.
In this case, you could easily rewrite it as load-env (if $after == ... { ... } else { {} }) but this pattern is fairly common and later we'll see that not all cases can be rewritten like this.
To deal with the above problem, we introduce another way to define a hook - a record:
$env.config = ($env.config | upsert hooks {
env_change: {
PWD: [
{
condition: {|before, after| $after == /some/path/to/directory }
code: {|before, after| load-env { SPAM: eggs } }
}
]
}
})When the hook triggers, it evaluates the condition block.
If it returns true, the code block will be evaluated.
If it returns false, nothing will happen.
If it returns something else, an error will be thrown.
The condition field can also be omitted altogether in which case the hook will always evaluate.
The pre_prompt and pre_execution hook types also support the conditional hooks but they don't accept the before and after parameters.
So far a hook was defined as a block that preserves only the environment, but nothing else.
To be able to define commands or aliases, it is possible to define the code field as a string.
You can think of it as if you typed the string into the REPL and hit Enter.
So, the hook from the previous section can be also written as
$env.config = ($env.config | upsert hooks {
pre_prompt: '$env.SPAM = "eggs"'
})
$env.SPAM
# => eggsThis feature can be used, for example, to conditionally bring in definitions based on the current directory:
$env.config = ($env.config | upsert hooks {
env_change: {
PWD: [
{
condition: {|_, after| $after == /some/path/to/directory }
code: 'def foo [] { print "foo" }'
}
{
condition: {|before, _| $before == /some/path/to/directory }
code: 'hide foo'
}
]
}
})When defining a hook as a string, the $before and $after variables are set to the previous and current environment variable value, respectively, similarly to the previous examples:
$env.config = ($env.config | upsert hooks {
env_change: {
PWD: {
code: 'print $"changing directory from ($before) to ($after)"'
}
}
}An example for PWD env change hook:
$env.config = ($env.config | upsert hooks.env_change.PWD {|config|
let val = ($config | get -o hooks.env_change.PWD)
if $val == null {
$val | append {|before, after| print $"changing directory from ($before) to ($after)" }
} else {
[
{|before, after| print $"changing directory from ($before) to ($after)" }
]
}
})This one looks for test-env.nu in a directory
$env.config = ($env.config | upsert hooks.env_change.PWD {
[
{
condition: {|_, after|
($after == '/path/to/target/dir'
and ($after | path join test-env.nu | path exists))
}
code: "overlay use test-env.nu"
}
{
condition: {|before, after|
('/path/to/target/dir' not-in $after
and '/path/to/target/dir' in ($before | default "")
and 'test-env' in (overlay list))
}
code: "overlay hide test-env --keep-env [ PWD ]"
}
]
})You can use the display_output hook to redirect the output of commands.
You should define a block that works on all value types.
The output of external commands is not filtered through display_output.
This hook can display the output in a separate window, perhaps as rich HTML text. Here is the basic idea of how to do that:
$env.config = ($env.config | upsert hooks {
display_output: { to html --partial --no-color | save --raw /tmp/nu-output.html }
})You can view the result by opening file:///tmp/nu-output.html in
a web browser.
Of course this isn't very convenient unless you use
a browser that automatically reloads when the file changes.
Instead of the save command, you would normally customize this
to send the HTML output to a desired window.
You can change to default behavior of how output is displayed by using the display_output hook.
Here is an example that changes the default display behavior to show a table 1 layer deep if the terminal is wide enough, or collapse otherwise:
$env.config = ($env.config | upsert hooks {
display_output: {if (term size).columns >= 100 { table -ed 1 } else { table }}
})The following hook uses the pkgfile command, to find which packages commands belong to in Arch Linux.
$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 comes with the command command-not-found. We only need to plug it in the nushell hook:
$env.config.hooks.command_not_found = {
|command_name|
print (command-not-found $command_name | str trim)
}The following hook uses the ftype command, to find program paths in Windows that might be relevant to the user for alias-ing.
$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)
}
}
)
}
}
}
`docs/nushell/nushell.github.io/book/how_nushell_code_gets_run.md`:
```md
---
prev:
text: Design Notes
link: /book/design_notes.md
next:
text: (Not so) Advanced
link: /book/advanced.md
---
# How Nushell Code Gets Run
In [Thinking in Nu](./thinking_in_nu.md#think-of-nushell-as-a-compiled-language), we encouraged you to _"Think of Nushell as a compiled language"_ due to the way in which Nushell code is processed. We also covered several code examples that won't work in Nushell due that process.
The underlying reason for this is a strict separation of the **_parsing and evaluation_** stages that **_disallows `eval`-like functionality_**. In this section, we'll explain in detail what this means, why we're doing it, and what the implications are. The explanation aims to be as simple as possible, but it might help if you've programmed in another language before.
[[toc]]
## Interpreted vs. Compiled Languages
### Interpreted Languages
Nushell, Python, and Bash (and many others) are _"interpreted"_ languages.
Let's start with a simple "Hello, World!" Nushell program:
```nu
# hello.nu
print "Hello, World!"
Of course, this runs as expected using nu hello.nu. A similar program written in Python or Bash would look (and behave) nearly the same.
In "interpreted languages" code usually gets handled something like this:
Source Code → Interpreter → Result
Nushell follows this pattern, and its "Interpreter" is split into two parts:
Source Code → Parser → Intermediate Representation (IR)IR → Evaluation Engine → Result
First, the source code is analyzed by the Parser and converted into an intermediate representation (IR), which in Nushell's case is just a collection of data structures. Then, these data structures are passed to the Engine for evaluation and output of the results.
This, as well, is common in interpreted languages. For example, Python's source code is typically converted into bytecode before evaluation.
On the other side are languages that are typically "compiled", such as C, C++, or Rust. For example, here's a simple "Hello, World!" in Rust:
// main.rs
fn main() {
println!("Hello, World!");
}To "run" this code, it must be:
- Compiled into machine code instructions
- The compilation results stored as a binary file on the disk
The first two steps are handled with rustc main.rs.
- Then, to produce a result, you need to run the binary (
./main), which passes the instructions to the CPU
So:
Source Code ⇒ Compiler ⇒ Machine CodeMachine Code ⇒ CPU ⇒ Result
::: important You can see that the compile-run sequence is not much different from the parse-evaluate sequence of an interpreter. You begin with source code, parse (or compile) it into some state (e.g., bytecode, IR, machine code), then evaluate (or run) the IR to get a result. You could think of machine code as just another type of IR and the CPU as its interpreter.
One big difference, however, between interpreted and compiled languages is that interpreted languages typically implement an eval function while compiled languages do not. What does this mean?
:::
::: tip Terminology In general, the difference between a dynamic and static language is how much of the source code is resolved during Compilation (or Parsing) vs. Evaluation/Runtime:
-
"Static" languages perform more code analysis (e.g., type-checking, data ownership) during Compilation/Parsing.
-
"Dynamic" languages perform more code analysis, including
evalof additional code, during Evaluation/Runtime.
For the purposes of this discussion, the primary difference between a static and dynamic language is whether or not it has an eval function.
:::
Most dynamic, interpreted languages have an eval function. For example, Python eval (also, Python exec) or Bash eval.
The argument to an eval is "source code inside of source code", typically conditionally or dynamically computed. This means that, when an interpreted language encounters an eval in source code during Parse/Eval, it typically interrupts the normal Evaluation process to start a new Parse/Eval on the source code argument to the eval.
Here's a simple Python eval example to demonstrate this (potentially confusing!) concept:
# hello_eval.py
print("Hello, World!")
eval("print('Hello, Eval!')")
When you run the file (python hello_eval.py), you'll see two messages: "Hello, World!" and "Hello, Eval!". Here is what happens:
- The entire program is Parsed
- (Line 3)
print("Hello, World!")is Evaluated - (Line 4) In order to evaluate
eval("print('Hello, Eval!')"):print('Hello, Eval!')is Parsedprint('Hello, Eval!')is Evaluated
::: tip More fun
Consider eval("eval(\"print('Hello, Eval!')\")") and so on!
:::
Notice how the use of eval here adds a new "meta" step into the execution process. Instead of a single Parse/Eval, the eval creates additional, "recursive" Parse/Eval steps instead. This means that the bytecode produced by the Python interpreter can be further modified during the evaluation.
Nushell does not allow this.
As mentioned above, without an eval function to modify the bytecode during the interpretation process, there's very little difference (at a high level) between the Parse/Eval process of an interpreted language and that of the Compile/Run in compiled languages like C++ and Rust.
::: tip Takeaway
This is why we recommend that you "think of Nushell as a compiled language". Despite being an interpreted language, its lack of eval gives it some of the characteristic benefits as well as limitations common in traditional static, compiled languages.
:::
We'll dig deeper into what it means in the next section.
Consider this Python example:
exec("def hello(): print('Hello eval!')")
hello()
::: note
We're using exec in this example instead of eval because it can execute any valid Python code rather than being limited to eval expressions. The principle is similar in both cases, though.
:::
During interpretation:
- The entire program is Parsed
- In order to Evaluate Line 1:
def hello(): print('Hello eval!')is Parseddef hello(): print('Hello eval!')is Evaluated
- (Line 2)
hello()is evaluated.
Note, that until step 2.2, the interpreter has no idea that a function hello even exists! This makes static analysis of dynamic languages challenging. In this example, the existence of the hello function cannot be checked just by parsing (compiling) the source code. The interpreter must evaluate (run) the code to discover it.
- In a static, compiled language, a missing function is guaranteed to be caught at compile-time.
- In a dynamic, interpreted language, however, it becomes a possible runtime error. If the
eval-defined function is conditionally called, the error may not be discovered until that condition is met in production.
::: important In Nushell, there are exactly two steps:
- Parse the entire source code
- Evaluate the entire source code
This is the complete Parse/Eval sequence. :::
::: tip Takeaway
By not allowing eval-like functionality, Nushell prevents these types of eval-related bugs. Calling a non-existent definition is guaranteed to be caught at parse-time in Nushell.
Furthermore, after parsing completes, we can be certain the bytecode (IR) won't change during evaluation. This gives us a deep insight into the resulting bytecode (IR), allowing for powerful and reliable static analysis and IDE integration which can be challenging to achieve with more dynamic languages.
In general, you have more peace of mind that errors will be caught earlier when scaling Nushell programs. :::
As with most any shell, Nushell has a "Read→Eval→Print Loop" (REPL) that is started when you run nu without any file. This is often thought of, but isn't quite the same, as the "commandline".
::: tip Note
In this section, the > character at the beginning of a line in a code-block is used to represent the commandline prompt. For instance:
> some code...Code after the prompt in the following examples is executed by pressing the Enter key. For example:
> print "Hello world!"
# => Hello world!
> ls
# => prints files and directories...The above means:
- From inside Nushell (launched with
nu):- Type
print "Hello world!" - Press Enter
- Nushell will display the result
- Type
ls - Press Enter
- Nushell will display the result
- Type
:::
When you press Enter after typing a commandline, Nushell:
- (Read): Reads the commandline input
- (Evaluate): Parses the commandline input
- (Evaluate): Evaluates the commandline input
- (Evaluate): Merges the environment (such as the current working directory) to the internal Nushell state
- (Print): Displays the results (if non-
null) - (Loop): Waits for another input
In other words, each REPL invocation is its own separate parse-evaluation sequence. By merging the environment back to the Nushell's state, we maintain continuity between the REPL invocations.
Compare a simplified version of the cd example from "Thinking in Nu":
cd spam
source-env foo.nuThere we saw that this cannot work (as a script or other single expression) because the directory will be changed after the parse-time source-env keyword attempts to read the file.
Running these commands as separate REPL entries, however, works:
> cd spam
> source-env foo.nu
# Yay, works!To see why, let's break down what happens in the example:
- Read the
cd spamcommandline. - Parse the
cd spamcommandline. - Evaluate the
cd spamcommandline. - Merge environment (including the current directory) into the Nushell state.
- Read and Parse
source-env foo.nu. - Evaluate
source-env foo.nu. - Merge environment (including any changes from
foo.nu) into the Nushell state.
When source-env tries to open foo.nu during the parsing in Step 5, it can do so because the directory change from Step 3 was merged into the Nushell state during Step 4. As a result, it's visible in the following Parse/Eval cycles.
Keep in mind that this only works for separate commandlines.
In Nushell, it's possible to group multiple commands into one commandline using:
-
A semicolon:
cd spam; source-env foo.nu -
A newline:
> cd span source-env foo.nuNotice there is no "prompt" before the second line. This type of multiline commandline is usually created with a keybinding to insert a Newline when Alt+Enter or Shift+ Enter is pressed.
These two examples behave exactly the same in the Nushell REPL. The entire commandline (both statements) are processed a single Read→Eval→Print Loop. As such, they will fail the same way that the earlier script-example did.
::: tip Multiline commandlines are very useful in Nushell, but watch out for any out-of-order Parser-keywords. :::
While it is impossible to add parsing into the evaluation stage and yet still maintain our static-language benefits, we can safely add a little bit of evaluation into parsing.
::: tip Terminology In the text below, we use the term "constant" to refer to:
- A
constdefinition - The result of any command that outputs a constant value when provide constant inputs.
:::
By their nature, constants and constant values are known at Parse-time. This, of course, is in sharp contrast to variable declarations and values.
As a result, we can utilize constants as safe, known arguments to parse-time keywords like source, use, and related commands.
Consider this example from "Thinking in Nu":
let my_path = "~/nushell-files"
source $"($my_path)/common.nu"As noted there, we can, however, do the following instead:
const my_path = "~/nushell-files"
source $"($my_path)/common.nu"
Let's analyze the Parse/Eval process for this version:
-
The entire program is Parsed into IR.
- Line 1: The
constdefinition is parsed. Because it is a constant assignment (andconstis also a parser-keyword), that assignment can also be Evaluated at this stage. Its name and value are stored by the Parser. - Line 2: The
sourcecommand is parsed. Becausesourceis also a parser-keyword, it is Evaluated at this stage. In this example, however, it can be successfully parsed since its argument is known and can be retrieved at this point. - The source-code of
~/nushell-files/common.nuis parsed. If it is invalid, then an error will be generated, otherwise the IR results will be included in evaluation in the next stage.
- Line 1: The
-
The entire IR is Evaluated:
- Line 1: The
constdefinition is Evaluated. The variable is added to the runtime stack. - Line 2: The IR result from parsing
~/nushell-files/common.nuis Evaluated.
- Line 1: The
::: important
- An
evaladds additional parsing during evaluation - Parse-time constants do the opposite, adding additional evaluation to the parser.
:::
Also keep in mind that the evaluation allowed during parsing is very restricted. It is limited to only a small subset of what is allowed during a regular evaluation.
For example, the following is not allowed:
const foo_contents = (open foo.nu)Put differently, only a small subset of commands and expressions can generate a constant value. For a command to be allowed:
- It must be designed to output a constant value
- All of its inputs must also be constant values, literals, or composite types (e.g., records, lists, tables) of literals.
In general, the commands and resulting expressions will be fairly simple and without side effects. Otherwise, the parser could all-too-easily enter an unrecoverable state. Imagine, for instance, attempting to assign an infinite stream to a constant. The Parse stage would never complete!
::: tip You can see which Nushell commands can return constant values using:
help commands | where is_const:::
For example, the path join command can output a constant value. Nushell also defines several useful paths in the $nu constant record. These can be combined to create useful parse-time constant evaluations like:
const my_startup_modules = $nu.default-config-dir | path join "my-mods"
use $"($my_startup_modules)/my-utils.nu"::: note Additional Notes Compiled ("static") languages also tend to have a way to convey some logic at compile time. For instance:
- C's preprocessor
- Rust macros
- Zig's comptime, which was an inspiration for Nushell's parse-time constant evaluation.
There are two reasons for this:
-
Increasing Runtime Performance: Logic in the compilation stage doesn't need to be repeated during runtime.
This isn't currently applicable to Nushell, since the parsed results (IR) are not stored beyond Evaluation. However, this has certainly been considered as a possible future feature.
-
As with Nushell's parse-time constant evaluations, these features help (safely) work around limitations caused by the absence of an
evalfunction.
:::
Nushell operates in a scripting language space typically dominated by "dynamic", "interpreted" languages, such as Python, Bash, Zsh, Fish, and many others. Nushell is also "interpreted" since code is run immediately (without a separate, manual compilation).
However, is not "dynamic" in that it does not have an eval construct. In this respect, it shares more in common with "static", compiled languages like Rust or Zig.
This lack of eval is often surprising to many new users and is why it can be helpful to think of Nushell as a compiled, and static, language.
`docs/nushell/nushell.github.io/book/installation.md`:
```md
---
next:
text: Default Shell
link: /book/default_shell.md
---
# Installing Nu
There are lots of ways to get Nu up and running. You can download pre-built binaries from our [release page](https://github.com/nushell/nushell/releases), [use your favourite package manager](https://repology.org/project/nushell/versions), or build from source.
The main Nushell binary is named `nu` (or `nu.exe` on Windows). After installation, you can launch it by typing `nu`.
@[code](@snippets/installation/run_nu.sh)
[[toc]]
## Pre-built Binaries
Nu binaries are published for Linux, macOS, and Windows [with each GitHub release](https://github.com/nushell/nushell/releases). Just download, extract the binaries, then copy them to a location on your PATH.
## Package Managers
Nu is available via several package managers:
[](https://repology.org/project/nushell/versions)
For macOS and Linux, [Homebrew](https://brew.sh/) is a popular choice (`brew install nushell`).
For Windows:
- [Winget](https://docs.microsoft.com/en-us/windows/package-manager/winget/)
- Machine scope installation: `winget install nushell --scope machine`
- Machine scope upgrade: `winget update nushell`
- User scope installation: `winget install nushell` or `winget install nushell --scope user`
- User scope upgrade: Due to [winget-cli issue #3011](https://github.com/microsoft/winget-cli/issues/3011), running `winget update nushell` will unexpectedly install the latest version to `C:\Program Files\nu`. To work around this, run `winget install nushell` again to install the latest version in the user scope.
- [Scoop](https://scoop.sh/) (`scoop install nu`)
For 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
For RedHat/Fedora & Rocky Linux:
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 nushellFor Alpine Linux:
echo "https://alpine.fury.io/nushell/" | tee -a /etc/apk/repositories
apk update
apk add --allow-untrusted nushellCross Platform installation:
- npm (
npm install -g nushellNote that nu plugins are not included if you install in this way)
Docker images are available from the GitHub Container Registry. An image for the latest release is built regularly for Alpine and Debian. You can run the image in interactive mode using:
docker run -it --rm ghcr.io/nushell/nushell:<version>-<distro>Where <version> is the version of Nushell you want to run and <distro> is alpine or the latest supported Debian release, such as bookworm.
To run a specific command, use:
docker run --rm ghcr.io/nushell/nushell:latest-alpine -c "ls /usr/bin | where size > 10KiB"To run a script from the current directory using Bash, use:
docker run --rm \
-v $(pwd):/work \
ghcr.io/nushell/nushell:latest-alpine \
"/work/script.nu"You can also build Nu from source. First, you will need to set up the Rust toolchain and its dependencies.
For Rust to work properly, you'll need to have a compatible compiler suite installed on your system. These are the recommended compiler suites:
- Linux: GCC or Clang
- macOS: Clang (install Xcode)
- Windows: MSVC (install Visual Studio or the Visual Studio Build Tools)
- Make sure to install the "Desktop development with C++" workload
- Any Visual Studio edition will work (Community is free)
If you don't already have Rust on our system, the best way to install it is via rustup. Rustup is a way of managing Rust installations, including managing using different Rust versions.
Nu currently requires the latest stable (1.66.1 or later) version of Rust. The best way is to let rustup find the correct version for you. When you first open rustup it will ask what version of Rust you wish to install:
@code
Once you are ready, press 1 and then enter.
If you'd rather not install Rust via rustup, you can also install it via other methods (e.g. from a package in a Linux distro). Just be sure to install a version of Rust that is 1.66.1 or later.
You will need to install the "pkg-config", "build-essential" and "libssl-dev" packages:
@code
You will need to install "libxcb", "openssl-devel" and "libX11-devel":
@code
Using Homebrew, you will need to install "openssl" and "cmake" using:
@code
If using Nix for package management on macOS, the openssl, cmake, pkg-config, and curl packages are required. These can be installed:
- Globally, using
nix-env --install(and others). - Locally, using Home Manager in your
home.nixconfig. - Temporarily, using
nix-shell(and others).
Build from crates.io using Cargo
Nushell releases are published as source to the popular Rust package registry crates.io. This makes it easy to build and install the latest Nu release with cargo:
cargo install nu --lockedThe cargo tool will do the work of downloading Nu and its source dependencies, building it, and installing it into the cargo bin path.
Note that the default plugins must be installed separately when using cargo. See the Plugins Installation section of the Book for instructions.
You can also build Nu from the latest source on GitHub. This gives you immediate access to the latest features and bug fixes. First, clone the repo:
@code
From there, we can build and run Nu with:
@code
You can also build and run Nu in release mode, which enables more optimizations:
@code
People familiar with Rust may wonder why we do both a "build" and a "run" step if "run" does a build by default. This is to get around a shortcoming of the new default-run option in Cargo, and ensure that all plugins are built, though this may not be required in the future.
`docs/nushell/nushell.github.io/book/line_editor.md`:
```md
# Reedline, Nu's Line Editor
Nushell's line-editor [Reedline](https://github.com/nushell/reedline) is
cross-platform and designed to be modular and flexible. The line-editor is
in charge of controlling the command history, validations, completions, hints,
screen paint, and more.
[[toc]]
## Multi-line Editing
Reedline allows Nushell commandlines to extend across multiple lines. This can be accomplished using several methods:
1. Pressing <kbd>Enter</kbd> when a bracketed expression is open.
For example:
```nu
def my-command [] {
Pressing Enter after the open-bracket will insert a newline. This will also occur with opening (and valid) ( and [ expressions.
This is commonly used to create blocks and closures (as above), but also list, record, and table literals:
let file = {
name: 'repos.sqlite'
hash: 'b939a3fa4ca011ca1aa3548420e78cee'
version: '1.4.2'
}It can even be used to continue a single command across multiple lines:
::: details Example
(
tar
-cvz
-f archive.tgz
--exclude='*.temp'
--directory=../project/
./
):::
-
Pressing Enter at the end of a line with a trailing pipe-symbol (
|).ls | where name =~ '^[0-9]' | # Comments after a trailing pipe are okay get name | mv ...$in ./backups/
-
Manually insert a newline using Alt+Enter or Shift+Enter.
This can be used to create a somewhat more readable version of the previous commandline:
ls | where name =~ '^[0-9]' # Files starting with a digit | get name | mv ...$in ./backups/
::: tip It's possible that one or both of these keybindings may be intercepted by the terminal application or window-manager. For instance, Windows Terminal (and most other terminal applications on Windows) assign Alt+Enter to expand the terminal to full-screen. If neither of the above keybindings work in your terminal, you can assign a different keybinding to:
event: { edit: insertnewline }See Keybindings below for more details.
:::
-
Pressing Ctrl+O opens the current commandline in your editor. Saving the resulting file and exiting the editor will update the commandline with the results.
Reedline allows you to edit text using two modes — Vi and Emacs. If not
specified, the default mode is Emacs. To change the mode, use the
edit_mode setting.
$env.config.edit_mode = 'vi'This can be changed at the commandline or persisted in config.nu.
::: note Vi is a "modal" editor with "normal" mode and an "insert" mode. We recommend becoming familiar with these modes through the use of the Vim or Neovim editors before using Vi mode in Nushell. Each has a built-in tutorial covering the basics (and more) of modal editing. :::
Each edit mode comes with common keybindings for Vi and Emacs text editing.
These keybinding events apply to both Emacs and Vi-insert mode:
| Key | Event |
|---|---|
| Shift+Enter | Insert newline |
| Alt+Enter | Insert newline |
| Backspace | Backspace |
| End | Move to end of line |
| End | Complete history hint |
| Home | Move to line start |
| Ctrl+C | Cancel current line |
| Ctrl+L | Clear screen |
| Ctrl+R | Search history |
| Ctrl+→ (Right Arrow) | Complete history word |
| Ctrl+→ (Right Arrow) | Move word right |
| Ctrl+← (Left Arrow) | Move word left |
| ↑ (Up Arrow) | Move up |
| ↓ (Down Arrow) | Move down |
| ← (Left Arrow) | Move left |
| → (Right Arrow) | Move right |
| Ctrl+P | Move up |
| Ctrl+N | Move down |
| Ctrl+B | Move left |
| Ctrl+F | Move right |
| → (Right Arrow) | History-hint complete |
| Ctrl+F | History-hint complete |
| Alt+F | History-hint complete one word |
| Alt+← (Left Arrow) | History-hint complete one word less |
These keybinding events apply only to Vi-insert mode:
| Key | Event |
|---|---|
| Esc | Switch to Vi-normal mode |
These keybinding events apply only to Vi-normal mode:
| Key | Event |
|---|---|
| Ctrl+C | Cancel current line |
| Ctrl+L | Clear screen |
| ↑ (Up Arrow) | Move up |
| ↓ (Down Arrow) | Move down |
| ← (Left Arrow) | Move left |
| → (Right Arrow) | Move right |
| Ctrl+→ (Right Arrow) | Move right one word |
| Ctrl+← (Left Arrow) | Move left one word |
As with Vi, many motions and actions can be combined with an optional count in normal-mode. For example, 3dw deletes the next three words.
| Key | Motion |
|---|---|
| w | Move to beginning of next word |
| e | Move to end of current or next word |
| b | Move to beginning of current or previous word |
| 0 | Move to start of line |
| $ | Move to end of line |
| h | Move left |
| l | Move right |
| j | Move down |
| k | Move up |
| f+<char> | Move right to <char> |
| t+<char> | Move right to before <char> |
| Shift+F+<char> | Move left to <char> |
| Shift+T+<char> | Move left to after <char> |
These actions can be combined with many of the motions above.
| Key | Action |
|---|---|
| d | Delete |
| Shift+D | Delete to end of line |
| p | Paste after current character |
| Shift+P | Paste before current character |
| i | Enter Vi insert-mode (append) at current character |
| Shift+I | Enter insert-mode at beginning of line |
| a | Append after current character |
| Shift+A | Append to end of line |
| 0 | Move to start of line |
| ^ | Move to start of line |
| $ | Move to end of line |
| c | Change |
| r | Replace |
| s | Substitute character(s) |
| x | Delete character |
| u | Undo |
As mentioned before, Reedline manages and stores all the commands that are edited and sent to Nushell. To configure the max number of records that Reedline should store you will need to adjust this value in your config file:
$env.config.history.max_size = 1000The Reedline prompt is configured using a number of environment variables. See Prompt Configuration for details.
Reedline keybindings are powerful constructs that let you build chains of events that can be triggered with a specific combination of keys.
For example, let's say that you would like to map the completion menu to the
Ctrl + t keybinding (default is tab). You can add the next entry to your
config file.
$env.config.keybindings ++= [{
name: completion_menu
modifier: control
keycode: char_t
mode: emacs
event: { send: menu name: completion_menu }
}]After loading this new config.nu, your new keybinding (Ctrl + t) will open
the completion command.
Each keybinding requires the next elements:
- name: Unique name for your keybinding for easy reference in
$config.keybindings - modifier: A key modifier for the keybinding. The options are:
- none
- control
- alt
- shift
- shift_alt
- alt_shift
- control_alt
- alt_control
- control_shift
- shift_control
- control_alt_shift
- control_shift_alt
- keycode: This represent the key to be pressed
- mode: emacs, vi_insert, vi_normal (a single string or a list. e.g.
[
vi_insertvi_normal]) - event: The type of event that is going to be sent by the keybinding. The
options are:
- send
- edit
- until
::: tip
All of the available modifiers, keycodes and events can be found with
the command keybindings list
:::
::: tip
The keybindings added to vi_insert mode will be available when the
line editor is in insert mode (when you can write text), and the keybindings
marked with vi_normal mode will be available when in normal (when the cursor
moves using h, j, k or l)
:::
The event section of the keybinding entry is where the actions to be performed are defined. In this field you can use either a record or a list of records. Something like this
...
event: { send: Enter }
...or
...
event: [
{ edit: Clear }
{ send: Enter }
]
...The first keybinding example shown in this page follows the first case; a single event is sent to the engine.
The next keybinding is an example of a series of events sent to the engine. It first clears the prompt, inserts a string and then enters that value
$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 }
]
}]One disadvantage of the previous keybinding is the fact that the inserted text
will be processed by the validator and saved in the history, making the
keybinding a bit slow and populating the command history with the same command.
For that reason there is the executehostcommand type of event. The next
example does the same as the previous one in a simpler way, sending a single
event to the engine
$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)"
}
}]Before we continue you must have noticed that the syntax changes for edits and
sends, and for that reason it is important to explain them a bit more. A send
is all the Reedline events that can be processed by the engine and an edit
are all the EditCommands that can be processed by the engine.
To find all the available options for send you can use
keybindings list | where type == eventsAnd the syntax for send events is the next one
...
event: { send: <NAME OF EVENT FROM LIST> }
...::: tip You can write the name of the events with capital letters. The keybinding parser is case insensitive :::
There are two exceptions to this rule: the Menu and ExecuteHostCommand.
Those two events require an extra field to be complete. The Menu needs the
name of the menu to be activated (completion_menu or history_menu)
...
event: {
send: menu
name: completion_menu
}
...and the ExecuteHostCommand requires a valid command that will be sent to the
engine
...
event: {
send: executehostcommand
cmd: "cd ~"
}
...It is worth mentioning that in the events list you will also see Edit([]),
Multiple([]) and UntilFound([]). These options are not available for the
parser since they are constructed based on the keybinding definition. For
example, a Multiple([]) event is built for you when defining a list of
records in the keybinding's event. An Edit([]) event is the same as the
edit type that was mentioned. And the UntilFound([]) event is the same as
the until type mentioned later.
The edit type is the simplification of the Edit([]) event. The event type
simplifies defining complex editing events for the keybindings. To list the
available options you can use the next command
keybindings list | where type == editsThe usual syntax for an edit is the next one
...
event: { edit: <NAME OF EDIT FROM LIST> }
...The syntax for the edits in the list that have a () changes a little bit.
Since those edits require an extra value to be fully defined. For example, if
we would like to insert a string where the prompt is located, then you will
have to use
...
event: {
edit: insertstring
value: "MY NEW STRING"
}
...or say you want to move right until the first S
...
event: {
edit: moverightuntil
value: "S"
}
...As you can see, these two types will allow you to construct any type of keybinding that you require
To complete this keybinding tour we need to discuss the until type for event.
As you have seen so far, you can send a single event or a list of events. And
as we have seen, when a list of events is sent, each and every one of them is
processed.
However, there may be cases when you want to assign different events to the
same keybinding. This is especially useful with Nushell menus. For example, say
you still want to activate your completion menu with Ctrl + t but you also
want to move to the next element in the menu once it is activated using the
same keybinding.
For these cases, we have the until keyword. The events listed inside the
until event will be processed one by one with the difference that as soon as
one is successful, the event processing is stopped.
The next keybinding represents this case.
$env.config.keybindings ++= [{
name: completion_menu
modifier: control
keycode: char_t
mode: emacs
event: {
until: [
{ send: menu name: completion_menu }
{ send: menunext }
]
}
}]The previous keybinding will first try to open a completion menu. If the menu is not active, it will activate it and send a success signal. If the keybinding is pressed again, since there is an active menu, then the next event it will send is MenuNext, which means that it will move the selector to the next element in the menu.
As you can see the until keyword allows us to define two events for the same
keybinding. At the moment of this writing, only the Menu events allow this type
of layering. The other non menu event types will always return a success value,
meaning that the until event will stop as soon as it reaches the command.
For example, the next keybinding will always send a down because that event
is always successful
$env.config.keybindings ++= [{
name: completion_menu
modifier: control
keycode: char_t
mode: emacs
event: {
until: [
{ send: down }
{ send: menu name: completion_menu }
{ send: menunext }
]
}
}]If you want to remove a certain default keybinding without replacing it with a different action, you can set event: null.
e.g. to disable screen clearing with Ctrl + l for all edit modes
$env.config.keybindings ++= [{
modifier: control
keycode: char_l
mode: [emacs, vi_normal, vi_insert]
event: null
}]Your terminal environment may not always propagate your key combinations on to Nushell the way you expect it to. You can use the command keybindings listen to determine if certain keypresses are actually received by Nushell, and how.
Thanks to Reedline, Nushell has menus that can help you with your day to day shell scripting. Next we present the default menus that are always available when using Nushell
When a menu is active, some keybindings change based on the keybinding until specifier discussed above. Common keybindings for menus are:
| Key | Event |
|---|---|
| Tab | Select next item |
| Shift+Tab | Select previous item |
| Enter | Accept selection |
| ↑ (Up Arrow) | Move menu up |
| ↓ (Down Arrow) | Move menu down |
| ← (Left Arrow) | Move menu left |
| → (Right Arrow) | Move menu right |
| Ctrl+P | Move menu up |
| Ctrl+N | Move menu down |
| Ctrl+B | Move menu left |
| Ctrl+F | Move menu right |
::: note
Menu direction behavior varies based on the menu type (see below). For example,
in a description menu, "Up" and "Down" apply to the "Extra" list, but in a
list menu the directions apply to the selection.
:::
The help menu is there to ease your transition into Nushell. Say you are
putting together an amazing pipeline and then you forgot the internal command
that would reverse a string for you. Instead of deleting your pipe, you can
activate the help menu with F1. Once active just type keywords for the
command you are looking for and the menu will show you commands that match your
input. The matching is done on the name of the commands or the commands
description.
To navigate the menu you can select the next element by using tab, you can
scroll the description by pressing left or right and you can even paste into
the line the available command examples.
The help menu can be configured by modifying the next parameters
$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
}
}]The completion menu is a context sensitive menu that will present suggestions based on the status of the prompt. These suggestions can range from path suggestions to command alternatives. While writing a command, you can activate the menu to see available flags for an internal command. Also, if you have defined your custom completions for external commands, these will appear in the menu as well.
The completion menu by default is accessed by pressing tab and it can be configured by
modifying these values from the config object:
$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
}
}]By modifying these parameters you can customize the layout of your menu to your liking.
The history menu is a handy way to access the editor history. When activating
the menu (default Ctrl+r) the command history is presented in reverse
chronological order, making it extremely easy to select a previous command.
The history menu can be configured by modifying these values from the config object:
$env.config.menus ++= [{
name: history_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
}
}]When the history menu is activated, it pulls page_size records from the
history and presents them in the menu. If there is space in the terminal, when
you press Ctrl+x again the menu will pull the same number of records and
append them to the current page. If it isn't possible to present all the pulled
records, the menu will create a new page. The pages can be navigated by
pressing Ctrl+z to go to previous page or Ctrl+x to go to next page.
To search in your history you can start typing key words for the command you are looking for. Once the menu is activated, anything that you type will be replaced by the selected command from your history. for example, say that you have already typed this
let a = ()you can place the cursor inside the () and activate the menu. You can filter
the history by typing key words and as soon as you select an entry, the typed
words will be replaced
let a = (ls | where size > 10MiB)Another nice feature of the menu is the ability to quick select something from it. Say you have activated your menu and it looks like this
>
0: ls | where size > 10MiB
1: ls | where size > 20MiB
2: ls | where size > 30MiB
3: ls | where size > 40MiBInstead of pressing down to select the fourth entry, you can type !3 and
press enter. This will insert the selected text in the prompt position, saving
you time scrolling down the menu.
History search and quick selection can be used together. You can activate the menu, do a quick search, and then quick select using the quick selection character.
In case you find that the default menus are not enough for you and you have the need to create your own menu, Nushell can help you with that.
In order to add a new menu that fulfills your needs, you can use one of the default layouts as a template. The templates available in nushell are columnar, list or description.
The columnar menu will show you data in a columnar fashion adjusting the column number based on the size of the text displayed in your columns.
The list type of menu will always display suggestions as a list, giving you the
option to select values using ! plus number combination.
The description type will give you more space to display a description for some values, together with extra information that could be inserted into the buffer.
Let's say we want to create a menu that displays all the variables created
during your session, we are going to call it vars_menu. This menu will use a
list layout (layout: list). To search for values, we want to use only the things
that are written after the menu has been activated
(only_buffer_difference: true).
With that in mind, the desired menu would look like this
$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} }
}
}]As you can see, the new menu is identical to the history_menu previously
described. The only huge difference is the new field called source. The
source field is a nushell definition of the values you want to display in the
menu. For this menu we are extracting the data from scope variables and we
are using it to create records that will be used to populate the menu.
The required structure for the record is the next one
{
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
start:
end:
}
extra: [string] # Optional. A list of strings that will be displayed with the selected value. Only works with a description menu
}For the menu to display something, at least the value field has to be present
in the resulting record.
In order to make the menu interactive, these two variables are available in
the block: $buffer and $position. The $buffer contains the value captured
by the menu, when the option only_buffer_difference is true, $buffer is the
text written after the menu was activated. If only_buffer_difference is
false, $buffer is all the string in line. The $position variable can be
used to create replacement spans based on the idea you had for your menu. The
value of $position changes based on whether only_buffer_difference is true
or false. When true, $position is the starting position in the string where
text was inserted after the menu was activated. When the value is false,
$position indicates the actual cursor position.
Using this information, you can design your menu to present the information you require and to replace that value in the location you need it. The only thing extra that you need to play with your menu is to define a keybinding that will activate your brand new menu.
In case you want to change the default way both menus are activated, you can
change that by defining new keybindings. For example, the next two keybindings
assign the completion and history menu to Ctrl+t and Ctrl+y respectively
$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 }
]
}
}
{
name: history_menu
modifier: control
keycode: char_y
mode: [vi_insert vi_normal]
event: {
until: [
{ send: menu name: history_menu }
{ send: menupagenext }
]
}
}
]
`docs/nushell/nushell.github.io/book/loading_data.md`:
```md
# Loading Data
Earlier, we saw how you can use commands like [`ls`](/commands/docs/ls.md), [`ps`](/commands/docs/ps.md), [`date`](/commands/docs/date.md), and [`sys`](/commands/docs/sys.md) to load information about your files, processes, time of day, and the system itself. Each command gives us a table of information that we can explore. There are other ways we can load in a table of data to work with.
## Opening files
One of Nu's most powerful assets in working with data is the [`open`](/commands/docs/open.md) command. It is a multi-tool that can work with a number of different data formats. To see what this means, let's try opening a json file:
@[code](@snippets/loading_data/vscode.sh)
In a similar way to [`ls`](/commands/docs/ls.md), opening a file type that Nu understands will give us back something that is more than just text (or a stream of bytes). Here we open a "package.json" file from a JavaScript project. Nu can recognize the JSON text and parse it to a table of data.
If we wanted to check the version of the project we were looking at, we can use the [`get`](/commands/docs/get.md) command.
```nu
open editors/vscode/package.json | get version
# => 1.0.0
Nu currently supports the following formats for loading data directly into tables:
- csv
- eml
- ics
- ini
- json
- nuon
- ods
- SQLite databases
- ssv
- toml
- tsv
- url
- vcf
- xlsx / xls
- xml
- yaml / yml
::: tip Did you know?
Under the hood open will look for a from ... subcommand in your scope which matches the extension of your file.
You can thus simply extend the set of supported file types of open by creating your own from ... subcommand.
:::
But what happens if you load a text file that isn't one of these? Let's try it:
open README.mdWe're shown the contents of the file.
Below the surface, what Nu sees in these text files is one large string. Next, we'll talk about how to work with these strings to get the data we need out of them.
Nushell Object Notation (NUON) aims to be for Nushell what JavaScript Object Notation (JSON) is for JavaScript. That is, NUON code is a valid Nushell code that describes some data structure. For example, this is a valid NUON (example from the default configuration file):
{
menus: [
# Configuration for default nushell menus
# Note the lack of source parameter
{
name: completion_menu
only_buffer_difference: false
marker: "| "
type: {
layout: columnar
columns: 4
col_width: 20 # Optional value. If missing all the screen width is used to calculate column width
col_padding: 2
}
style: {
text: green
selected_text: green_reverse
description_text: yellow
}
}
]
}You might notice it is quite similar to JSON, and you're right. NUON is a superset of JSON! That is, any JSON code is a valid NUON code, therefore a valid Nushell code. Compared to JSON, NUON is more "human-friendly". For example, comments are allowed and commas are not required.
One limitation of NUON currently is that it cannot represent all of the Nushell data types. Most notably, NUON does not allow the serialization of blocks.
An important part of working with data coming from outside Nu is that it's not always in a format that Nu understands. Often this data is given to us as a string.
Let's imagine that we're given this data file:
open people.txt
# => Octavia | Butler | Writer
# => Bob | Ross | Painter
# => Antonio | Vivaldi | ComposerEach bit of data we want is separated by the pipe ('|') symbol, and each person is on a separate line. Nu doesn't have a pipe-delimited file format by default, so we'll have to parse this ourselves.
The first thing we want to do when bringing in the file is to work with it a line at a time:
open people.txt | lines
# => ───┬──────────────────────────────
# => 0 │ Octavia | Butler | Writer
# => 1 │ Bob | Ross | Painter
# => 2 │ Antonio | Vivaldi | Composer
# => ───┴──────────────────────────────We can see that we're working with the lines because we're back into a list. Our next step is to see if we can split up the rows into something a little more useful. For that, we'll use the split command. split, as the name implies, gives us a way to split a delimited string. We will use split's column subcommand to split the contents across multiple columns. We tell it what the delimiter is, and it does the rest:
open people.txt | lines | split column "|"
# => ───┬──────────┬───────────┬───────────
# => # │ column1 │ column2 │ column3
# => ───┼──────────┼───────────┼───────────
# => 0 │ Octavia │ Butler │ Writer
# => 1 │ Bob │ Ross │ Painter
# => 2 │ Antonio │ Vivaldi │ Composer
# => ───┴──────────┴───────────┴───────────That almost looks correct. It looks like there's an extra space there. Let's trim that extra space:
open people.txt | lines | split column "|" | str trim
# => ───┬─────────┬─────────┬──────────
# => # │ column1 │ column2 │ column3
# => ───┼─────────┼─────────┼──────────
# => 0 │ Octavia │ Butler │ Writer
# => 1 │ Bob │ Ross │ Painter
# => 2 │ Antonio │ Vivaldi │ Composer
# => ───┴─────────┴─────────┴──────────Not bad. The split command gives us data we can use. It also goes ahead and gives us default column names:
open people.txt | lines | split column "|" | str trim | get column1
# => ───┬─────────
# => 0 │ Octavia
# => 1 │ Bob
# => 2 │ Antonio
# => ───┴─────────We can also name our columns instead of using the default names:
open people.txt | lines | split column "|" first_name last_name job | str trim
# => ───┬────────────┬───────────┬──────────
# => # │ first_name │ last_name │ job
# => ───┼────────────┼───────────┼──────────
# => 0 │ Octavia │ Butler │ Writer
# => 1 │ Bob │ Ross │ Painter
# => 2 │ Antonio │ Vivaldi │ Composer
# => ───┴────────────┴───────────┴──────────Now that our data is in a table, we can use all the commands we've used on tables before:
open people.txt | lines | split column "|" first_name last_name job | str trim | sort-by first_name
# => ───┬────────────┬───────────┬──────────
# => # │ first_name │ last_name │ job
# => ───┼────────────┼───────────┼──────────
# => 0 │ Antonio │ Vivaldi │ Composer
# => 1 │ Bob │ Ross │ Painter
# => 2 │ Octavia │ Butler │ Writer
# => ───┴────────────┴───────────┴──────────There are other commands you can use to work with strings:
There is also a set of helper commands we can call if we know the data has a structure that Nu should be able to understand. For example, let's open a Rust lock file:
open Cargo.lock
# => # This file is automatically @generated by Cargo.
# => # It is not intended for manual editing.
# => [[package]]
# => name = "adhoc_derive"
# => version = "0.1.2"The "Cargo.lock" file is actually a .toml file, but the file extension isn't .toml. That's okay, we can use the from command using the toml subcommand:
@code
The from command can be used for each of the structured data text formats that Nu can open and understand by passing it the supported format as a subcommand.
While it's helpful to be able to open a file and immediately work with a table of its data, this is not always what you want to do. To get to the underlying text, the open command can take an optional --raw flag:
open Cargo.toml --raw
# => [package] name = "nu"
# => version = "0.1.3"
# => authors = ["Yehuda Katz <[email protected]>", "Sophia Turner <[email protected]>"]
# => description = "A shell for the GitHub era"
# => license = "MIT"SQLite databases are automatically detected by open, no matter what their file extension is. You can open a whole database:
open foo.dbOr get a specific table:
open foo.db | get some_tableOr run any SQL query you like:
open foo.db | query db "select * from some_table"(Note: some older versions of Nu use into db | query instead of query db )
In addition to loading files from your filesystem, you can also load URLs by using the http get command. This will fetch the contents of the URL from the internet and return it:
@code
`docs/nushell/nushell.github.io/book/metadata.md`:
```md
# Metadata
In using Nu, you may have come across times where you felt like there was something extra going on behind the scenes. For example, let's say that you try to open a file that Nu supports only to forget and try to convert again:
```nu
open Cargo.toml | from toml
# => error: Expected a string from pipeline
# => - shell:1:18
# => 1 | open Cargo.toml | from toml
# => | ^^^^^^^^^ requires string input
# => - shell:1:5
# => 1 | open Cargo.toml | from toml
# => | ---------- object originates from here
The error message tells us not only that what we gave from toml wasn't a string, but also where the value originally came from. How would it know that?
Values that flow through a pipeline in Nu often have a set of additional information, or metadata, attached to them. These are known as tags, like the tags on an item in a store. These tags don't affect the data, but they give Nu a way to improve the experience of working with that data.
Let's run the open command again, but this time, we'll look at the tags it gives back:
metadata (open Cargo.toml)
# => ╭──────┬───────────────────╮
# => │ span │ {record 2 fields} │
# => ╰──────┴───────────────────╯Currently, we track only the span of where values come from. Let's take a closer look at that:
metadata (open Cargo.toml) | get span
# => ╭───────┬────────╮
# => │ start │ 212970 │
# => │ end │ 212987 │
# => ╰───────┴────────╯The span "start" and "end" here refer to where the underline will be in the line. If you count over 5, and then count up to 15, you'll see it lines up with the "Cargo.toml" filename. This is how the error we saw earlier knew what to underline.
`docs/nushell/nushell.github.io/book/modules.md`:
```md
# Modules
Like many programming languages, Nushell supports writing and using _modules_ as a way to organize code more efficiently. Modules can be thought of as "containers" that hold various definitions, including:
- Additional custom commands
- Aliases
- Constants
- Externs
- Environment variables
- And even other modules (a.k.a., submodules)
Many users will start off using modules written by others, then progress to writing their own. This chapter covers those two topics:
- [Using Modules](./modules/using_modules.md)
- [Creating Modules](./modules/creating_modules.md)
docs/nushell/nushell.github.io/book/modules/creating_modules.md:
# Creating Modules
[[toc]]
::: important
When working through the examples below, it is recommended that you start a new shell before importing an updated version of each module or command. This will help reduce any confusion caused by definitions from previous imports.
:::
## Overview
Modules (and Submodules, to be covered below) are created in one of two ways:
- Most commonly, by creating a file with a series of `export` statements of definitions to be exported from the module.
- For submodules inside a module, using the `module` command
::: tip
While it's possible to use the `module` command to create a module directly at the commandline, it's far more useful and common to store the module definitions in a file for reusability.
:::
The module file can be either:
- A file named `mod.nu`, in which case its _directory_ becomes the module name
- Any other `<module_name>.nu` file, in which case the filename becomes the module name
### Simple Module Example
Create a file named `inc.nu` with the following:
```nu
export def increment []: int -> int {
$in + 1
}This is a module! We can now import it and use the increment command:
use inc.nu *
5 | increment
# => 6Of course, you can easily distribute a file like this so that others can make use of the module as well.
We covered the types of definitions that are available in modules briefly in the main Modules Overview above. While this might be enough explanation for an end-user, module authors will need to know how to create the export definitions for:
- Commands (
export def) - Aliases (
export alias) - Constants (
export const) - Known externals (
export extern) - Submodules (
export module) - Imported symbols from other modules (
export use) - Environment setup (
export-env)
::: tip
Only definitions marked with export (or export-env for environment variables) are accessible when the module is imported. Definitions not marked with export are only visible from inside the module. In some languages, these would be called "private" or "local" definitions. An example can be found below in Additional Examples.
:::
::: important An export cannot have the same name as that of the module itself. :::
In the Basic Example above, we had a module named inc with a command named increment. However, if we rename that file to increment.nu, it will fail to import.
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.As helpfully mentioned in the error message, you can simply rename the export main, in which case it will take on the name of the module when imported. Edit the increment.nu file:
export def main []: int -> int {
$in + 1
}Now it works as expected:
use ./increment.nu
2024 | increment
# => 2025::: note
main can be used for both export def and export extern definitions.
:::
::: tip
main definitions are imported in the following cases:
- The entire module is imported with
use <module>oruse <module.nu> - The
*glob is used to import all of the modules definitions (e.g.,use <module> *, etc.) - The
maindefinition is explicitly imported withuse <module> main,use <module> [main], etc.)
Conversely, the following forms do not import the main definition:
use <module> <other_definition>
# or
use <module> [ <other_definitions> ]
:::
::: note
Additionally, `main` has special behavior if used in a script file, regardless of whether it is exported or not. See the [Scripts](../scripts.html#parameterizing-scripts) chapter for more details.
:::
## Module Files
As mentioned briefly in the Overview above, modules can be created either as:
1. `<module_name>.nu`: "File-form" - Useful for simple modules
2. `<module_name>/mod.nu`: "Directory-form" - Useful for organizing larger module projects where submodules can easily map to subdirectories of the main module
The `increment.nu` example above is clearly an example of (1) the file-form. Let's try converting it to the directory-form:
```nu
mkdir increment
mv increment.nu increment/mod.nu
use increment *
41 | increment
# => 42Notice that the behavior of the module once imported is identical regardless of whether the file-form or directory-form is used; only its path changes.
::: note
Technically, you can import this either using the directory form above or explicitly with use increment/mod.nu *, but the directory shorthand is preferred when using a mod.nu.
:::
As covered in Custom Commands, subcommands allow us to group commands logically. Using modules, this can be done in one of two ways:
- As with any custom command, the command can be defined as
"<command> <subcommand>", using a space inside quotes. Let's add anincrement bysubcommand to theincrementmodule we defined above:
export def main []: int -> int {
$in + 1
}
export def "increment by" [amount: int]: int -> int {
$in + $amount
}It can then be imported with use increment * to load both the increment command and increment by subcommand.
- Alternatively, we can define the subcommand simply using the name
by, since importing the entireincrementmodule will result in the same commands:
export def main []: int -> int {
$in + 1
}
export def by [amount: int]: int -> int {
$in + $amount
}This module is imported using use increment (without the glob *) and results in the same increment command and increment by subcommand.
::: note
We'll continue to use this version for further examples below, so notice that the import pattern has changed to use increment (rather than use increment *) below.
:::
Submodules are modules that are exported from another module. There are two ways to add a submodule to a module:
- With
export module: Exports (a) the submodule and (b) its definitions as members of the submodule - With
export use: Exports (a) the submodule and (b) its definitions as members of the parent module
To demonstrate the difference, let's create a new my-utils module, with our increment example as a submodule. Additionally, we'll create a new range-into-list command in its own submodule.
-
Create a directory for the new
my-utilsand move theincrement.nuinto itmkdir my-utils # Adjust the following as needed mv increment/mod.nu my-utils/increment.nu rm increment cd my-utils
-
In the
my-utilsdirectory, create arange-into-list.nufile with the following:export def main []: range -> list { # It looks odd, yes, but the following is just # a simple way to convert ranges to lists each {||} }
-
Test it:
use range-into-list.nu 1..5 | range-into-list | describe # => list<int> (stream)
-
We should now have a
my-utilsdirectory with the:increment.numodulerange-into-list.numodule
The following examples show how to create a module with submodules.
The most common form for a submodule definition is with export module.
-
Create a new module named
my-utils. Since we're in themy-utilsdirectory, we will create amod.nuto define it. This version ofmy-utils/mod.nuwill contain:export module ./increment.nu export module ./range-into-list.nu
-
We now have a module
my-utilswith the two submodules. Try it out:# Go to the parent directory of 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
Before proceeding to the next section, run scope modules and look for the my-utils module. Notice that it has no commands of its own; just the two submodules.
Alternatively, we can (re)export the definitions from other modules. This is slightly different from the first form, in that the commands (and other definitions, if they were present) from increment and range-into-list become members of the my-utils module itself. We'll be able to see the difference in the output of the scope modules command.
Let's change my-utils/mod.nu to:
export use ./increment.nu
export use ./range-into-list.nuTry it out using the same commands as above:
# Go to the parent directory of 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-newestRun scope modules again and notice that all of the commands from the submodules are re-exported into the my-utils module.
::: tip
While export module is the recommended and most common form, there is one module-design scenario in which export use is required -- export use can be used to selectively export definitions from the submodule, something export module cannot do. See Additional Examples - Selective Export for an example.
:::
::: note
module without export defines only a local module; it does not export a submodule.
:::
As with custom commands, modules can include documentation that can be viewed with help <module_name>. The documentation is simply a series of commented lines at the beginning of the module file. Let's document the my-utils module:
# A collection of helpful utility functions
export use ./increment.nu
export use ./range-into-list.nuNow examine the help:
use my-utils *
help my-utils
# => A collection of helpful utility functionsAlso notice that, because the commands from increment and range-into-list are re-exported with export use ..., those commands show up in the help for the main module as well.
Modules can define an environment using export-env. Let's extend our my-utils module with an environment variable export for a common directory where we'll place our modules in the future. This directory is (by default) in the $env.NU_LIB_DIRS search path discussed in Using Modules - Module Path.
# A collection of helpful utility functions
export use ./increment.nu
export use ./range-into-list.nu
export-env {
$env.NU_MODULES_DIR = ($nu.default-config-dir | path join "scripts")
}When this module is imported with use, the code inside the export-env block is run and the its environment merged into the current scope:
use my-utils
$env.NU_MODULES_DIR
# => Returns the directory name
cd $env.NU_MODULES_DIR::: tip
As with any command defined without --env, commands and other definitions in the module use their own scope for environment. This allows changes to be made internal to the module without them bleeding into the user's scope. Add the following to the bottom of my-utils/mod.nu:
export def examine-config-dir [] {
# Changes the PWD environment variable
cd $nu.default-config-dir
ls
}Running this command changes the directory locally in the module, but the changes are not propagated to the parent scope.
:::
::: note
This scenario is commonly encountered when creating a module that uses std/log.
:::
Attempting to import a module's environment within another environment may not work as expected. Let's create a new module go.nu that creates "shortcuts" to common directories. One of these will be the $env.NU_MODULES_DIR defined above in my-utils.
We might try:
# go.nu, in the parent directory of my-utils
use my-utils
export def --env home [] {
cd ~
}
export def --env modules [] {
cd $env.NU_MODULES_DIR
}And then import it:
use go.nu
go home
# => Works
go modules
# => Error: $env.NU_MODULES_DIR is not foundThis doesn't work because my-utils isn't evaluated in this case; it is only parsed when the go.nu module is imported. While this brings all of the other exports into scope, it does not run the export-env block.
::: important
As mentioned at the start of this chapter, trying this while my-utils (and its $env.NU_MODULES_DIR) is still in scope from a previous import will not fail as expected. Test in a new shell session to see the "normal" failure.
:::
To bring my-utils exported environment into scope for the go.nu module, there are two options:
-
Import the module in each command where it is needed
By placing
use my-utilsin thego homecommand itself, itsexport-envwill be evaluated when the command is. For example:# go.nu export def --env home [] { cd ~ } export def --env modules [] { use my-utils cd $env.NU_MODULES_DIR }
-
Import the
my-utilsenvironment inside anexport-envblock in thego.numoduleuse my-utils export-env { use my-utils [] } export def --env home [] { cd ~ } export def --env modules [] { cd $env.NU_MODULES_DIR }
In the example above,
go.nuimportsmy-utilstwice:- The first
use my-utilsimports the module and its definitions (except for the environment) into the module scope. - The second
use my-utils []imports nothing but the environment intogo.nu's exported environment block. Because theexport-envofgo.nuis executed when the module is first imported, theuse my-utils []is also evaluated.
- The first
Note that the first method keeps my-utils environment inside the go.nu module's scope. The second, on the other hand, re-exports my-utils environment into the user scope.
A .nu file cannot have the same name as its module directory (e.g., spam/spam.nu) as this would create an ambiguous condition with the name being defined twice. This is similar to the situation described above where a command cannot have the same name as its parent.
::: important
Nushell on Windows supports both forward-slashes and back-slashes as the path separator. However, to ensure that they work on all platforms, using only the forward-slash / in your modules is highly recommended.
:::
As mentioned above, definitions in a module without the export keyword are only accessible in the module's scope.
To demonstrate, create a new module is-alphanumeric.nu. Inside this module, we'll create a str is-alphanumeric command. If any of the characters in the string are not alpha-numeric, it returns false:
# 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)}
}
}Notice that we have two definitions in this module -- alpha-num-range and str is-alphanumeric, but only the second is exported.
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
While the following is a rare use-case, this technique is used by the Standard Library to
make the dirs commands and its aliases available separately.
:::
As mentioned in the Submodules section above, only export use can selectively export definitions from a submodule.
To demonstrate, let's add a modified form of the go.nu module example above to my-utils:
# go.nu, in the my-utils directory
export def --env home [] {
cd ~
}
export def --env modules [] {
cd ($nu.default-config-dir | path join "scripts")
}
export alias h = home
export alias m = modulesThis go.nu includes the following changes from the original:
- It doesn't rely on the
my-utilsmod since it will now be a submodule ofmy-utilsinstead - It adds "shortcut" aliases:
h: Goes to the home directory (alias ofgo home)m: Goes to the modules directory (alias ofgo modules)
A user could import just the aliases with:
use my-utils/go.nu [h, m]However, let's say we want to have go.nu be a submodule of my-utils. When a user imports my-utils, they should only get the commands, but not the aliases. Edit my-utils/mod.nu and add:
export use ./go.nu [home, modules]That almost works -- It selectively exports home and modules, but not the aliases. However, it does so without the go prefix. For example:
use my-utils *
home
# => works
go home
# => Error: command not foundTo export them as go home and go modules, make the following change to my-utils/mod.nu:
# Replace the existing `export use` with ...
export module go {
export use ./go.nu [home, modules]
}This creates a new, exported submodule go in my-utils with the selectively (re)exported definitions for go home and go modules.
use my-utils *
# => As expected:
go home
# => works
home
# => Error: command not found
`docs/nushell/nushell.github.io/book/modules/using_modules.md`:
```md
# Using Modules
[[toc]]
## Overview
End-users can add new functionality to Nushell by using ("importing") modules written by others.
To import a module and its definitions, we call the [`use`](/commands/docs/use.md) command:
```nu
use <path/to/module> <members...>
For example:
use std/log
log info "Hello, Modules"::: tip The example above uses the Standard Library, a collection of modules built-in to Nushell. Because it is readily available to all Nushell users, we'll also use it for several of the examples below. :::
Installing a module is simply a matter of placing its files in a directory. This might be done via git clone (or other version control system), a package manager such as nupm, or manually. The module's documentation should provide recommendations.
Anything after the use keyword forms an import pattern which controls how the definitions are imported.
Notice above that use has two arguments:
- A path to the module
- (Optional) The definitions to import
The module's documentation will usually tell you the recommended way to import it. However, it can still be useful to understand the options available:
The path to the module can be:
-
An absolute path to a directory containing a
mod.nufile:::: details Example
use ~/nushell/modules/nupm
Note that the module name (i.e., its directory) can end in a
/(or\on Windows), but as with most commands that take paths (e.g.,cd), this is completely optional.:::
-
A relative path to a directory containing a
mod.nufile:::: details Example
# cd then use the mod.nu in the relative nupm directory cd ~/nushell/modules use nupm # or use nupm/
Note that the module name (its directory) can end in a
/(or\on Windows), but as with most commands that take a paths (e.g.,cd), this is completely optional. :::::: important Important! Importing modules from
$env.NU_LIB_DIRSWhen importing a module via a relative path, Nushell first searches from the current directory. If a matching module is not found at that location, Nushell then searches each directory in the$env.NU_LIB_DIRSlist.This allows you to install modules to a location that is easily accessible via a relative path regardless of the current directory. :::
-
An absolute or relative path to a Nushell module file. As above, Nushell will search the
$env.NU_LIB_DIRSfor a matching relative path.::: details Example
use ~/nushell/modules/std-rfc/bulk-rename.nu # Or cd ~/nushell/modules use std-rfc/bulk-rename.nu
:::
-
A virtual directory:
::: details Example The standard library modules mentioned above are stored in a virtual filesystem with a
stddirectory. Consider this an alternate form of the "absolute path" examples above.use std/assert assert equal 'string1' "string1"
:::
-
Less commonly, the name of a module already created with the
modulecommand. While it is possible to use this command to create a module at the commandline, this isn't common or useful. Instead, this form is primarily used by module authors to define a submodule. See Creating Modules - Submodules.
The second argument to the use command is an optional list of the definitions to import. Again, the module documentation should provide recommendations. For example, the Standard Library Chapter covers the recommended imports for each submodule.
Of course, you always have the option to choose a form that works best for your use-case.
-
Import an entire module/submodule as a command with subcommands
In an earlier example above, we imported the
std/logmodule without specifying the definitions:use std/log log info "Hello, std/log Module"
Notice that this imports the
logsubmodule with all of its subcommands (e.g.,log info,log error, etc.) into the current scope.Compare the above to the next version, where the command becomes
std log info:use std std log info "Hello, std Module" -
Import all of the definitions from a module
Alternatively, you can import the definitions themselves into the current scope. For example:
use std/formats * ls | to jsonl
Notice how the
to jsonlcommand is placed directly in the current scope, rather than being a subcommand offormats. -
Import one or more definitions from a module
Nushell can also selectively import a subset of the definitions of a module. For example:
use std/math PI let circle = 2 * $PI * $radius
Keep in mind that the definitions can be:
- Commands
- Aliases
- Constants
- Externs
- Other modules (as submodules)
- Environment variables (always imported)
Less commonly, a list of imports can also be used:
use std/formats [ 'from ndjson' 'to ndjson' ]::: note Importing submodules While you can import a submodule by itself using
use <module> </submodule>(e.g.,use std help), the entire parent module and all of its definitions (and thus submodules) will be parsed when using this form. When possible, loading the submodule as a module will result in faster code. For example:# Faster use std/help
:::
As seen above with the std/math examples, some modules may export constant definitions. When importing the entire module, constants can be accessed through a record with the same name as the module:
# Importing entire module - Record access
use std/math
$math.PI
# => 3.141592653589793
$math
# => ╭───────┬──────╮
# => │ GAMMA │ 0.58 │
# => │ E │ 2.72 │
# => │ PI │ 3.14 │
# => │ TAU │ 6.28 │
# => │ PHI │ 1.62 │
# => ╰───────┴──────╯
# Or importing all of the module's members
use std/math *
$PI
# => 3.141592653589793Any custom command or alias, whether imported from a module or not, can be "hidden" to restore the previous definition using
the hide command.
The hide command also accepts import patterns, similar to use, but interprets them slightly differently. These patterns can be one of the following:
- If the name is a custom command, the
hidecommand hides it directly. - If the name is a module name, it hides all of its exports prefixed with the module name
For example, using std/assert:
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`Just as you can use a subset of the module's definitions, you can also hide them selectively as well:
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 is covered in more detail in Creating Modules, but for end-users, main simply means "the command named the same as the module." In this case the assert module exports a main command that "masquerades" as the assert command. Hiding main has the effect of hiding the assert command, but not its subcommands.
:::
-
To make a module always be available without having to
useit in each Nushell session, simply add its import (use) to your startup configuration. See the Configuration Chapter to learn how. -
Modules can also be used as part of an Overlay.
`docs/nushell/nushell.github.io/book/moving_around.md`:
```md
# Moving Around the System
A defining characteristic of a shell is the ability to navigate and interact with the filesystem. Nushell is, of course, no exception. Here are some common commands you might use when interacting with the filesystem:
## Viewing Directory Contents
```nu
ls
As seen in the Quick Tour, the ls command returns the contents of a directory. Nushell's ls will return the contents as a table.
The ls command also takes an optional argument to change what you'd like to view. For example, we can list the files that end in ".md"
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 │
# => ╰───┴────────────────────┴──────┴──────────┴──────────────╯The asterisk (*) in the above optional argument *.md is sometimes called a wildcard or a glob. It lets us match anything. You can read this glob *.md as "match any filename, so long as it ends with '.md'."
The most general glob is *, which will match all paths. More often, you'll see this pattern used as part of another pattern, for example *.bak and temp*.
Nushell also supports a double * which will traverse paths that are nested inside of other directories. For example, ls **/* will list all the non-hidden paths nested under the current directory.
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 │
# => ╰───┴───────────────────────────────┴──────┴──────────┴──────────────╯Here, we're looking for any file that ends with ".md". The double-asterisks further specify "in any directory starting from here."
Nushell's globbing syntax not only supports *, but also matching single characters with ? and character groups with [...].
Escaping the *, ?, and [] patterns works by enclosing them in a single-quoted, double-quoted, or
raw string. For example, to show the contents of a directory named
[slug], use ls "[slug]" or ls '[slug]'.
However, backtick quoted strings do not escape globs. For example, compare the following scenarios:
-
Unquoted: Glob pattern
An unquoted bare word string with glob characters is interpreted as a glob pattern, so the following will remove all files in the current directory that contain
myfileas any part of the filename:rm *myfile*
-
Quoted: String literal with asterisks
When quoting with single or double quotes, or using a raw string, a string with the literal, escaped asterisks (or other glob characters) is passed to the command. The result is not a glob. The following command will only remove a file literally named
*myfile*(including the asterisks). Other files withmyfilein the name are not affected:rm "*myfile*" -
Backtick-quoted: Glob pattern
Asterisks (and other glob patterns) within a backtick-quoted string are interpreted as a glob pattern. Notice that this is the same behavior as that of the bare-word string example in #1 above.
The following, as with that first example, removes all files in the current directory that contain
myfileas part of the filenamerm `*myfile*`
::: tip
Nushell also includes a dedicated glob command with support for more complex globbing scenarios.
:::
The quoting techniques above are useful when constructing glob-literals, but you may need to construct globs programmatically. There are several techniques available for this purpose:
-
into globThe
into globcommand can be used to convert a string (and other types) into a glob. For instance:# Find files whose name includes the current month in the form YYYY-mm let current_month = (date now | format date '%Y-%m') let glob_pattern = ($"*($current_month)*" | into glob) ls $glob_pattern
-
The spread operator combined with the
globcommand:The
globcommand (note: not the same asinto glob) produces alistof filenames that match the glob pattern. This list can be expanded and passed to filesystem commands using the spread operator:# Find files whose name includes the current month in the form YYYY-mm let current_month = (date now | format date '%Y-%m') ls ...(glob $"*($current_month)*")
-
Force
globtype via annotation:# Find files whose name includes the current month in the form YYYY-mm let current_month = (date now | format date '%Y-%m') let glob_pattern: glob = ($"*($current_month)*") ls $glob_pattern
As with most other shells, the mkdir command is used to create new directories. One subtle difference is that Nushell's internal mkdir command operates like the Unix/Linux mkdir -p by default, in that it:
-
Will create multiple directory levels automatically. For example:
mkdir modules/my/new_moduleThis will create all three directories even if none of them currently exists. On Linux/Unix, this requires
mkdir -p. -
Will not error if the directory already exists. For example:
mkdir modules/my/new_module mkdir modules/my/new_module # => No error
::: tip A common mistake when coming to Nushell is to attempt to use
mkdir -p <directory>as in the native Linux/Unix version. However, this will generate anUnknown Flagerror on Nushell.Just repeat the command without the
-pto achieve the same effect. :::
cd cookbookTo change from the current directory to a new one, use the cd command.
Changing the current working directory can also be done if cd is omitted and a path by itself is given:
cookbook/Just as in other shells, you can use either the name of the directory, or if you want to go up a directory you can use the .. shortcut.
You can also add additional dots to go up additional directory levels:
# Change to the parent directory
cd ..
# or
..
# Go up two levels (parent's parent)
cd ...
# or
...
# Go up three levels (parent of parent's parent)
cd ....
# Etc.::: tip
Multi-dot shortcuts are available to both internal Nushell filesystem commands as well as to external commands. For example, running ^stat .... on a Linux/Unix system will show that the path is expanded to ../../..
:::
You can combine relative directory levels with directory names as well:
cd ../sibling::: tip IMPORTANT TIP
Changing the directory with cd changes the PWD environment variable. This means that a change of a directory is kept to the current scope (e.g. block or closure). Once you exit the block, you'll return to the previous directory. You can learn more about this in the Environment chapter.
:::
Nu also provides some basic filesystem commands that work cross-platform such as:
mvto rename or move a file or directory to a new locationcpto copy an item to a new locationrmto remove items from the filesystem
::: tip NOTE
Under Bash and many other shells, most filesystem commands (other than cd) are actually separate binaries in the system. For instance, on a Linux system, cp is the /usr/bin/cp binary. In Nushell, these commands are built-in. This has several advantages:
- They work consistently on platforms where a binary version may not be available (e.g. Windows). This allows the creation of cross-platform scripts, modules, and custom commands.
- They are more tightly integrated with Nushell, allowing them to understand Nushell types and other constructs
- As mentioned in the Quick Tour, they are documented in the Nushell help system. Running
help <command>or<command> --helpwill display the Nushell documentation for the command.
While the use of the Nushell built-in versions is typically recommended, it is possible to access the Linux binaries. See Running System Commands for details.
`docs/nushell/nushell.github.io/book/navigating_structured_data.md`:
```md
# Navigating and Accessing Structured Data
Given Nushell's strong support for structured data, some of the more common tasks involve navigating and accessing that data.
## Index to this Section
- [Background and Definitions](#background)
- [Cell-paths](#cell-paths)
- [With Records](#records)
- [With Lists](#lists)
- [With Tables](#tables)
- Sample Data
- Example - Access a Table Row
- Example - Access a Table Column
- [With Nested Data](#nested-data)
- [Using `get` and `select`](#using-get-and-select)
- Example - `get` vs. `select` with a Table Row
- Example - `select` with multiple rows and columns
- [Handling missing data using the optional operator](#the-optional-operator)
- [Key/Column names with spaces](#keycolumn-names-with-spaces)
- [Other commands for navigating structured data](#other-commands-for-accessing-structured-data)
## Background
For the examples and descriptions below, keep in mind several definitions regarding structured data:
- **_List:_** Lists contain a series of zero or more values of any type. A list with zero values is known as an "empty list"
- **_Record:_** Records contain zero or more pairs of named keys and their corresponding value. The data in a record's value can also be of any type. A record with zero key-value pairs is known as an "empty record"
- **_Nested Data:_** The values contained in a list, record, or table can be either of a basic type or structured data themselves. This means that data can be nested multiple levels and in multiple forms:
- List values can contain tables, records, and even other lists
- **_Table:_** Tables are a list of records
- Record values can contain tables, lists, and other records
- This means that the records of a table can also contain nested tables, lists, and other records
::: tip
Because a table is a list of records, any command or syntax that works on a list will also work on a table. The converse is not necessarily the case; there are some commands and syntax that work on tables but not lists.
:::
## Cell-paths
A cell-path is the primary way to access values inside structured data. This path is based on a concept similar to that of a spreadsheet, where columns have names and rows have numbers. Cell-path names and indices are separated by dots.
### Records
For a record, the cell-path specifies the name of a key, which is a `string`.
#### Example - Access a Record Value:
```nu
let my_record = {
a: 5
b: 42
}
$my_record.b + 5
# => 47
For a list, the cell-path specifies the position (index) of the value in the list. This is an int:
Remember, list indices are 0-based.
let scoobies_list = [ Velma Fred Daphne Shaggy Scooby ]
$scoobies_list.2
# => Daphne- To access a column, a cell-path uses the name of the column, which is a
string - To access a row, it uses the index number of the row, which is an
int - To access a single cell, it uses a combination of the column name with the row index.
The next few examples will use the following table:
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 Expand for a visual representation of this data
╭───┬─────────────┬───────────────┬───────────╮
│ # │ 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 │ │ │
│ │ │ ╰───┴───────╯ │ │
╰───┴─────────────┴───────────────┴───────────╯:::
This represents weather data in the form of a table with three columns:
- date: A Nushell
datefor each day - temps: A Nushell
listof 5floatvalues representing temperature readings at different weather stations in the area - conditions: A Nushell
stringfor each day's weather condition for the area
Access the second day's data as a record:
$data.1
# => ╭───────────┬───────────────╮
# => │ date │ 2 years ago │
# => │ │ ╭───┬───────╮ │
# => │ temps │ │ 0 │ 35.24 │ │
# => │ │ │ 1 │ 35.94 │ │
# => │ │ │ 2 │ 34.91 │ │
# => │ │ │ 3 │ 35.24 │ │
# => │ │ │ 4 │ 36.65 │ │
# => │ │ ╰───┴───────╯ │
# => │ condition │ sunny │
# => ╰───────────┴───────────────╯$data.condition
# => ╭───┬────────╮
# => │ 0 │ sunny │
# => │ 1 │ sunny │
# => │ 2 │ cloudy │
# => │ 3 │ rain │
# => ╰───┴────────╯The condition for the fourth day:
$data.condition.3
# => rainSince data can be nested, a cell-path can contain references to multiple names or indices.
To obtain the temperature at the second weather station on the third day:
$data.temps.2.1
# => 36.67The first index 2 accesses the third day, then the next index 1 accesses the second weather station's temperature reading.
In addition to the cell-path literal syntax used above, Nushell also provides several commands that utilize cell-paths. The most important of these are:
getis equivalent to using a cell-path literal but with support for variable names and expressions.get, like the cell-path examples above, returns the value indicated by the cell-path.selectis subtly, but critically, different. It returns the specified data structure itself, rather than just its value.- Using
selecton a table will return a table of equal or lesser size - Using
selecton a list will return a list of equal or lesser size - Using
selecton a record will return a record of equal or lesser size
- Using
Continuing with the sample table above:
$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 │ │ │
# => │ │ │ ╰───┴───────╯ │ │
# => ╰───┴─────────────┴───────────────┴───────────╯Notice that:
getreturns the same record as the$data.1example aboveselectreturns a new, single-row table, including column names and row indices
::: tip
The row indices of the table resulting from select are not the same as that of the original. The new table has its own, 0-based index.
To obtain the original index, you can use the enumerate command. For example:
$data | enumerate | select 1:::
Because select results in a new table, it's possible to specify multiple column names, row indices, or even both. This example creates a new table containing the date and condition columns of the first and second rows:
$data | select date condition 0 1
# => ╭───┬─────────────┬───────────╮
# => │ # │ date │ condition │
# => ├───┼─────────────┼───────────┤
# => │ 0 │ 2 years ago │ sunny │
# => │ 1 │ 2 years ago │ sunny │
# => ╰───┴─────────────┴───────────╯If a key name or column name contains spaces or other characters that prevent it from being accessible as a bare-word string, then the key name may be quoted.
Example:
let record_example = {
"key x":12
"key y":4
}
$record_example."key x"
# => 12
# or
$record_example | get "key x"
# => 12Quotes are also required when a key name may be confused for a numeric value.
Example:
let record_example = {
"1": foo
"2": baz
"3": far
}
$record_example."1"
# => fooDo not confuse the key name with a row index in this case. Here, the first item is assigned the key name 1 (a string). If converted to a table using the transpose command, key 1 (string) would be at row-index 0 (an integer).
By default, cell path access will fail if it can't access the requested row or column. To suppress these errors, you can add a ? to a cell path member to mark it as optional:
Using the temp data from above:
let cp: cell-path = $.temps?.1 # only get the 2nd location from the temps column
# Ooops, we've removed the temps column
$data | reject temps | get $cpBy default missing cells will be replaced by null when accessed via the optional operator.
The default command can be used to apply a default value to missing or null column result.
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/arejectis the opposite ofselect, removing the specified rows and columnsrangespecifies the rows of a list or table to select using arangetype
`docs/nushell/nushell.github.io/book/nu_as_a_shell.md`:
```md
---
prev:
text: Best Practices
link: /book/style_guide.md
next:
text: Configuration
link: /book/configuration.md
---
# Nu as a Shell
The [Nu Fundamentals](nu_fundamentals.md) and [Programming in Nu](programming_in_nu.md) chapter focused mostly on the language aspects of Nushell.
This chapter sheds the light on the parts of Nushell that are related to the Nushell interpreter (the Nushell [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop)).
Some of the concepts are directly a part of the Nushell programming language (such as environment variables) while others are implemented purely to enhance the interactive experience (such as hooks) and thus are not present when, for example, running a script.
Many parameters of Nushell can be [configured](configuration.md).
The config itself is stored as an environment variable.
Furthermore, Nushell has several different configuration files that are run on startup where you can put custom commands, aliases, etc.
A big feature of any shell are [environment variables](environment.md).
In Nushell, environment variables are scoped and can have any type supported by Nushell.
This brings in some additional design considerations so please refer to the linked section for more details.
The other sections explain how to work with [stdout, stderr and exit codes](stdout_stderr_exit_codes.md), how to [run an external command when there is a built-in with the same name](./running_externals.md), and how to [configure 3rd party prompts](3rdpartyprompts.md) to work with Nushell.
An interesting feature of Nushell is the [Directory Stack](directory_stack.md) which let you work in multiple directories simultaneously.
Nushell also has its own line editor [Reedline](line_editor.md).
With Nushell's config, it is possible to configure some of the Reedline's features, such as the prompt, keybindings, history, or menus.
It is also possible to define [custom signatures for external commands](externs.md) which lets you define [custom completions](custom_completions.md) for them (the custom completions also work for Nushell custom commands).
[Coloring and Theming in Nu](coloring_and_theming.md) goes into more detail about how to configure Nushell's appearance.
If you want to schedule some commands to run in the background, [Background jobs](background_jobs.md) provides simple guidelines for you to follow.
Finally, [hooks](hooks.md) allow you to insert fragments of Nushell code to run at certain events.
docs/nushell/nushell.github.io/book/nu_fundamentals.md:
---
prev:
text: Nushell Cheat Sheet
link: cheat_sheet.md
next:
text: Types of Data
link: /book/types_of_data.md
---
# Nu Fundamentals
This chapter explains some of the fundamentals of the Nushell programming language.
After going through it, you should have an idea how to write simple Nushell programs.
Nushell has a rich type system.
You will find typical data types such as strings or integers and less typical data types, such as cell paths.
Furthermore, one of the defining features of Nushell is the notion of _structured data_ which means that you can organize types into collections: lists, records, or tables.
Contrary to the traditional Unix approach where commands communicate via plain text, Nushell commands communicate via these data types.
All of the above is explained in [Types of Data](types_of_data.md).
[Loading Data](loading_data.md) explains how to read common data formats, such as JSON, into _structured data_. This includes our own "NUON" data format.
Just like Unix shells, Nushell commands can be composed into [pipelines](pipelines.md) to pass and modify a stream of data.
Some data types have interesting features that deserve their own sections: [strings](working_with_strings.md), [lists](working_with_lists.md), and [tables](working_with_tables.md).
Apart from explaining the features, these sections also show how to do some common operations, such as composing strings or updating values in a list.
Finally, [Command Reference](/commands/) lists all the built-in commands with brief descriptions.
Note that you can also access this info from within Nushell using the [`help`](/commands/docs/help.md) command.
docs/nushell/nushell.github.io/book/nushell_map.md:
# Nu map from other shells and domain specific languages
The idea behind this table is to help you understand how Nu builtins and plugins relate to other known shells and domain specific languages. We've tried to produce a map of relevant Nu commands and what their equivalents are in other languages. Contributions are welcome.
| 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)<br /><kbd>Ctrl/⌘</kbd>+<kbd>L</kbd> | | | `Clear-Host`<br /><kbd>Ctrl/⌘</kbd>+<kbd>L</kbd> | `clear`<br /><kbd>Ctrl/⌘</kbd>+<kbd>L</kbd> |
| [`config`](/commands/docs/config.md)<br />`$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)<br />[`ls --du`](/commands/docs/ls.md) | | | | `du` |
| [`each`](/commands/docs/each.md) | Cursors | | `ForEach-Object`, `foreach`, `for` | `for` |
| [`exit`](/commands/docs/exit.md)<br /><kbd>Ctrl/⌘</kbd>+<kbd>D</kbd> | | | `exit`<br /><kbd>Ctrl/⌘</kbd>+<kbd>D</kbd> | `exit`<br /><kbd>Ctrl/⌘</kbd>+<kbd>D</kbd> |
| [`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)<br />[`length`](/commands/docs/length.md)<br />[`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)<br />[`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"`<br />[`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` |
docs/nushell/nushell.github.io/book/nushell_map_functional.md:
# Nu Map from Functional Languages
The idea behind this table is to help you understand how Nu builtins and plugins relate to functional languages. We've tried to produce a map of relevant Nu commands and what their equivalents are in other languages. Contributions are welcome.
Note: this table assumes Nu 0.43 or later.
| 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 |
docs/nushell/nushell.github.io/book/nushell_map_imperative.md:
# Nu Map from Imperative Languages
The idea behind this table is to help you understand how Nu built-ins and plugins relate to imperative languages. We've tried to produce a map of programming-relevant Nu commands and what their equivalents are in other languages. Contributions are welcome.
Note: this table assumes Nu 0.94 or later.
| 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)<br />[`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)<br />[`str length`](/commands/docs/str_length.md)<br />[`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)<br />`$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 }` | | | |
docs/nushell/nushell.github.io/book/nushell_operator_map.md:
---
next:
text: Design Notes
link: /book/design_notes.md
---
# Nushell operator map
The idea behind this table is to help you understand how Nu operators relate to other language operators. We've tried to produce a map of all the nushell operators and what their equivalents are in other languages. Contributions are welcome.
Note: this table assumes Nu 0.14.1 or later.
| Nushell | SQL | Python | .NET LINQ (C#) | PowerShell | Bash |
| ------- | -------- | ------------------ | -------------------- | ---------------------- | ------------------ |
| == | = | == | == | -eq, -is | -eq |
| != | !=, <> | != | != | -ne, -isnot | -ne |
| < | < | < | < | -lt | -lt |
| <= | <= | <= | <= | -le | -le |
| > | > | > | > | -gt | -gt |
| >= | >= | >= | >= | -ge | -ge |
| =~ | like | re, in, startswith | Contains, StartsWith | -like, -contains | =~ |
| !~ | not like | not in | Except | -notlike, -notcontains | ! "str1" =~ "str2" |
| + | + | + | + | + | + |
| - | - | - | - | - | - |
| \* | \* | \* | \* | \* | \* |
| / | / | / | / | / | / |
| \*\* | pow | \*\* | Power | Pow | \*\* |
| in | in | re, in, startswith | Contains, StartsWith | -In | case in |
| not-in | not in | not in | Except | -NotIn | |
| and | and | and | && | -And, && | -a, && |
| or | or | or | \|\| | -Or, \|\| | -o, \|\| |
docs/nushell/nushell.github.io/book/operators.md:
# Operators
Nushell supports the following operators for common math, logic, and string operations:
| Operator | Description |
| ------------------ | ------------------------------------------------------- |
| `+` | add |
| `-` | subtract |
| `*` | multiply |
| `/` | divide |
| `//` | floor division |
| `mod` | modulo |
| `**` | exponentiation (power) |
| `==` | equal |
| `!=` | not equal |
| `<` | less than |
| `<=` | less than or equal |
| `>` | greater than |
| `>=` | greater than or equal |
| `=~` or `like` | regex match / string contains another |
| `!~` or `not-like` | inverse regex match / string does *not* contain another |
| `in` | value in list |
| `not-in` | value not in list |
| `has` | list has value |
| `not-has` | list does not have value |
| `not` | logical not |
| `and` | and two Boolean expressions (short-circuits) |
| `or` | or two Boolean expressions (short-circuits) |
| `xor` | exclusive or two boolean expressions |
| `bit-or` | bitwise or |
| `bit-xor` | bitwise xor |
| `bit-and` | bitwise and |
| `bit-shl` | bitwise shift left |
| `bit-shr` | bitwise shift right |
| `starts-with` | string starts with |
| `ends-with` | string ends with |
| `++` | append lists |
Parentheses can be used for grouping to specify evaluation order or for calling commands and using the results in an expression.
## Order of Operations
To understand the precedence of operations, you can run the command: `help operators | sort-by precedence -r`.
Presented in descending order of precedence, the article details the operations as follows:
- Parentheses (`()`)
- Exponentiation/Power (`**`)
- Multiply (`*`), Divide (`/`), Integer/Floor Division (`//`), and Modulo (`mod`)
- Add (`+`) and Subtract (`-`)
- Bit shifting (`bit-shl`, `bit-shr`)
- Comparison operations (`==`, `!=`, `<`, `>`, `<=`, `>=`), membership tests (`in`, `not-in`, `starts-with`, `ends-with`), regex matching (`=~`, `!~`), and list appending (`++`)
- Bitwise and (`bit-and`)
- Bitwise xor (`bit-xor`)
- Bitwise or (`bit-or`)
- Logical and (`and`)
- Logical xor (`xor`)
- Logical or (`or`)
- Assignment operations
- Logical not (`not`)
```nu
3 * (1 + 2)
# => 9Not all operations make sense for all data types. If you attempt to perform an operation on non-compatible data types, you will be met with an error message that should explain what went wrong:
"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.The rules might sometimes feel a bit strict, but on the other hand there should be less unexpected side effects.
The =~ and !~ operators provide a convenient way to evaluate regular expressions. You don't need to know regular expressions to use them - they're also an easy way to check whether 1 string contains another.
string =~ patternreturns true ifstringcontains a match forpattern, and false otherwise.string !~ patternreturns false ifstringcontains a match forpattern, and true otherwise.
For example:
foobarbaz =~ bar # returns true
foobarbaz !~ bar # returns false
ls | where name =~ ^nu # returns all files whose names start with "nu"Both operators use the Rust regex crate's is_match() function.
Operators are usually case-sensitive when operating on strings. There are a few ways to do case-insensitive work instead:
- In the regular expression operators, specify the
(?i)case-insensitive mode modifier:
"FOO" =~ "foo" # returns false
"FOO" =~ "(?i)foo" # returns true- Use the
str containscommand's--ignore-caseflag:
"FOO" | str contains --ignore-case "foo"- Convert strings to lowercase with
str downcasebefore comparing:
("FOO" | str downcase) == ("Foo" | str downcase)Nushell has a spread operator (...) for unpacking lists and records. You may be familiar with it
if you've used JavaScript before. Some languages use * for their spread/splat operator. It can
expand lists or records in places where multiple values or key-value pairs are expected.
There are three places you can use the spread operator:
Suppose you have multiple lists you want to concatenate together, but you also want to intersperse
some individual values. This can be done with append and prepend, but the spread
operator can let you do it more easily.
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 │
# => ╰───┴───────────────────────────────╯The below code is an equivalent version using append:
$dogs |
append Polly |
append ($cats | each { |elt| $"($elt) \(cat\)" }) |
append [Porky Bessie] |
append ...NemoNote that each call to append results in the creation of a new list, meaning that in this second
example, 3 unnecessary intermediate lists are created. This is not the case with the spread operator,
so there may be (very minor) performance benefits to using ... if you're joining lots of large
lists together, over and over.
You may have noticed that the last item of the resulting list above is "...Nemo". This is because
inside list literals, it can only be used to spread lists, not strings. As such, inside list literals, it can
only be used before variables (...$foo), subexpressions (...(foo)), and list literals (...[foo]).
The ... also won't be recognized as the spread operator if there's any whitespace between it and
the next expression:
[ ... [] ]
# => ╭───┬────────────────╮
# => │ 0 │ ... │
# => │ 1 │ [list 0 items] │
# => ╰───┴────────────────╯This is mainly so that ... won't be confused for the spread operator in commands such as mv ... $dir.
Let's say you have a record with some configuration information and you want to add more fields to this record:
let config = { path: /tmp, limit: 5 }You can make a new record with all the fields of $config and some new additions using the spread
operator. You can use the spread multiple records inside a single record literal.
{
...$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 │
# => ╰────────────┴───────────────╯Similarly to lists, inside record literals, the spread operator can only be used before variables (...$foo),
subexpressions (...(foo)), and record literals (...{foo:bar}). Here too, there needs to be no
whitespace between the ... and the next expression for it to be recognized as the spread operator.
You can also spread arguments to a command, provided that it either has a rest parameter or is an external command.
Here is an example custom command that has a rest parameter:
def foo [ --flag req opt? ...args ] {
{ flag: $flag, req: $req, opt: $opt, args: $args } | to nuon
}It has one flag (--flag), one required positional parameter (req), one optional positional parameter
(opt?), and rest parameter (args).
If you have a list of arguments to pass to args, you can spread it the same way you'd spread a list
inside a list literal. The same rules apply: the spread operator is only
recognized before variables, subexpressions, and list literals, and no whitespace is allowed in between.
foo "bar" "baz" ...[1 2 3] # With ..., the numbers are treated as separate arguments
# => { flag: false, req: bar, opt: baz, args: [1, 2, 3] }
foo "bar" "baz" [1 2 3] # Without ..., [1 2 3] is treated as a single argument
# => { flag: false, req: bar, opt: baz, args: [[1, 2, 3]] }A more useful way to use the spread operator is if you have another command with a rest parameter
and you want it to forward its arguments to foo:
def bar [ ...args ] { foo --flag "bar" "baz" ...$args }
bar 1 2 3
# => { flag: true, req: bar, opt: baz, args: [1, 2, 3] }You can spread multiple lists in a single call, and also intersperse individual arguments:
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] }Flags/named arguments can go after a spread argument, just like they can go after regular rest arguments:
foo "bar" "baz" 1 ...[2 3] --flag 4
# => { flag: true, req: bar, opt: baz, args: [1, 2, 3, 4] }If a spread argument comes before an optional positional parameter, that optional parameter is treated as being omitted:
foo "bar" ...[1 2] "not opt" # The null means no argument was given for opt
# => { flag: false, req: bar, opt: null, args: [1, 2, "not opt"] }
`docs/nushell/nushell.github.io/book/overlays.md`:
```md
# Overlays
Overlays act as "layers" of definitions (custom commands, aliases, environment variables) that can be activated and deactivated on demand.
They resemble virtual environments found in some languages, such as Python.
_Note: To understand overlays, make sure to check [Modules](modules.md) first as overlays build on top of modules._
## Basics
First, Nushell comes with one default overlay called `zero`.
You can inspect which overlays are active with the [`overlay list`](/commands/docs/overlay_list.md) command.
You should see the default overlay listed there.
To create a new overlay, you first need a module:
```nu
module spam {
export def foo [] {
"foo"
}
export alias bar = echo "bar"
export-env {
load-env { BAZ: "baz" }
}
}
We'll use this module throughout the chapter, so whenever you see overlay use spam, assume spam is referring to this module.
::: tip The module can be created by any of the three methods described in Modules:
- "inline" modules (used in this example)
- file
- directory :::
To create the overlay, call overlay use:
overlay use spam
foo
# => foo
bar
# => bar
$env.BAZ
# => baz
overlay list
# => ───┬──────
# => 0 │ zero
# => 1 │ spam
# => ───┴──────It brought the module's definitions into the current scope and evaluated the export-env block the same way as use command would (see Modules chapter).
::: tip
In the following sections, the > prompt will be preceded by the name of the last active overlay.
(spam)> some-command means the spam overlay is the last active overlay when the command was typed.
:::
If you don't need the overlay definitions anymore, call overlay hide:
(spam)> overlay hide spam
(zero)> foo
Error: Can't run executable...
(zero)> overlay list
───┬──────
0 │ zero
───┴──────The overlays are also scoped. Any added overlays are removed at the end of the scope:
(zero)> do { overlay use spam; foo } # overlay is active only inside the block
foo
(zero)> overlay list
───┬──────
0 │ zero
───┴──────The last way to remove an overlay is to call overlay hide without an argument which will remove the last active overlay.
Any new definition (command, alias, environment variable) is recorded into the last active overlay:
(zero)> overlay use spam
(spam)> def eggs [] { "eggs" }Now, the eggs command belongs to the spam overlay.
If we remove the overlay, we can't call it anymore:
(spam)> overlay hide spam
(zero)> eggs
Error: Can't run executable...But we can bring it back!
(zero)> overlay use spam
(spam)> eggs
eggsOverlays remember what you add to them and store that information even if you remove them. This can let you repeatedly swap between different contexts.
::: tip Sometimes, after adding an overlay, you might not want custom definitions to be added into it. The solution can be to create a new empty overlay that would be used just for recording the custom changes:
(zero)> overlay use spam
(spam)> module scratchpad { }
(spam)> overlay use scratchpad
(scratchpad)> def eggs [] { "eggs" }The eggs command is added into scratchpad while keeping spam intact.
To make it less verbose, you can use the overlay new command:
(zero)> overlay use spam
(spam)> overlay new scratchpad
(scratchpad)> def eggs [] { "eggs" }:::
The overlay use command would take all commands and aliases from the module and put them directly into the current namespace.
However, you might want to keep them as subcommands behind the module's name.
That's what --prefix is for:
(zero)> module spam {
export def foo [] { "foo" }
}
(zero)> overlay use --prefix spam
(spam)> spam foo
fooNote that this does not apply for environment variables.
You can change the name of the added overlay with the as keyword:
(zero)> module spam { export def foo [] { "foo" } }
(zero)> overlay use spam as eggs
(eggs)> foo
foo
(eggs)> overlay hide eggs
(zero)>This can be useful if you have a generic script name, such as virtualenv's activate.nu but you want a more descriptive name for your overlay.
Sometimes, you might want to remove an overlay, but keep all the custom definitions you added without having to redefine them in the next active overlay:
(zero)> overlay use spam
(spam)> def eggs [] { "eggs" }
(spam)> overlay hide --keep-custom spam
(zero)> eggs
eggsThe --keep-custom flag does exactly that.
One can also keep a list of environment variables that were defined inside an overlay, but remove the rest, using the --keep-env flag:
(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
fooThe overlays are arranged as a stack.
If multiple overlays contain the same definition, say foo, the one from the last active one would take precedence.
To bring an overlay to the top of the stack, you can call overlay use again:
(zero)> def foo [] { "foo-in-zero" }
(zero)> overlay use spam
(spam)> foo
foo
(spam)> overlay use zero
(zero)> foo
foo-in-zero
(zero)> overlay list
───┬──────
0 │ spam
1 │ zero
───┴──────Now, the zero overlay takes precedence.
`docs/nushell/nushell.github.io/book/parallelism.md`:
```md
# Parallelism
Nushell now has early support for running code in parallel. This allows you to process elements of a stream using more hardware resources of your computer.
You will notice these commands with their characteristic `par-` naming. Each corresponds to a non-parallel version, allowing you to easily write code in a serial style first, and then go back and easily convert serial scripts into parallel scripts with a few extra characters.
## par-each
The most common parallel command is [`par-each`](/commands/docs/par-each.md), a companion to the [`each`](/commands/docs/each.md) command.
Like [`each`](/commands/docs/each.md), [`par-each`](/commands/docs/par-each.md) works on each element in the pipeline as it comes in, running a block on each. Unlike [`each`](/commands/docs/each.md), [`par-each`](/commands/docs/par-each.md) will do these operations in parallel.
Let's say you wanted to count the number of files in each sub-directory of the current directory. Using [`each`](/commands/docs/each.md), you could write this as:
```nu
ls | where type == dir | each { |row|
{ name: $row.name, len: (ls $row.name | length) }
}
We create a record for each entry, and fill it with the name of the directory and the count of entries in that sub-directory.
On your machine, the times may vary. For this machine, it took 21 milliseconds for the current directory.
Now, since this operation can be run in parallel, let's convert the above to parallel by changing each to par-each:
ls | where type == dir | par-each { |row|
{ name: $row.name, len: (ls $row.name | length) }
}On this machine, it now runs in 6ms. That's quite a difference!
As a side note: Because environment variables are scoped, you can use par-each to work in multiple directories in parallel (notice the cd command):
ls | where type == dir | par-each { |row|
{ name: $row.name, len: (cd $row.name; ls | length) }
}You'll notice, if you look at the results, that they come back in different orders each run (depending on the number of hardware threads on your system). As tasks finish, and we get the correct result, we may need to add additional steps if we want our results in a particular order. For example, for the above, we may want to sort the results by the "name" field. This allows both each and par-each versions of our script to give the same result.
`docs/nushell/nushell.github.io/book/pipelines.md`:
```md
# Pipelines
One of the core designs of Nu is the pipeline, a design idea that traces its roots back decades to some of the original philosophy behind Unix. Just as Nu extends from the single string data type of Unix, Nu also extends the idea of the pipeline to include more than just text.
## Basics
A pipeline is composed of three parts: the input, the filter, and the output.
```nu
open Cargo.toml | update workspace.dependencies.base64 0.24.2 | save Cargo_new.toml
The first command, open Cargo.toml, is an input (sometimes also called a "source" or "producer"). This creates or loads data and feeds it into a pipeline. It's from input that pipelines have values to work with. Commands like ls are also inputs, as they take data from the filesystem and send it through the pipelines so that it can be used.
The second command, update workspace.dependencies.base64 0.24.2, is a filter. Filters take the data they are given and often do something with it. They may change it (as with the update command in our example), or they may perform other operations, like logging, as the values pass through.
The last command, save Cargo_new.toml, is an output (sometimes called a "sink"). An output takes input from the pipeline and does some final operation on it. In our example, we save what comes through the pipeline to a file as the final step. Other types of output commands may take the values and view them for the user.
The $in variable will collect the pipeline into a value for you, allowing you to access the whole stream as a parameter:
[1 2 3] | $in.1 * $in.2
# => 6If a pipeline is getting a bit long for one line, you can enclose it within parentheses ():
let year = (
"01/22/2021" |
parse "{month}/{day}/{year}" |
get year
)Take this example:
line1; line2 | line3Here, semicolons are used in conjunction with pipelines. When a semicolon is used, no output data is produced to be piped. As such, the $in variable will not work when used immediately after the semicolon.
- As there is a semicolon after
line1, the command will run to completion and get displayed on the screen. line2|line3is a normal pipeline. It runs, and its contents are displayed afterline1's contents.
Much of Nu's composability comes from the special $in variable, which holds the current pipeline input.
$in is particular useful when used in:
- Command or external parameters
- Filters
- Custom command definitions or scripts that accept pipeline input
Compare the following two command-lines that create a directory with tomorrow's date as part of the name. The following are equivalent:
Using subexpressions:
mkdir $'((date now) + 1day | format date '%F') Report'or using pipelines:
date now # 1: today
| $in + 1day # 2: tomorrow
| format date '%F' # 3: Format as YYYY-MM-DD
| $'($in) Report' # 4: Format the directory name
| mkdir $in # 5: Create the directoryWhile the second form may be overly verbose for this contrived example, you'll notice several advantages:
- It can be composed step-by-step with a simple ↑ (up arrow) to repeat the previous command and add the next stage of the pipeline.
- It's arguably more readable.
- Each step can, if needed, be commented.
- Each step in the pipeline can be
inspected for debugging.
Let's examine the contents of $in on each line of the above example:
- On line 2,
$inrefers to the results of line 1'sdate now(a datetime value). - On line 4,
$inrefers to tomorrow's formatted date from line 3 and is used in an interpolated string - On line 5,
$inrefers to the results of line 4's interpolated string, e.g. '2024-05-14 Report'
Certain filter commands may modify the pipeline input to their closure in order to provide more convenient access to the expected context. For example:
1..10 | each {$in * 2}Rather than referring to the entire range of 10 digits, the each filter modifies $in to refer to the value of the current iteration.
In most filters, the pipeline input and its resulting $in will be the same as the closure parameter. For the each filter, the following example is equivalent to the one above:
1..10 | each {|value| $value * 2}However, some filters will assign an even more convenient value to their closures' input. The update filter is one example. The pipeline input to the update command's closure (as well as $in) refers to the column being updated, while the closure parameter refers to the entire record. As a result, the following two examples are also equivalent:
ls | update name {|file| $file.name | str upcase}
ls | update name {str upcase}With most filters, the second version would refer to the entire file record (with name, type, size, and modified columns). However, with update, it refers specifically to the contents of the column being updated, in this case name.
See: Custom Commands -> Pipeline Input
-
Rule 1: When used in the first position of a pipeline in a closure or block,
$inrefers to the pipeline (or filter) input to the closure/block.Example:
def echo_me [] { print $in } true | echo_me # => true
-
Rule 1.5: This is true throughout the current scope. Even on subsequent lines in a closure or block,
$inis the same value when used in the first position of any pipeline inside that scope.Example:
[ a b c ] | each { print $in print $in $in }
All three of the
$invalues are the same on each iteration, so this outputs:a a b b c c ╭───┬───╮ │ 0 │ a │ │ 1 │ b │ │ 2 │ c │ ╰───┴───╯
-
Rule 2: When used anywhere else in a pipeline (other than the first position),
$inrefers to the previous expression's result:Example:
4 # Pipeline input | $in * $in # $in is 4 in this expression | $in / 2 # $in is now 16 in this expression | $in # $in is now 8 # => 8
-
Rule 2.5: Inside a closure or block, Rule 2 usage occurs inside a new scope (a sub-expression) where that "new"
$invalue is valid. This means that Rule 1 and Rule 2 usage can coexist in the same closure or block.Example:
4 | do { print $in # closure-scope $in is 4 let p = ( # explicit sub-expression, but one will be created regardless $in * $in # initial-pipeline position $in is still 4 here | $in / 2 # $in is now 16 ) # $p is the result, 8 - Sub-expression scope ends print $in # At the closure-scope, the "original" $in is still 4 print $p }
So the output from the 3
printstatements is:4 4 8
Again, this would hold true even if the command above used the more compact, implicit sub-expression form:
Example:
4 | do { print $in # closure-scope $in is 4 let p = $in * $in | $in / 2 # Implicit let sub-expression print $in # At the closure-scope, $in is still 4 print $p } 4 4 8
-
Rule 3: When used with no input,
$inis null.Example:
# Input 1 | do { $in | describe } # => int "Hello, Nushell" | do { $in | describe } # => string {||} | do { $in | describe } # => closure # No input do { $in | describe } # => nothing
-
Rule 4: In a multi-statement line separated by semicolons,
$incannot be used to capture the results of the previous statement.This is the same as having no-input:
ls / | get name; $in | describe # => nothing
Instead, simply continue the pipeline:
ls / | get name | $in | describe # => list<string>
While $in can be reused as demonstrated above, assigning its value to another variable in the first line of your closure/block will often aid in readability and debugging.
Example:
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 yearCurrently, the use of $in on a stream in a pipeline results in a "collected" value, meaning the pipeline "waits" on the stream to complete before handling $in with the full results. However, this behavior is not guaranteed in future releases. To ensure that a stream is collected into a single variable, use the collect command.
Likewise, avoid using $in when normal pipeline input will suffice, as internally $in forces a conversion from PipelineData to Value and may result in decreased performance and/or increased memory usage.
Nu commands communicate with each other using the Nu data types (see types of data), but what about commands outside of Nu? Let's look at some examples of working with external commands:
internal_command | external_command
Data will flow from the internal_command to the external_command. This data will get converted to a string, so that they can be sent to the stdin of the external_command.
external_command | internal_command
Data coming from an external command into Nu will come in as bytes that Nushell will try to automatically convert to UTF-8 text. If successful, a stream of text data will be sent to internal_command. If unsuccessful, a stream of binary data will be sent to internal command. Commands like lines help make it easier to bring in data from external commands, as it gives discrete lines of data to work with.
external_command_1 | external_command_2
Nu works with data piped between two external commands in the same way as other shells, like Bash would. The stdout of external_command_1 is connected to the stdin of external_command_2. This lets data flow naturally between the two commands.
The Basics section above describes how commands can be combined in pipelines as input, filters, or output. How you can use commands depends on what they offer in terms of input/output handling.
You can check what a command supports with help <command name>, which shows the relevant Input/output types.
For example, through help first we can see that the first command supports multiple input and output types:
help first
# => […]
# => Input/output types:
# => ╭───┬───────────┬────────╮
# => │ # │ input │ output │
# => ├───┼───────────┼────────┤
# => │ 0 │ list<any> │ any │
# => │ 1 │ binary │ binary │
# => │ 2 │ range │ any │
# => ╰───┴───────────┴────────╯
[a b c] | first took 1ms
# => a
1..4 | first took 21ms
# => 1As another example, the ls command supports output but not input:
help ls
# => […]
# => Input/output types:
# => ╭───┬─────────┬────────╮
# => │ # │ input │ output │
# => ├───┼─────────┼────────┤
# => │ 0 │ nothing │ table │
# => ╰───┴─────────┴────────╯This means, for example, that attempting to pipe into ls (echo .. | ls) leads to unintended results.
The input stream is ignored, and ls defaults to listing the current directory.
To integrate a command like ls into a pipeline, you have to explicitly reference the input and pass it as a parameter:
echo .. | ls $inNote that this only works if $in matches the argument type. For example, [dir1 dir2] | ls $in will fail with the error can't convert list<string> to string.
Other commands without default behavior may fail in different ways, and with explicit errors.
For example, help sleep tells us that sleep supports no input and no output types:
help sleep
# => […]
# => Input/output types:
# => ╭───┬─────────┬─────────╮
# => │ # │ input │ output │
# => ├───┼─────────┼─────────┤
# => │ 0 │ nothing │ nothing │
# => ╰───┴─────────┴─────────╯When we erroneously pipe into it, instead of unintended behavior like in the ls example above, we receive an error:
echo 1sec | sleep
# => Error: nu::parser::missing_positional
# =>
# => × Missing required positional argument.
# => ╭─[entry #53:1:18]
# => 1 │ echo 1sec | sleep
# => ╰────
# => help: Usage: sleep <duration> ...(rest) . Use `--help` for more information.While there is no steadfast rule, Nu generally tries to copy established conventions in command behavior,
or do what 'feels right'.
The sleep behavior of not supporting an input stream matches Bash sleep behavior for example.
Many commands do have piped input/output however, and if it's ever unclear, check their help documentation as described above.
In interactive mode, when a pipeline ends, the display_output hook configuration defines how the result will be displayed.
The default configuration uses the table command to render structured data as a visual table.
The following example shows how the display_output hook can render
- an expanded table with
table -e - an unexpanded table with
table - an empty closure
{||}and empty string''lead to simple output nullcan be assigned to clear any customization, reverting back to default behavior
$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]
# clear to default behavior
$env.config.hooks.display_output = null
[1,2,3,[4,5,6]]
# => ╭───┬────────────────╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ [list 3 items] │
# => ╰───┴────────────────╯Sometimes you want to output Nushell structured data to an external command for further processing. However, Nushell's default formatting options for structured data may not be what you want. For example, you want to find a file named "tutor" under "/usr/share/vim/runtime" and check its ownership
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 │
# => ╰────┴───────────────────────────────────────┴──────┴─────────┴───────────────╯You decided to use grep and pipe the result to external ^ls
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 directoryWhat's wrong? Nushell renders lists and tables (by adding a border with characters like ╭,─,┬,╮) before piping them as text to external commands. If that's not the behavior you want, you must explicitly convert the data to a string before piping it to an external. For example, you can do so with to text:
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(Actually, for this simple usage you can just use find)
ls /usr/share/nvim/runtime/ | get name | find tutor | ansi strip | ^ls -al ...$inUnlike external commands, Nushell commands are akin to functions. Most Nushell commands do not print anything to stdout and instead just return data.
do { ls; ls; ls; "What?!" }This means that the above code will not display the files under the current directory three times.
In fact, running this in the shell will only display "What?!" because that is the value returned by the do command in this example. However, using the system ^ls command instead of ls would indeed print the directory thrice because ^ls does print its result once it runs.
Knowing when data is displayed is important when using configuration variables that affect the display output of commands such as table.
do { $env.config.table.mode = "none"; ls }For instance, the above example sets the $env.config.table.mode configuration variable to none, which causes the table command to render data without additional borders. However, as it was shown earlier, the command is effectively equivalent to
do { $env.config.table.mode = "none"; ls } | tableBecause Nushell $env variables are scoped, this means that the table command in the example is not affected by the
environment modification inside the do block and the data will not be shown with the applied configuration.
When displaying data early is desired, it is possible to explicitly apply | table inside the scope, or use the print command.
do { $env.config.table.mode = "none"; ls | table }
do { $env.config.table.mode = "none"; print (ls) }
`docs/nushell/nushell.github.io/book/plugins.md`:
```md
# Plugins
Nu can be extended using plugins. Plugins behave much like Nu's built-in commands, with the added benefit that they can be added separately from Nu itself.
::: warning Important
Plugins communicate with Nushell using the `nu-plugin` protocol. This protocol is versioned, and plugins must use the same `nu-plugin` version provided by Nushell.
When updating Nushell, please make sure to also update any plugins that you have registered.
:::
[[toc]]
## Overview
- In order to use a plugin, it needs to be:
- Installed
- Added
- Imported
There are two types of plugins:
- "Core plugins" are officially maintained and are usually installed with Nushell, in the same directory as the Nushell executable.
- Third-party plugins are also available from many sources.
The `$NU_LIB_DIRS` constant or `$env.NU_LIB_DIRS` environment variable can be used to set the search-path for plugins.
### Core Plugin Quickstart
To begin using the Polars plugin:
1. Most package managers will automatically install the core plugins with Nushell. A notable exception, however, is `cargo`. If you installed
Nushell using `cargo`, see [Installing Core Plugins](#core-plugins) below.
2. (Recommended) Set the plugin search path to include the directory where Nushell and its plugins are installed. Assuming the core plugins are installed
in the same directory as the Nushell binary, the following can be added to your startup config:
```nu
const NU_PLUGIN_DIRS = [
($nu.current-exe | path dirname)
...$NU_PLUGIN_DIRS
]
-
Add the plugin to the plugin registry. This only needs to be done one time. The name is the name of the plugin file, including its extension:
# On Unix/Linux platforms: plugin add nu_plugin_polars # Or on Windows plugin add nu_plugin_polars.exe plugin list # Confirm it was added to the registry
Alternatively, if you did not add the binary directory to the plugin path in Step 2, you can still use an absolute path:
plugin add ~/.local/share/rust/cargo/bin/nu_plugin_polars
-
Import the plugin (to begin using it immediately) or restart Nushell. All plugins in the registry are automatically imported when Nushell starts:
# The name of the plugin, without the leading `nu_plugin` nor any extension plugin use polars -
Confirm the plugin is working:
ls | polars into-df | describe # => NuDataFrame
Nushell ships with a set of officially maintained plugins which includes:
polars: Extremely fast columnar operations using DataFrames via the Polars Library. See the DataFrames Chapter for more details.formats: Support for several additional data formats - EML, ICS, INI, plist, and VCF.gstat: Returns information on the status of a Git repository as Nushell structured data.query: Support for querying SQL, XML, JSON, HTML (via selector), and WebPage Metadatainc: Increment a value or version (e.g., semver). This plugin acts as both an end-user plugin as well as a simple developer example of how to create a plugin.
Nushell also ships with several plugins that serve as examples or tools for plugin developers. These include nu_plugin_example, nu_plugin_custom_values, and nu_plugin_stress_internals.
Core plugins are typically distributed with the Nushell release and should already be installed in the same directory as the Nushell executable. If this is the case on your system, core plugins should be using correct nu-plugin protocol version. If your package management system installs them separately, please make sure to update the core plugins whenever Nushell itself is updated.
::: tip Installing using Cargo
For example, when installing or upgrading Nushell directly from crates.io using cargo install nu --locked, the corresponding core plugins for that version may also be installed or updated using cargo install nu_plugin_<plugin_name> --locked.
To install all of the default (non-developer) plugins, from within Nushell run:
[ nu_plugin_inc
nu_plugin_polars
nu_plugin_gstat
nu_plugin_formats
nu_plugin_query
] | each { cargo install $in --locked } | ignore:::
You can find third-party plugins on crates.io, online Git repositories, awesome-nu, and other sources. As with any third-party code you run on your system, please make sure you trust its source.
To install a third-party plugin on your system, you first need to make sure the plugin uses the same version of Nu as your system:
- Confirm your Nushell version with the
versioncommand - Confirm the version the plugin requires by checking its
Cargo.tomlfile
To install a plugin by name from crates.io, run:
cargo install nu_plugin_<plugin_name> --lockedWhen installing from a repository (e.g., GitHub), run the following from inside the cloned repository:
cargo install --path . --lockedThis will create a binary file that can be used to add the plugin.
::: tip Cargo installation location
By default, binaries installed with cargo install are placed in your home directory under .cargo/bin.
However, this can change depending on how your system is configured.
:::
To add a plugin to the plugin registry file, call the plugin add command to tell Nu where to find it.
::: tip Note
The plugin file name must start with nu_plugin_, Nu uses this filename prefix to identify plugins.
:::
-
Linux and macOS:
plugin add ./my_plugins/nu_plugin_cool
-
Windows:
plugin add .\my_plugins\nu_plugin_cool.exe
When plugin add is called, Nu:
- Runs the plugin binary
- Communicates via the plugin protocol in order to ensure compatibility and to get a list of all of the commands it supports
- This plugin information is then saved to the plugin registry file (
$nu.plugin-path), which acts as a cache
Once added to the registry, the next time nu is started, the plugin will be imported and available in that session.
You can also immediately import (or reload) a plugin in the current session by calling plugin use. In this case, the name of the plugin (rather than the filename) is used without the nu_plugin prefix:
plugin use coolIt is not necessary to add plugin use statements to your config file. All previously registered plugins are automatically loaded at startup.
::: tip Note
plugin use is a parser keyword, so when evaluating a script, it will be evaluated first. This means that while you can execute plugin add and then plugin use at the REPL on separate lines, you can't do this in a single script. If you need to run nu with a specific plugin or set of plugins without preparing a cache file, you can pass the --plugins option to nu with a list of plugin executable files:
nu --plugins '[./my_plugins/nu_plugin_cool]':::
Nushell includes two list variables that can be used to set a search path for plugins. This only applies when registering plugins with plugin add, but it can be a nice shortcut
if you commonly add and remove plugins.
const NU_PLUGIN_DIRS: A constant which takes effect immediately when set. However, as a constant, only certain commands may be used with it. It can be updated, for example, as seen in the QuickStart above.$env.NU_PLUGIN_DIRS: An environment variable which is mutable and can accept any command that updates its list. However, changes to it will not take effect until the next expression is parsed.
When updating a plugin, it is important to run plugin add again just as above to load the new signatures from the plugin and allow Nu to rewrite them to the plugin file ($nu.plugin-path). You can then plugin use to get the updated signatures within the current session.
Installed plugins are displayed using plugin list:
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 │ │
│ │ │ │ │ │ │ ╰───┴───────────────────────╯ │
╰───┴─────────┴────────────┴─────────┴───────────────────────┴───────┴───────────────────────────────╯All of the commands from installed plugins are available in the current scope:
scope commands | where type == "plugin"Plugins stay running while they are in use, and are automatically stopped by default after a period of time of inactivity. This behavior is managed by the plugin garbage collector. To manually stop a plugin, call plugin stop with its name:
For example, run the gstat command from the corresponding plugin, then check its is_running status:
gstat
# => gstat output
plugin list | where name == gstat | select name is_running
# =>
╭───┬───────┬────────────╮
│ # │ name │ is_running │
├───┼───────┼────────────┤
│ 0 │ gstat │ true │
╰───┴───────┴────────────╯Now stop the plugin manually, and we can see that it is no longer running:
plugin stop gstat
plugin list | where name == gstat | select name is_running
# =>
╭───┬───────┬────────────╮
│ # │ name │ is_running │
├───┼───────┼────────────┤
│ 0 │ gstat │ false │
╰───┴───────┴────────────╯As mentioned above, Nu comes with a plugin garbage collector which automatically stops plugins that are not actively in use after a period of time (by default, 10 seconds). This behavior is fully configurable:
$env.config.plugin_gc = {
# Settings for plugins not otherwise specified:
default: {
enabled: true # set to false to never automatically stop plugins
stop_after: 10sec # how long to wait after the plugin is inactive before stopping it
}
# Settings for specific plugins, by plugin name
# (i.e. what you see in `plugin list`):
plugins: {
gstat: {
stop_after: 1min
}
inc: {
stop_after: 0sec # stop as soon as possible
}
example: {
enabled: false # never stop automatically
}
}
}For information on when a plugin is considered to be active, see the relevant section in the contributor book.
To remove a plugin, call plugin rm <plugin_name>. Note that this is the plugin name, rather than the filename. For example, if you previously added the plugin ~/.cargo/bin/nu_plugin_gstat, its name would be gstat. To remove it:
plugin rm gstatYou can confirm the name of a plugin by running plugin list.
Running plugin rm removes the plugin from the registry so that it will not be loaded the next time Nushell starts. However, any commands created by the plugin remain in scope until the current Nushell session ends.
Nu plugins are executables; Nu launches them as needed and communicates with them over stdin and stdout or local sockets. Nu plugins can use either JSON or MessagePack as their communication encoding.
Nu's main repo contains example plugins that are useful for learning how the plugin protocol works:
The simplest way to debug a plugin is to print to stderr; plugins' standard error streams are redirected through Nu and displayed to the user.
The Nu plugin protocol message stream may be captured for diagnostic purposes using trace_nu_plugin.
::: warning
Trace output will accumulate for as long as the plugin is installed with the trace wrapper. Large files are possible. Be sure to remove the plugin with plugin rm when finished tracing, and reinstall without the trace wrapper.**
:::
Nu's plugin documentation is a work in progress. If you're unsure about something, the #plugins channel on the Nu Discord is a great place to ask questions!
The plugin chapter in the contributor book offers more details on the intricacies of how plugins work from a software developer point of view.
`docs/nushell/nushell.github.io/book/programming_in_nu.md`:
```md
---
prev:
text: Special Variables
link: /book/special_variables.md
next:
text: Custom Commands
link: /book/custom_commands.md
---
# Programming in Nu
This chapter goes into more detail of Nushell as a programming language.
Each major language feature has its own section.
Just like most programming languages allow you to define functions, Nushell uses [custom commands](custom_commands.md) for this purpose.
From other shells you might be used to [aliases](aliases.md).
Nushell's aliases work in a similar way and are a part of the programming language, not just a shell feature.
Common operations, such as addition or regex search, can be done with [operators](operators.md).
Not all operations are supported for all data types, and Nushell will make sure to let you know when there is a mismatch.
You can store intermediate results to [variables](variables.md).
Variables can be immutable, mutable, or a parse-time constant.
The last three sections are aimed at organizing your code:
[Scripts](scripts.md) are the simplest form of code organization: You just put the code into a file and source it.
However, you can also run scripts as standalone programs with command line signatures using the "special" `main` command.
With [modules](modules.md), just like in many other programming languages, it is possible to compose your code from smaller pieces.
Modules let you define a public interface vs. private commands and you can import custom commands, aliases, and environment variables from them.
[Overlays](overlays.md) build on top of modules.
By defining an overlay, you bring in module's definitions into its own swappable "layer" that gets applied on top of other overlays.
This enables features like activating virtual environments or overriding sets of default commands with custom variants.
The standard library also has a [testing framework](testing.md) if you want to prove your reusable code works perfectly.
docs/nushell/nushell.github.io/book/quick_tour.md:
---
prev:
text: Getting Started
link: /book/getting_started.md
---
# Quick Tour
[[toc]]
## Nushell Commands Output _Data_
The easiest way to see what Nu can do is to start with some examples, so let's dive in.
The first thing you'll notice when you run a command like [`ls`](/commands/docs/ls.md) is that instead of a block of text coming back, you get a structured table.
```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 │
# => ...This table does more than just format the output nicely. Like a spreadsheet, it allows us to work with the data interactively.
Next, let's sort this table by each file's size. To do this, we'll take the output from ls and feed it into a command that can sort tables based on the values in a column.
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 │
# => ╰───┴─────────────────┴──────┴───────────┴──────────────╯
Notice that we didn't pass commandline arguments or switches to ls. Instead, we used Nushell's built-in sort-by command to sort the output of the ls command. Then, to see the largest files on top, we used reverse on the output of sort-by.
::: tip Cool!
If you compare the sort order closely, you might realize that the data isn't sorted alphabetically. It's not even sorted by the numerical values. Instead, since the size column is a filesize type, Nushell knows that 1.1 KiB (kibibytes) is larger than 812 B (bytes).
:::
Nu provides many commands that can operate on the structured output of the previous command. These are usually categorized as "Filters" in Nushell.
For example, we can use where to filter the contents of the table so that it only shows files over 10 kilobytes:
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 │
# => ╰───┴─────────────────┴──────┴───────────┴───────────────╯Of course, this isn't limited to the ls command. Nushell follows the Unix philosophy where each command does one thing well and you can typically expect the output of one command to become the input of another. This allows us to mix-and-match commands in many different combinations.
Let's look at a different command:
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 │
# => ╰───┴──────┴──────┴───────────────┴──────────┴──────┴───────────┴─────────╯
You may be familiar with the Linux/Unix ps command. It provides a list of all of the current processes running in the system along with their current status. As with ls, Nushell provides a cross-platform, built-in ps command that returns its results as structured data.
::: note
The traditional Unix ps only shows the current process and its parents by default. Nushell's implementation shows all of the processes on the system by default.
Normally, running ps in Nushell uses its internal, cross-platform command. However, it is still possible to run the external, system-dependent version on Unix/Linux platforms by prefacing it with the caret sigil. For example:
^ps aux # run the Unix ps command with all processes in user-oriented formSee Running External System Commands for more details. :::
What if we wanted to just show the processes that are actively running? As with ls above, we can also work with the table that ps outputs:
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
Remember above, where the size column from the ls command was a filesize? Here, status is really just a string, and you can use all the normal string operations and commands with it, including (as above) the == comparison.
You can examine the types for the table's columns using:
ps | describe
# => table<pid: int, ppid: int, name: string, status: string, cpu: float, mem: filesize, virtual: filesize> (stream)The describe command can be used to display the output type of any command or expression.
:::
Sometimes, a command takes an argument instead of pipeline input. For this scenario, Nushell provides the $in variable that let's you use the previous command's output in variable-form. For example:
ls
| sort-by size
| reverse
| first
| get name
| cp $in ~
::: tip Nushell Design Note
Whenever possible, Nushell commands are designed to act on pipeline input. However, some commands, like cp in this example, have two (or more)
arguments with different meanings. In this case, cp needs to know both the path to copy as well as the target path. As a result, this command is
more ergonomic with two positional parameters.
:::
::: tip Nushell commands can extend across multiple lines for readability. The above is the same as:
ls | sort-by size | reverse | first | get name | cp $in ~See Also: Multi-line Editing :::
The first three lines are the same commands we used in the second example above, so let's examine the last three:
- The
firstcommand simply returns the first value from the table. In this case, that means the file with the largest size. That's theCargo.lockfile if using the directory listing from the second example above. This "file" is arecordfrom the table which still contains itsname,type,size, andmodifiedcolumns/fields. get namereturns the value of thenamefield from the previous command, so"Cargo.lock"(a string). This is also a simple example of acell-paththat can be used to navigate and isolate structured data.- The last line uses the
$invariable to reference the output of line 5. The result is a command that says "Copy 'Cargo.lock' to the home directory"
::: tip
get and its counterpart select are two of the most used filters in Nushell, but it might not be easy to
spot the difference between them at first glance. When you're ready to start using them more extensively, see Using get and select for a guide.
:::
Nushell provides an extensive, in-shell Help system. For example
# help <command>
help ls
# Or
ls --help
# Also
help operators
help escapes::: tip Cool!
Press the F1 key to access the Help menu. Search for the ps command here, but don't press Enter just yet!
Instead, press the Down Arrow key, and notice that you are scrolling through the Examples section. Highlight an example, then press Enter and the example will be entered at the commandline, ready to run!
This can be a great way to explore and learn about the extensive set of Nushell commands. :::
The Help system also has a "search" feature:
help --find filesize
# or
help -f filesizeIt may not surprise you by now that the Help system itself is based on structured data! Notice that the output of help -f filesize is a table.
The Help for each command is stored as a record with the:
- Name
- Category
- Type (built-in, plugin, custom)
- Parameters it accepts
- Signatures showing what types of data it can accept as well as output
- And more
You can view all commands (other than externals) as a single large table using:
help commands::: tip
Notice that the params and input_output columns of the output above are nested tables. Nushell allows arbitrarily nested data structures.
:::
That help commands output is quite long. You could send it to a pager like less or bat, but Nushell includes a built-in explore command that lets you not only scroll, but also telescope-in to nested data. Try:
help commands | exploreThen press the Enter key to access the data itself. Use the arrow keys to scroll to the cp command, and over to the params column. Hit Enter again to
telescope in to the complete list of parameters available to the cp command.
::: note Pressing Esc one time returns from Scroll-mode to the View; Pressing it a second time returns to the previous view (or exits, if already at the top view level). :::
::: tip
You can, of course, use the explore command on any structured data in Nushell. This might include JSON data coming from a Web API, a spreadsheet or CSV file, YAML, or anything that can be represented as structured data in Nushell.
Try $env.config | explore for fun!
:::
`docs/nushell/nushell.github.io/book/regular_expressions.md`:
```md
# Regular Expressions
Regular expressions in Nushell's commands are handled by the `rust-lang/regex` crate. If you want to know more, check the crate documentation: "[regex](https://github.com/rust-lang/regex)".
docs/nushell/nushell.github.io/book/running_externals.md:
# Running System (External) Commands
Nu provides a set of commands that you can use across different operating systems ("internal" commands) and having this consistency is helpful when creating cross-platform code. Sometimes, though, you want to run an external command that has the same name as an internal Nu command. To run the external [`ls`](/commands/docs/ls.md) or [`date`](/commands/docs/date.md) command, for example, preface it with the caret (^) sigil. Prefacing with the caret calls the external command found in the user's `PATH` (e.g. `/bin/ls`) instead of Nu's internal [`ls`](/commands/docs/ls.md) command).
Nu internal command:
```nu
lsExternal command (typically /usr/bin/ls):
^ls::: note
On Windows, ls is a PowerShell alias by default, so ^ls will not find a matching system command.
:::
When running an external command on Windows,
Nushell forwards some CMD.EXE internal commands to cmd instead of attempting to run external commands.
Coming from CMD.EXE contains a list of these commands and describes the behavior in more detail.
`docs/nushell/nushell.github.io/book/scripts.md`:
```md
# Scripts
In Nushell, you can write and run scripts in the Nushell language. To run a script, you can pass it as an argument to the `nu` commandline application:
```nu
nu myscript.nu
This will run the script to completion in a new instance of Nu. You can also run scripts inside the current instance of Nu using source:
source myscript.nuLet's look at an example script file:
# myscript.nu
def greet [name] {
["hello" $name]
}
greet "world"A script file defines the definitions for custom commands as well as the main script itself, which will run after the custom commands are defined.
In the above example, first greet is defined by the Nushell interpreter. This allows us to later call this definition. We could have written the above as:
greet "world"
def greet [name] {
["hello" $name]
}There is no requirement that definitions have to come before the parts of the script that call the definitions, allowing you to put them where you feel comfortable.
In a script, definitions run first. This allows us to call the definitions using the calls in the script.
After the definitions run, we start at the top of the script file and run each group of commands one after another.
To better understand how Nushell sees lines of code, let's take a look at an example script:
a
b; c | dWhen this script is run, Nushell will first run the a command to completion and view its results. Next, Nushell will run b; c | d following the rules in the "Semicolons" section.
Script files can optionally contain a special "main" command. main will be run after any other Nu code, and is primarily used to allow positional parameters and flags in scripts. You can pass arguments to scripts after the script name (nu <script name> <script args>).
For example:
# myscript.nu
def main [x: int] {
$x + 10
}nu myscript.nu 100
# => 110By default, arguments provided to a script are interpreted with the type Type::Any, implying that they are not constrained to a specific data type and can be dynamically interpreted as fitting any of the available data types during script execution.
In the previous example, main [x: int] denotes that the argument x should possess an integer data type. However, if arguments are not explicitly typed, they will be parsed according to their apparent data type.
For example:
# implicit_type.nu
def main [x] {
$"Hello ($x | describe) ($x)"
}
# explicit_type.nu
def main [x: string] {
$"Hello ($x | describe) ($x)"
}nu implicit_type.nu +1
# => Hello int 1
nu explicit_type.nu +1
# => Hello string +1A script can have multiple subcommands, like run or build for example:
# myscript.nu
def "main run" [] {
print "running"
}
def "main build" [] {
print "building"
}
def main [] {
print "hello from myscript!"
}You can then execute the script's subcommands when calling it:
nu myscript.nu
# => hello from myscript!
nu myscript.nu build
# => building
nu myscript.nu run
# => runningUnlike modules, main does not need to be exported in order to be visible. In the above example, our main command is not export def, however it was still executed when running nu myscript.nu. If we had used myscript as a module by running use myscript.nu, rather than running myscript.nu as a script, trying to execute the myscript command would not work since myscript is not exported.
It is important to note that you must define a main command in order for subcommands of main to be correctly exposed. For example, if we had just defined the run and build subcommands, they wouldn't be accessible when running the script:
# myscript.nu
def "main run" [] {
print "running"
}
def "main build" [] {
print "building"
}nu myscript.nu build
nu myscript.nu runThis is a limitation of the way scripts are currently processed. If your script only has subcommands, you can add an empty main to expose the subcommands, like so:
def main [] {}On Linux and macOS you can optionally use a shebang to tell the OS that a file should be interpreted by Nu. For example, with the following in a file named myscript:
#!/usr/bin/env nu
"Hello World!"./myscript
# => Hello World!For script to have access to standard input, nu should be invoked with --stdin flag:
#!/usr/bin/env -S nu --stdin
def main [] {
echo $"stdin: ($in)"
}echo "Hello World!" | ./myscript
# => stdin: Hello World!
`docs/nushell/nushell.github.io/book/sorting.md`:
```md
# Sorting
Nushell offers many ways of sorting data, and which method you reach for will depend on the problem and what kind of data you're working with. Let's take a look at some of the ways you might wish to sort data.
## Basic sorting
### Lists
Sorting a basic list works exactly how you might expect:
```nu
[9 3 8 1 4 6] | sort
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 3 │
# => │ 2 │ 4 │
# => │ 3 │ 6 │
# => │ 4 │ 8 │
# => │ 5 │ 9 │
# => ╰───┴───╯
However, things get a bit more complex when you start combining types. For example, let's see what happens when we have a list with numbers and strings:
["hello" 4 9 2 1 "foobar" 8 6] | sort
# => ╭───┬────────╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 4 │
# => │ 3 │ 6 │
# => │ 4 │ 8 │
# => │ 5 │ 9 │
# => │ 6 │ foobar │
# => │ 7 │ hello │
# => ╰───┴────────╯We can see that the numbers are sorted in order, and the strings are sorted to the end of the list, also in order. If you are coming from other programming languages, this may not be quite what you expect. In Nushell, as a general rule, data can always be sorted without erroring.
::: tip If you do want a sort containing differing types to error, see strict sort. :::
Nushell's sort is also stable, meaning equal values will retain their original ordering relative to each other. This is illustrated here using the case insensitive sort option:
["foo" "FOO" "BAR" "bar"] | sort -i
# => ╭───┬─────╮
# => │ 0 │ BAR │
# => │ 1 │ bar │
# => │ 2 │ foo │
# => │ 3 │ FOO │
# => ╰───┴─────╯Since this sort is case insensitive, foo and FOO are considered equal to each other, and the same is true for bar and BAR. In the result, the uppercase BAR precedes the lowercase bar, since the uppercase BAR also precedes the lowercase bar in the input. Similarly, the lowercase foo precedes the uppercase FOO in both the input and the result.
Records can be sorted two ways: by key, and by value. By default, passing a record to sort will sort in order of its keys:
{x: 123, a: hello!, foo: bar} | sort
# => ╭─────┬────────╮
# => │ a │ hello! │
# => │ foo │ bar │
# => │ x │ 123 │
# => ╰─────┴────────╯To instead sort in order of values, use the -v flag:
{x: 123, a: hello! foo: bar} | sort -v
# => ╭─────┬────────╮
# => │ x │ 123 │
# => │ foo │ bar │
# => │ a │ hello! │
# => ╰─────┴────────╯Table rows are sorted by comparing rows by the columns in order. If two rows have equal values in their first column, they are sorted by their second column. This repeats until the rows are sorted different or all columns are equal.
let items = [
{id: 100, quantity: 10, price: 5 }
{id: 100, quantity: 5, price: 8 }
{id: 100, quantity: 5, price: 1 }
]
$items | sort
# => ╭───┬─────┬──────────┬───────╮
# => │ # │ id │ quantity │ price │
# => ├───┼─────┼──────────┼───────┤
# => │ 0 │ 100 │ 5 │ 1 │
# => │ 1 │ 100 │ 5 │ 8 │
# => │ 2 │ 100 │ 10 │ 5 │
# => ╰───┴─────┴──────────┴───────╯In this example, the id column for all items is equal. Then, the two items with quantity 5 are sorted before the item with quantity 10. Finally, the item with price 1 is sorted before the item with price 8.
In order to sort more complex types, such as tables, you can use the sort-by command. sort-by can order its input by a cell path.
Here's an example directory, sorted by filesize:
ls | sort-by size
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯We can also provide multiple cell paths to sort-by, which will sort by each cell path in order of priority. You can think of providing multiple cell paths as a "tiebreaker" for elements which have equal values. Let's sort first by size, then by modification time:
ls | sort-by size modified
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 1 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯This time, shopping_list.txt comes before my-secret-plans.txt, since it has an earlier modification time, but two larger files remain sorted after the .txt files.
Furthermore, we can use more complex cell paths to sort nested data:
let cities = [
{name: 'New York', info: { established: 1624, population: 18_819_000 } }
{name: 'Kyoto', info: { established: 794, population: 37_468_000 } }
{name: 'São Paulo', info: { established: 1554, population: 21_650_000 } }
]
$cities | sort-by info.established
# => ╭───┬───────────┬────────────────────────────╮
# => │ # │ name │ info │
# => ├───┼───────────┼────────────────────────────┤
# => │ 0 │ Kyoto │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 794 │ │
# => │ │ │ │ population │ 37468000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => │ 1 │ São Paulo │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 1554 │ │
# => │ │ │ │ population │ 21650000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => │ 2 │ New York │ ╭─────────────┬──────────╮ │
# => │ │ │ │ established │ 1624 │ │
# => │ │ │ │ population │ 18819000 │ │
# => │ │ │ ╰─────────────┴──────────╯ │
# => ╰───┴───────────┴────────────────────────────╯Sometimes, it's useful to sort data in a more complicated manner than "increasing" or "decreasing". Instead of using sort-by with a cell path, you can supply a closure, which will transform each value into a sorting key without changing the underlying data. Here's an example of a key closure, where we want to sort a list of assignments by their average grade:
let assignments = [
{name: 'Homework 1', grades: [97 89 86 92 89] }
{name: 'Homework 2', grades: [91 100 60 82 91] }
{name: 'Exam 1', grades: [78 88 78 53 90] }
{name: 'Project', grades: [92 81 82 84 83] }
]
$assignments | sort-by { get grades | math avg }
# => ╭───┬────────────┬───────────────────────╮
# => │ # │ name │ grades │
# => ├───┼────────────┼───────────────────────┤
# => │ 0 │ Exam 1 │ [78, 88, 78, 53, 90] │
# => │ 1 │ Project │ [92, 81, 82, 84, 83] │
# => │ 2 │ Homework 2 │ [91, 100, 60, 82, 91] │
# => │ 3 │ Homework 1 │ [97, 89, 86, 92, 89] │
# => ╰───┴────────────┴───────────────────────╯The value is passed into the pipeline input of the key closure, however, you can also use it as a parameter:
let weight = {alpha: 10, beta: 5, gamma: 3}
[alpha gamma beta gamma alpha] | sort-by {|val| $weight | get $val }
# => ╭───┬───────╮
# => │ 0 │ gamma │
# => │ 1 │ gamma │
# => │ 2 │ beta │
# => │ 3 │ alpha │
# => │ 4 │ alpha │
# => ╰───┴───────╯In addition to key closures, sort-by also supports closures which specify a custom sort order. The --custom, or -c, flag will tell sort-by to interpret closures as custom sort closures. A custom sort closure has two parameters, and returns a boolean. The closure should return true if the first parameter comes before the second parameter in the sort order.
For a simple example, we could rewrite a cell path sort as a custom sort. This can be read as "If $a.size is less than $b.size, a should appear before b in the sort order":
ls | sort-by -c {|a, b| $a.size < $b.size }
# => ╭───┬─────────────────────┬──────┬──────────┬────────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├───┼─────────────────────┼──────┼──────────┼────────────────┤
# => │ 0 │ my-secret-plans.txt │ file │ 100 B │ 10 minutes ago │
# => │ 1 │ shopping_list.txt │ file │ 100 B │ 2 months ago │
# => │ 2 │ myscript.nu │ file │ 1.1 KiB │ 2 weeks ago │
# => │ 3 │ bigfile.img │ file │ 10.0 MiB │ 3 weeks ago │
# => ╰───┴─────────────────────┴──────┴──────────┴────────────────╯::: tip The parameters are also passed to the custom closure as a two element list, so the following are equivalent:
{|a, b| $a < $b }{ $in.0 < $in.1 }
:::
Here's an example of a custom sort which couldn't be trivially written as a key sort. In this example, we have a queue of tasks with some amount of work time and a priority. We want to sort by priority (highest first). If a task has had zero work time, we want to schedule it immediately; otherwise, we ignore the work time.
let queue = [
{task: 139, work_time: 0, priority: 1 }
{task: 52, work_time: 355, priority: 8 }
{task: 948, work_time: 72, priority: 2 }
{task: 583, work_time: 0, priority: 5 }
]
let my_sort = {|a, b|
match [$a.work_time, $b.work_time] {
[0, 0] => ($a.priority > $b.priority) # fall back to priority if equal work time
[0, _] => true, # only a has 0 work time, so a comes before b in the sort order
[_, 0] => false, # only b has 0 work time, so a comes after b in the sort order
_ => ($a.priority > $b.priority) # both have non-zero work time, sort by priority
}
}
$queue | sort-by -c $my_sortWhen using case insensitive sort, strings (and globs) which are the same except for different casing will be considered equal for sorting, while other types remain unaffected:
let data = [
Nushell,
foobar,
10,
nushell,
FoOBaR,
9
]
$data | sort -i
# => ╭───┬─────────╮
# => │ 0 │ 9 │
# => │ 1 │ 10 │
# => │ 2 │ foobar │
# => │ 3 │ FoOBaR │
# => │ 4 │ Nushell │
# => │ 5 │ nushell │
# => ╰───┴─────────╯The natural sort option allows strings which contain numbers to be sorted in the same way that numbers are normally sorted. This works both for strings which consist solely of numbers, and strings which have numbers and letters:
let data = ["10", "9", "foo123", "foo20", "bar123", "bar20"]
$data | sort
# => ╭───┬────────╮
# => │ 0 │ 10 │
# => │ 1 │ 9 │
# => │ 2 │ bar123 │
# => │ 3 │ bar20 │
# => │ 4 │ foo123 │
# => │ 5 │ foo20 │
# => ╰───┴────────╯
# "1" is sorted before "9", so "10" is sorted before "9"
$data | sort -n
# => ╭───┬────────╮
# => │ 0 │ 9 │
# => │ 1 │ 10 │
# => │ 2 │ bar20 │
# => │ 3 │ bar123 │
# => │ 4 │ foo20 │
# => │ 5 │ foo123 │
# => ╰───┴────────╯Furthermore, natural sort allows you to sort numbers together with numeric strings:
let data = [4, "6.2", 1, "10", 2, 8.1, "3", 5.5, "9", 7]
$data | sort -n
# => ╭───┬──────╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => │ 4 │ 5.50 │
# => │ 5 │ 6.2 │
# => │ 6 │ 7 │
# => │ 7 │ 8.10 │
# => │ 8 │ 9 │
# => │ 9 │ 10 │
# => ╰───┴──────╯Under some circumstances, you might need to sort data containing mixed types. There are a couple things to be aware of when sorting mixed types:
- Generally, values of the same type will appear next to each other in the sort order. For example, sorted numbers come first, then sorted strings, then sorted lists.
- Some types will be intermixed in the sort order. These are:
- Integers and floats. For example,
[2.2, 1, 3]will be sorted as[1, 2.2, 3]. - Strings and globs. For example,
[("b" | into glob) a c]will be sorted as[a b c](where b is still a glob). - If using natural sort, integers, floats, and strings will be intermixed as described in that section.
- Integers and floats. For example,
- The ordering between non-intermixed types is not guaranteed, except for
nullvalues, which will always be sorted to the end of a list.- Within the same Nushell version the ordering should always be the same, but this should not be relied upon. If you have code which is sensitive to the ordering across types, consider using a custom sort which better expresses your requirements.
If you need to sort data which may contain mixed types, consider one of the following strategies:
- Strict sort to disallow sorting of incompatible types
- Natural sort to sort intermixed numbers and numeric strings
- A key sort using
to text,to nuon, orto json, as appropriate - A custom sort using
describeto explicitly check types
Custom sort closures also provide a simple way to sort data while ensuring only types with well-defined comparisons are sorted together. This takes advantage of operators requiring compatible data types:
let compatible = [8 3.2 null 58 2]
let incompatible = ["hello" 4 9 2 1 "meow" 8 6]
$compatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => ╭───┬──────╮
# => │ 0 │ 2 │
# => │ 1 │ 3.20 │
# => │ 2 │ 8 │
# => │ 3 │ 58 │
# => │ 4 │ │
# => ╰───┴──────╯
$incompatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => Error: nu::shell::type_mismatch
# =>
# => × Type mismatch during operation.
# => ╭─[entry #26:1:36]
# => 1 │ $incompatible | sort-by -c {|a, b| $a < $b | default ($a != null) }
# => · ─┬ ┬ ─┬
# => · │ │ ╰── string
# => · │ ╰── type mismatch for operator
# => · ╰── int
# => ╰────Special handling is required for null values, since comparison between any value and null returns null. To instead reject null values, try the following:
let baddata = [8 3.2 null 58 2]
let strict = {|a, b|
match [$a, $b] {
[null, _] => (error make {msg: "Attempt to sort null"}),
[_, null] => (error make {msg: "Attempt to sort null"}),
_ => ($a < $b)
}
}
$baddata | sort-by -c $strict
# => Error: × Attempt to sort null
# => ╭─[entry #3:4:21]
# => 3 │ match [$a, $b] {
# => 4 │ [null, _] => (error make {msg: "Attempt to sort null"}),
# => · ─────┬────
# => · ╰── originates from here
# => 5 │ [_, null] => (error make {msg: "Attempt to sort null"}),
# => ╰────
`docs/nushell/nushell.github.io/book/special_variables.md`:
```md
---
next:
text: Programming in Nu
link: /book/programming_in_nu.md
---
# Special Variables
Nushell makes available and uses a number of special variables and constants. Many of these are mentioned or documented in other places in this Book, but this page
should include _all_ variables for reference.
[[toc]]
## `$nu`
The `$nu` constant is a record containing several useful values:
- `default-config-dir`: The directory where the configuration files are stored and read.
- `config-path`: The path of the main Nushell config file, normally `config.nu` in the config directory.
- `env-path`: The optional environment config file, normally `env.nu` in the config directory.
- `history-path`: The text or SQLite file storing the command history.
- `loginshell-path`: The optional config file which runs for login shells, normally `login.nu` in the config directory.
- `plugin-path`: The plugin registry file, normally `plugin.msgpackz` in the config directory.
- `home-path`: The user's home directory which can be accessed using the shorthand `~`.
- `data-dir`: The data directory for Nushell, which includes the `./vendor/autoload` directories loaded at startup and other user data.
- `cache-dir`: A directory for non-essential (cached) data.
- `vendor-autoload-dirs`: A list of directories where third-party applications should install configuration files that will be auto-loaded during startup.
- `user-autoload-dirs`: A list of directories where the user may create additional configuration files which will be auto-loaded during startup.
- `temp-path`: A path for temporary files that should be writeable by the user.
- `pid`: The PID of the currently running Nushell process.
- `os-info`: Information about the host operating system.
- `startup-time`: The amount of time (in duration) that it took for Nushell to start and process all configuration files.
- `is-interactive`: A boolean indicating whether Nushell was started as an interactive shell (`true`) or is running a script or command-string. For example:
```nu
$nu.is-interactive
# => true
nu -c "$nu.is-interactive"
# => false
# Force interactive with --interactive (-i)
nu -i -c "$nu.is-interactive"
# => true
Note: When started as an interactive shell, startup config files are processed. When started as a non-interactive shell, no config files are read unless explicitly called via flag.
is-login: Indicates whether or not Nushell was started as a login shell.history-enabled: History may be disabled vianu --no-history, in which case this constant will befalse.current-exe: The full path to the currently-runningnubinary. Can be combined withpath dirname(which is constant) to determine the directory where the binary is located.
$env is a special mutable variable containing the current environment variables. As with any process, the initial environment is inherited from the parent process which started nu.
There are also several environment variables that Nushell uses for specific purposes:
The amount of time in milliseconds that the previous command took to run.
$env.config is the main configuration record used in Nushell. Settings are documented in config nu --doc.
Inside a script, module, or sourced-file, this variable holds the fully-qualified filename. Note that this
information is also available as a constant through the path self command.
Allows users to specify how to convert certain environment variables to Nushell types. See ENV_CONVERSIONS.
Inside a script, module, or sourced-file, this variable holds the fully qualified name of the directory in which the file resides. Note that this value is also available as a constant through:
path self | path dirnameThe exit code of the last command, usually used for external commands — Equivalent to $? from POSIX. Note that this information is also made available to the catch block in a try expression for external commands. For instance:
^ls file-that-does-not-exist e> /dev/null
$env.LAST_EXIT_CODE
# => 2
# or
try {
^ls file-that-does-not-exist e> /dev/null
} catch {|e|
print $e.exit_code
}
# => 2A list of directories which will be searched when using the source, use, or overlay use commands. See also:
- The
$NU_LIB_DIRSconstant below - Module Path
- Configuration -
$NU_LIB_DIRS
The standard library offers logging in std/log. The NU_LOG_LEVEL environment variable is used to define the log level being used for custom commands, modules, and scripts.
nu -c '1 | print; use std/log; log debug 1111; 9 | print'
# => 1
# => 9
nu -c '1 | print; use std/log; NU_LOG_LEVEL=debug log debug 1111; 9 | print'
# => 1
# => 2025-07-12T21:27:30.080|DBG|1111
# => 9
nu -c '1 | print; use std/log; $env.NU_LOG_LEVEL = "debug"; log debug 1111; 9 | print'
# => 1
# => 2025-07-12T21:27:57.888|DBG|1111
# => 9Note that $env.NU_LOG_LEVEL is different from nu --log-level, which sets the log level for built-in native Rust Nushell commands. It does not influence the std/log logging used in custom commands and scripts.
nu --log-level 'debug' -c '1 | print; use std/log; log debug 1111; 9 | print'
# => … a lot more log messages, with references to the Nushell command Rust source files
# and without our own `log debug` message
# => 1
# => 9
# => …A list of directories which will be searched when registering plugins with plugin add. See also:
The current Nushell version. The same as (version).version, but, as an environment variable, it is exported to and can be read by child processes.
The search path for executing other applications. It is initially inherited from the parent process as a string, but converted to a Nushell list at startup for easy access.
It is converted back to a string before running a child-process.
When executing a script, this variable represents the name and relative path of the script. Unlike the two variables above, it is not present when sourcing a file or importing a module.
Note: Also unlike the two variables above, the exact path (including symlinks) that was used to invoke the file is returned.
A number of variables are available for configuring the Nushell prompt that appears on each commandline. See also:
- Configuration - Prompt Configuration
config nu --doc
SHLVL is incremented by most shells when entering a new subshell. It can be used to determine the number of nested shells. For instance,
if $env.SHLVL == 2 then typing exit should return you to a parent shell.
Can be used to optionally override the $nu.default-config-dir location. See Configuration - Startup Variables.
Can be used to optionally override the $nu.data-dir location. See Configuration - Startup Variables.
The $in variable represents the pipeline input into an expression. See Pipelines - The Special $in Variable.
$it is a special variable that is only available in a where "row condition" — a convenient shorthand which simplifies field access. See help where or where for more information.
A constant version of $env.NU_LIB_DIRS - a list of directories which will be searched when using the source, use, or overlay use commands. See also:
A constant version of $env.NU_PLUGIN_DIRS - a list of directories which will be searched when registering plugins with plugin add. See also:
`docs/nushell/nushell.github.io/book/standard_library.md`:
```md
---
prev:
text: (Not so) Advanced
link: /book/advanced.md
---
# Standard Library (Preview)
Nushell ships with a standard library of useful commands written in native Nu. By default, the standard library is loaded into memory (but not automatically imported) when Nushell starts.
[[toc]]
## Overview
The standard library currently includes:
- Assertions
- An alternative `help` system with support for completions.
- Additional JSON variant formats
- XML Access
- Logging
- And more
To see a complete list of the commands available in the standard library, run the following:
```nu
nu -c "
use std
scope commands
| where name =~ '^std '
| select name description extra_description
| wrap 'Standard Library Commands'
| table -e
"
::: note
The use std command above loads the entire standard library so that you can see all of the commands at once. This is typically not how it will be used (more info below). It is also run in a separate Nu subshell simply so that it is not loaded into scope in the shell you are using.
:::
The Standard Library modules and submodules are imported with the use command, just as any other module. See Using Modules for more information.
While working at the commandline, it can be convenient to load the entire standard library using:
use std *However, this form should be avoided in custom commands and scripts since it has the longest load time.
::: important Optimal Startup when Using the Standard Library See the notes below on how to ensure that your configuration isn't loading the entire Standard Library. :::
Each submodule of the standard library can be loaded separately. Again, for best performance, load only the submodule(s) that you need in your code.
See Importing Modules for general information on using modules. The recommended import for each of the Standard Library submodules is listed below:
These submodules are normally imported with use std/<submodule> (without a glob/*):
use std/assert:assertand its subcommandsuse std/bench: The benchmarking commandbenchuse std/dirs: The directory stack commanddirsand its subcommandsuse std/input: Theinput displaycommanduse std/help: An alternative version of thehelpcommand and its subcommands which supports completion and other featuresuse std/iters: Additionaliters-prefixed iteration commands.use std/log: Thelog <subcommands>such aslog warning <msg>use std/math: Mathematical constants such as$math.E. These can also be imported as definitions as in Form #2 below.
Some submodules are easier to use when their definitions (commands, aliases, constants, etc.) are loaded into the current scope. For instance:
use std/formats *
ls | to jsonlSubmodules that are normally imported with use std/<submodule> * (with a glob/*):
use std/dt *: Additional commands for working withdatevaluesuse std/formats *: Additionaltoandfromformat conversionsuse std/math *: The math constants without a prefix, such as$E. Note that the prefixed form #1 above is likely more understandable when reading and maintaining code.use std/xml *: Additional commands for working with XML data
It is possible to import Standard Library submodules using a space-separated form:
use std log
use std formats *::: important
As mentioned in Using Modules, this form (like use std *) first loads the entire Standard Library into scope and then imports the submodules. In contrast, the slash-separated versions in #1 and #2 above only import the submodule and will be much faster as a result.
:::
std-rfc, found in the nushell Repository, serves as a staging ground for possible Standard Library additions.
If you are interested in adding to the Standard Library, please submit your code via PR to the std-rfc module in that repository. We also encourage you to install this module and provide feedback on upcoming candidate commands.
::: details More details
Candidate commands for the Standard Library should, in general:
- Have broad appeal - Be useful to a large number of users or use cases
- Be well-written and clearly commented for future maintainers
- Implement help comments with example usage
- Have a description that explains why you feel the command should be a part of the standard library. Think of this as an "advertisement" of sorts to convince people to try the command and provide feedback so that it can be promoted in the future.
In order for a command to be graduated from RFC to the Standard Library, it must have:
- Positive feedback
- Few (or no) outstanding issues and, of course, no significant issues
- A PR author for the
stdsubmission. This does not necessarily have to be the original author of the command. - Test cases as part of the
stdsubmission PR
Ultimately a member of the core team will decide when and if to merge the command into std based on these criteria.
Of course, if a candidate command in std-rfc no longer works or has too many issues, it may be removed from or disabled in std-rfc.
:::
To disable the standard library, you can start Nushell using:
nu --no-std-libThis can be especially useful to minimize overhead when running a command in a subshell using nu -c. For example:
nu --no-std-lib -n -c "$nu.startup-time"
# => 1ms 125µs 10ns
nu -n -c "$nu.startup-time"
# => 4ms 889µs 576nsYou will not be able to import the library, any of its submodules, nor use any of its commands, when it is disabled in this way.
::: warning Important!
std/log exports environment variables. To use the std/log module in your own module, please see this caveat in the "Creating Modules" Chapter.
:::
If Nushell's startup time is important to your workflow, review your startup configuration in config.nu, env.nu, and potentially others for inefficient use of the standard library. The following command should identify any problem areas:
view files
| enumerate | flatten
| where filename !~ '^std'
| where filename !~ '^entry'
| where {|file|
(view span $file.start $file.end) =~ 'use\W+std[^\/]'
}Edit those files to use the recommended syntax in the Importing Submodules section above.
::: note
If a Nushell library (e.g., from the nu_scripts repository), example, or doc is using this syntax, please report it via an issue or PR. These will be updated over time after Nushell 0.99.0 is released.
If a third-party module is using this syntax, please report it to the author/maintainers to update. :::
`docs/nushell/nushell.github.io/book/stdout_stderr_exit_codes.md`:
```md
# Stdout, Stderr, and Exit Codes
An important piece of interop between Nushell and external commands is working with the standard streams of data coming from the external.
The first of these important streams is stdout.
## Stdout
Stdout is the way that most external apps will send data into the pipeline or to the screen. Data sent by an external app to its stdout is received by Nushell by default if it's part of a pipeline:
```nu
external | str join
The above would call the external named external and would redirect the stdout output stream into the pipeline. With this redirection, Nushell can then pass the data to the next command in the pipeline, here str join.
Without the pipeline, Nushell will not do any redirection, allowing it to print directly to the screen.
Another common stream that external applications often use to print error messages is stderr. By default, Nushell does not do any redirection of stderr, which means that by default it will print to the screen.
But you can do pass stderr to a command or a file if you want to:
- use
e>|to pass stderr to next command. - use
e> fileto redirect stderr to a file. - use
do -i { cmd } | completeto capture stderr message.
Finally, external commands have an "exit code". These codes help give a hint to the caller whether the command ran successfully.
Nushell tracks the last exit code of the recently completed external in one of two ways. The first way is with the LAST_EXIT_CODE environment variable.
do { external }
$env.LAST_EXIT_CODEThe second way is to use the complete command.
Using the complete command
The complete command allows you to run an external to completion, and gather the stdout, stderr, and exit code together in one record.
If we try to run the external cat on a file that doesn't exist, we can see what complete does with the streams, including the redirected stderr:
cat unknown.txt | complete
# => ╭───────────┬─────────────────────────────────────────────╮
# => │ stdout │ │
# => │ stderr │ cat: unknown.txt: No such file or directory │
# => │ exit_code │ 1 │
# => ╰───────────┴─────────────────────────────────────────────╯The echo command is mainly for pipes. It returns its arguments, ignoring the piped-in value. There is usually little reason to use this over just writing the values as-is.
In contrast, the print command prints the given values to stdout as plain text. It can be used to write to standard error output, as well. Unlike echo, this command does not return any value (print | describe will return "nothing"). Since this command has no output, there is no point in piping it with other commands.
The standard library has commands to write out messages in different logging levels. For example:
@code
The log level for output can be set with the NU_LOG_LEVEL environment variable:
NU_LOG_LEVEL=DEBUG nu std_log.nuIf you want to redirect stdout of an external command to a file, you can use out> followed by a file path. Similarly, you can use err> to redirect stderr:
cat unknown.txt out> out.log err> err.logIf you want to redirect both stdout and stderr to the same file, you can use out+err>:
cat unknown.txt out+err> log.logNote that out can be shortened to just o, and err can be shortened to just e. So, the following examples are equivalent to the previous ones above:
cat unknown.txt o> out.log e> err.log
cat unknown.txt o+e> log.logAlso, any expression can be used for the file path, as long as it is a string value:
use std
cat unknown.txt o+e> (std null-device)Note that file redirections are scoped to an expression and apply to all external commands in the expression. In the example below, out.txt will contain hello\nworld:
let text = "hello\nworld"
($text | head -n 1; $text | tail -n 1) o> out.txtPipes and additional file redirections inside the expression will override any file redirections applied from the outside.
If a regular pipe | comes after an external command, it redirects the stdout of the external command as input to the next command. To instead redirect the stderr of the external command, you can use the stderr pipe, err>| or e>|:
cat unknown.txt e>| str upcaseOf course, there is a corresponding pipe for combined stdout and stderr, out+err>| or o+e>|:
nu -c 'print output; print -e error' o+e>| str upcaseUnlike file redirections, pipe redirections do not apply to all commands inside an expression. Rather, only the last command in the expression is affected. For example, only cmd2 in the snippet below will have its stdout and stderr redirected by the pipe.
(cmd1; cmd2) o+e>| cmd3Both stdout and stderr are represented as "raw streams" inside of Nushell. These are streams of bytes rather than the structured data used by internal Nushell commands.
Because streams of bytes can be difficult to work with, especially given how common it is to use output as if it was text data, Nushell attempts to convert raw streams into text data. This allows other commands to pull on the output of external commands and receive strings they can further process.
Nushell attempts to convert to text using UTF-8. If at any time the conversion fails, the rest of the stream is assumed to always be bytes.
If you want more control over the decoding of the byte stream, you can use the decode command. The decode command can be inserted into the pipeline after the external, or other raw stream-creating command, and will handle decoding the bytes based on the argument you give decode. For example, you could decode shift-jis text this way:
0x[8a 4c] | decode shift-jis
# => 貝
`docs/nushell/nushell.github.io/book/style_guide.md`:
```md
---
next:
text: Nu as a Shell
link: /book/nu_as_a_shell.md
---
# Best Practices
This page is a working document collecting syntax guidelines and best practices we have discovered so far.
The goal of this document is to eventually land on a canonical Nushell code style, but as for now it is still work in
progress and subject to change. We welcome discussion and contributions.
Keep in mind that these guidelines are not required to be used in external repositories (not ours), you can change them in the
way you want, but please be consistent and follow your rules.
All escape sequences should not be interpreted literally, unless directed to do so. In other words,
treat something like `\n` like the new line character and not a literal slash followed by `n`.
## Formatting
### Defaults
**It's recommended to** assume that by default no spaces or tabs allowed, but the following rules define where they are allowed.
### Basic
- **It's recommended to** put one space before and after pipe `|` symbol, commands, subcommands, their options and arguments.
- **It's recommended to** never put several consecutive spaces unless they are part of string.
- **It's recommended to** omit commas between list items.
Correct:
```nu
'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'
Incorrect:
# - too many spaces after "|": 2 instead of 1
'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'One-line format is a format for writing all commands in one line.
It's recommended to default to this format:
- unless you are writing scripts
- in scripts for lists and records unless they either:
- more than 80 characters long
- contain nested lists or records
- for pipelines less than 80 characters long not containing items should be formatted with a long format
Rules:
- parameters:
- It's recommended to put one space after comma
,after block or closure parameter. - It's recommended to put one space after pipe
|symbol denoting block or closure parameter list end.
- It's recommended to put one space after comma
- block and closure bodies:
- It's recommended to put one space after opening block or closure curly brace
{if no explicit parameters defined. - It's recommended to put one space before closing block or closure curly brace
}.
- It's recommended to put one space after opening block or closure curly brace
- records:
- It's recommended to put one space after
:after record key. - It's recommended to put one space after comma
,after key value.
- It's recommended to put one space after
- lists:
- It's recommended to put one space after comma
,after list value.
- It's recommended to put one space after comma
- surrounding constructs:
- It's recommended to put one space before opening square
[, curly brace{, or parenthesis(if preceding symbol is not the same. - It's recommended to put one space after closing square
], curly brace}, or parenthesis)if following symbol is not the same.
- It's recommended to put one space before opening square
Correct:
[[status]; [UP] [UP]] | all {|el| $el.status == UP }
[1 2 3 4] | reduce {|elt, acc| $elt + $acc }
[1 2 3 4] | reduce {|elt acc| $elt + $acc }
{x: 1, y: 2}
{x: 1 y: 2}
[1 2] | zip [3 4]
[]
(1 + 2) * 3Incorrect:
# too many spaces before "|el|": no space is allowed
[[status]; [UP] [UP]] | all { |el| $el.status == UP }
# too many spaces before ",": no space is allowed
[1 2 3 4] | reduce {|elt , acc| $elt + $acc }
# too many spaces before "x": no space is allowed
{ x: 1, y: 2}
# too many spaces before "[3": one space is required
[1 2] | zip [3 4]
# too many spaces before "]": no space is allowed
[ ]
# too many spaces before ")": no space is allowed
(1 + 2 ) * 3Multi-line format is a format for writing all commands in several lines. It inherits all rules from one-line format and modifies them slightly.
It's recommended to default to this format:
- while you are writing scripts
- in scripts for lists and records while they either:
- more than 80 characters long
- contain nested lists or records
- for pipelines more 80 characters long
Rules:
- general:
- It's required to omit trailing spaces.
- block and closure bodies:
- It's recommended to put each body pipeline on a separate line.
- records:
- It's recommended to put each record key-value pair on separate line.
- lists:
- It's recommended to put each list item on separate line.
- surrounding constructs:
- It's recommended to put one
\nbefore opening square[, curly brace{, or parenthesis(if preceding symbol is not the and applying this rule produce line with a singular parenthesis. - It's recommended to put one
\nafter closing square], curly brace}, or parenthesis)if following symbol is not the same and applying this rule produce line with a singular parenthesis.
- It's recommended to put one
Correct:
[[status]; [UP] [UP]] | all {|el|
$el.status == UP
}
[1 2 3 4] | reduce {|elt, acc|
$elt + $acc
}
{x: 1, y: 2}
[
{name: "Teresa", age: 24},
{name: "Thomas", age: 26}
]
let selectedProfile = (for it in ($credentials | transpose name credentials) {
echo $it.name
})Incorrect:
# too many spaces before "|el|": no space is allowed (like in one-line format)
[[status]; [UP] [UP]] | all { |el|
# too few "\n" before "}": one "\n" is required
$el.status == UP}
# too many spaces before "2": one space is required (like in one-line format)
[1 2 3 4] | reduce {|elt, acc|
$elt + $acc
}
{
# too many "\n" before "x": one-line format required as no nested lists or record exist
x: 1,
y: 2
}
# too few "\n" before "{": multi-line format required as there are two nested records
[{name: "Teresa", age: 24},
{name: "Thomas", age: 26}]
let selectedProfile = (
# too many "\n" before "foo": no "\n" is allowed
for it in ($credentials | transpose name credentials) {
echo $it.name
})It's recommended to use full concise words over abbreviations and acronyms, unless they are well-known and/or commonly used.
Correct:
query-user --id 123
$user.name | str downcaseIncorrect:
qry-usr --id 123
$user.name | string downcaseIt's recommended to use kebab-case for command names with multiple words.
Correct:
fetch-user --id 123Incorrect:
fetch_user --id 123
fetchUser --id 123See also Naming Commands.
Sub commands are commands that are logically grouped under a parent command and separated by a space. It's recommended to use kebab-case for the sub-command name.
Correct:
date now
date list-timezone
def "login basic-auth" [username: string password: string] {
# ...
}See also Naming Sub-Commands.
It's recommended to use kebab-case for flag names.
Correct:
def greet [name: string, --all-caps] {
# ...
}Incorrect:
def greet [name: string, --all_caps] {
# ...
}::: tip Notice that the name used to access the flag is accessed by replacing the dash with an underscore in the resulting variable name.
See Flags. :::
It's recommended to use snake_case for variable names, including command parameters.
Correct:
let user_id = 123
def fetch-user [user_id: int] {
# ...
}Incorrect:
let user-id = 123
let userId = 123
def fetch-user [user-id: int] {
# ...
}It's recommended to use SCREAMING_SNAKE_CASE for environment variable names.
Correct:
$env.ENVIRONMENT_CODE = "prod"
$env.APP_VERSION = "1.0.0"Incorrect:
$env.ENVIRONMENT-CODE = "prod"
$env.app_version = "1.0.0"- It's recommended to keep count of all positional parameters less than or equal to 2, for remaining inputs use options. Assume that command can expect source and destination parameter, like
mv: source and target file or directory. - It's recommended to use positional parameters unless they can't be used due to rules listed here or technical restrictions.
For instance, when there are several kinds of optional parameters (but at least one parameter should be provided)
use options. Great example of this is
ansi gradientcommand where at least foreground or background must be passed. - It's recommended to provide both long and short options.
- It's recommended to provide documentation for all exported entities (like custom commands) and their inputs (like custom command parameters and options).
`docs/nushell/nushell.github.io/book/table_of_contents.md`:
```md
# Table of Contents
- [Installation](installation.md) - Installing Nushell
- [Introduction](README.md) - Getting started
- [Thinking in Nu](thinking_in_nu.md) - Thinking in Nushell
- [Moving around](moving_around.md) - Moving around in Nushell
- [Types of data](types_of_data.md) - Types of data in Nushell
- [Loading data](loading_data.md) - Loading data and using it
- [Strings](working_with_strings.md) - Strings, escape characters, and string interpolation
- [Working with lists](working_with_lists.md) - Working with Nu lists
- [Working with tables](working_with_tables.md) - Working with Nu tables
- [Pipelines](pipelines.md) - How the pipeline works
- [Configuration](configuration.md) - How to configure Nushell
- [3rd Party Prompts](3rdpartyprompts.md) - How to configure 3rd party prompts
- [Custom commands](custom_commands.md) - Creating your own commands
- [Aliases](aliases.md) - How to alias commands
- [Operators](operators.md) - Operators supported by Nushell
- [Variables](variables.md) - Working with variables
- [Control flow](control_flow.md) - Working with the control flow commands
- [Environment](environment.md) - Working with environment variables
- [Stdout, stderr, and exit codes](stdout_stderr_exit_codes.md) - Working with stdout, stderr, and exit codes
- [Modules](modules.md) - Creating and using your own modules
- [Hooks](hooks.md) - Adding code snippets to be run automatically
- [Scripts](scripts.md) - Creating your own scripts
- [Metadata](metadata.md) - An explanation of Nu's metadata system
- [Creating your own errors](creating_errors.md) - Creating your own error messages
- [Directory Stack](directory_stack.md) - Working with multiple locations
- [Running External (System) Commands](./running_externals.md) - Running external commands with a naming conflict
- [Plugins](plugins.md) - Enhancing Nushell with more features using plugins
- [Parallelism](parallelism.md) - Running your code in parallel
- [Line editor](line_editor.md) - Nushell's line editor
- [Dataframes](dataframes.md) - Working with dataframes in Nushell
- [Explore](explore.md) - Using the Nushell TUI
- [Coloring and Theming](coloring_and_theming.md) - How to change the colors and themes in Nushell
- [Regular Expressions](regular_expressions.md) - Guide to use regex
- [Coming from Bash](coming_from_bash.md) - Guide for those coming to Nushell from Bash
- [Nushell map from shells/DSL](nushell_map.md) - Guide to show how Nushell compares with SQL, LINQ, PowerShell, and Bash
- [Nushell map from imperative languages](nushell_map_imperative.md) - Guide to show how Nushell compares with Python, Kotlin, C++, C#, and Rust
- [Nushell map from functional languages](nushell_map_functional.md) - Guide to show how Nushell compares with Clojure, Tablecloth (OCaml / Elm) and Haskell
- [Nushell operator map](nushell_operator_map.md) - Guide to show how Nushell operators compare with those in general purpose programming languages
- [Command Reference](/commands/) - List of all Nushell's commands
docs/nushell/nushell.github.io/book/testing.md:
# Testing your Nushell Code
## Assert Commands
Nushell provides a set of "assertion" commands in the standard library.
One could use built-in equality / order tests such as `==` or `<=` or more complex commands and throw errors manually when an expected condition fails, but using what the standard library has to offer is arguably easier!
In the following, it will be assumed that the `std assert` module has been imported inside the current scope
```nu
use std/assertThe foundation for every assertion is the std assert command. If the condition is not true, it makes an error.
assert (1 == 2)Error:
× Assertion failed.
╭─[entry #13:1:1]
1 │ assert (1 == 2)
· ───┬──
· ╰── It is not true.
╰────
Optionally, a message can be set to show the intention of the assert command, what went wrong or what was expected:
let a = 0
assert ($a == 19) $"The lockout code is wrong, received: ($a)"Error:
× The lockout code is wrong, received: 13
╭─[entry #25:1:1]
1 │ let a = 0
2 │ assert ($a == 19) $"The lockout code is wrong, received: ($a)"
· ────┬───
· ╰── It is not true.
╰────
There are many assert commands, which behave exactly as the base one with the proper operator. The additional value for them is the ability for better error messages.
For example this is not so helpful without additional message:
let a = "foo"
let b = "bar"
assert ($b | str contains $a)Error: × Assertion failed.
╭─[entry #5:3:8]
2 │ let b = "bar"
3 │ assert ($b | str contains $a)
· ───────────┬──────────
· ╰── It is not true.
╰────
While with using assert str contains:
let a = "a needle"
let b = "haystack"
assert str contains $b $aError: × Assertion failed.
╭─[entry #7:3:21]
2 │ let b = "bar"
3 │ assert str contains $b $a
· ──┬──
· ╰─┤ This does not contain 'a needle'.
· │ value: "haystack"
╰────
In general for base assert command it is encouraged to always provide the additional message to show what went wrong. If you cannot use any built-in assert command, you can create a custom one with passing the label for error make for the assert command:
def "assert even" [number: int] {
assert ($number mod 2 == 0) --error-label {
text: $"($number) is not an even number",
span: (metadata $number).span,
}
}Then you'll have your detailed custom error message:
let $a = 13
assert even $aError:
× Assertion failed.
╭─[entry #37:1:1]
1 │ assert even $a
· ─┬
· ╰── 13 is not an even number
╰────
Now that we are able to write tests by calling commands from std assert, it would be great to be able to run them and see our tests fail when there is an issue and pass when everything is correct :)
In this first case, we will assume that the code you are trying to test is part of a Nupm package.
In that case, it is as easy as following the following steps
- create a
tests/directory next to thenupm.nuonpackage file of your package - make the
tests/directory a valid module by adding amod.nufile into it - write commands inside
tests/ - call
nupm test
The convention is that any command fully exported from the tests module will be run as a test, e.g.
export def some-testintests/mod.nuwill rundef just-an-internal-cmdintests/mod.nuwill NOT runexport def another-testintests/spam.nuwill run if and only if there is something likeexport use spam.nu *intests/mod.nu
If your Nushell script or module is not part of a Nupm package, the simplest way is to write tests in standalone scripts and then call them, either from a Makefile or in a CI:
Let's say we have a simple math.nu module which contains a simple Fibonacci command:
# `fib n` is the n-th Fibonacci number
export def fib [n: int] [ nothing -> int ] {
if $n == 0 {
return 0
} else if $n == 1 {
return 1
}
(fib ($n - 1)) + (fib ($n - 2))
}then a test script called tests.nu could look like
use math.nu fib
use std/assert
for t in [
[input, expected];
[0, 0],
[1, 1],
[2, 1],
[3, 2],
[4, 3],
[5, 5],
[6, 8],
[7, 13],
] {
assert equal (fib $t.input) $t.expected
}and be invoked as nu tests.nu
It is also possible to define tests in Nushell as functions with descriptive names and discover
them dynamically without requiring a Nupm package. The following uses scope commands and a
second instance of Nushell to run the generated list of tests.
use std/assert
source fib.nu
def main [] {
print "Running tests..."
let test_commands = (
scope commands
| where ($it.type == "custom")
and ($it.name | str starts-with "test ")
and not ($it.description | str starts-with "ignore")
| get name
| each { |test| [$"print 'Running test: ($test)'", $test] } | flatten
| str join "; "
)
nu --commands $"source ($env.CURRENT_FILE); ($test_commands)"
print "Tests completed successfully"
}
def "test fib" [] {
for t in [
[input, expected];
[0, 0],
[1, 1],
[2, 1],
[3, 2],
[4, 3],
[5, 5],
[6, 8],
[7, 13]
] {
assert equal (fib $t.input) $t.expected
}
}
# ignore
def "test show-ignored-test" [] {
print "This test will not be executed"
}This is a simple example but could be extended to include many of the things you might expect from a testing framework, including setup and tear down functions and test discovery across files.
`docs/nushell/nushell.github.io/book/thinking_in_nu.md`:
```md
# Thinking in Nu
Nushell is different! It's common (and expected!) for new users to have some existing "habits" or mental models coming from other shells or languages.
The most common questions from new users typically fall into one of the following topics:
[[toc]]
## Nushell isn't Bash
### It can sometimes look like Bash
Nushell is both a programming language and a shell. Because of this, it has its own way of working with files, directories, websites, and more. You'll find that some features in Nushell work similar to those you're familiar with in other shells. For instance, pipelines work by combining two (or more) commands together, just like in other shells.
For example, the following commandline works the same in both Bash and Nushell on Unix/Linux platforms:
```nu
curl -s https://api.github.com/repos/nushell/nushell/contributors | jq -c '.[] | {login,contributions}'
# => returns contributors to Nushell, ordered by number of contributions
Nushell has many other similarities with Bash (and other shells) and many commands in common.
::: tip Bash is primarily a command interpreter which runs external commands. Nushell provides many of these as cross-platform, built-in commands.
While the above commandline works in both shells, in Nushell there's just no need to use the curl and jq commands. Instead, Nushell has a built-in http get command and handles JSON data natively. For example:
http get https://api.github.com/repos/nushell/nushell/contributors | select login contributions:::
::: warning Thinking in Nushell Nushell borrows concepts from many shells and languages. You'll likely find many of Nushell's features familiar. :::
Because of this, however, it's sometimes easy to forget that some Bash (and POSIX in general) style constructs just won't work in Nushell. For instance, in Bash, it would be normal to write:
# Redirect using >
echo "hello" > output.txt
# But compare (greater-than) using the test command
test 4 -gt 7
echo $?
# => 1In Nushell, however, the > is used as the greater-than operator for comparisons. This is more in line with modern programming expectations.
4 > 10
# => falseSince > is an operator, redirection to a file in Nushell is handled through a pipeline command that is dedicated to saving content - save:
"hello" | save output.txt::: warning Thinking in Nushell We've put together a list of common Bash'isms and how to accomplish those tasks in Nushell in the Coming from Bash Chapter. :::
Users coming from other shells will likely be very familiar with the echo command. Nushell's
echo might appear the same at first, but it is very different.
First, notice how the following output looks the same in both Bash and Nushell (and even PowerShell and Fish):
echo "Hello, World"
# => Hello, WorldBut while the other shells are sending Hello, World straight to standard output, Nushell's echo is
simply returning a value. Nushell then renders the return value of a command, or more technically, an expression.
More importantly, Nushell implicitly returns the value of an expression. This is similar to PowerShell or Rust in many respects.
::: tip
An expression can be more than just a pipeline. Even custom commands (similar to functions in many languages, but we'll cover them more in depth in a later chapter) automatically, implicitly return the last value. There's no need for an echo or even a return command to return a value - It just happens.
:::
In other words, the string "Hello, World" and the output value from echo "Hello, World" are equivalent:
"Hello, World" == (echo "Hello, World")
# => trueHere's another example with a custom command definition:
def latest-file [] {
ls | sort-by modified | last
}The output of that pipeline (its "value") becomes the return value of the latest-file custom command.
::: warning Thinking in Nushell
Most anywhere you might write echo <something>, in Nushell, you can just write <something> instead.
:::
It's important to understand that an expression can only return a single value. If there are multiple subexpressions inside an expression, only the last value is returned.
A common mistake is to write a custom command definition like this:
def latest-file [] {
echo "Returning the last file"
ls | sort-by modified | last
}
latest-file
New users might expect:
- Line 2 to output "Returning the last file"
- Line 3 to return/output the file
However, remember that echo returns a value. Since only the last value is returned, the Line 2 value is discarded. Only the file will be returned by line 3.
To make sure the first line is displayed, use the print command:
def latest-file [] {
print "Returning last file"
ls | sort-by modified | last
}Also compare:
40; 50; 60::: tip A semicolon is the same as a newline in a Nushell expression. The above is the same as a file or multi-line command:
40
50
60or
echo 40
echo 50
echo 60See Also: Multi-line Editing :::
In all of the above:
- The first value is evaluated as the integer 40 but is not returned
- The second value is evaluated as the integer 50 but is not returned
- The third value is evaluated as the integer 60, and since it is the last value, it is is returned and displayed (rendered).
::: warning Thinking in Nushell When debugging unexpected results, be on the lookout for:
- Subexpressions (e.g., commands or pipelines) that ...
- ... output a (non-
null) value ... - ... where that value isn't returned from the parent expression.
These can be likely sources of issues in your code. :::
Some languages have the concept of "statements" which don't return values. Nushell does not.
In Nushell, every command returns a value, even if that value is null (the nothing type). Consider the following multiline expression:
let p = 7
print $p
$p * 6
- Line 1: The integer 7 is assigned to
$p, but the return value of theletcommand itself isnull. However, because it is not the last value in the expression, it is not displayed. - Line 2: The return value of the
printcommand itself isnull, but theprintcommand forces its argument ($p, which is 7) to be displayed. As with Line 1, thenullreturn value is discarded since this isn't the last value in the expression. - Line 3: Evaluates to the integer value 42. As the last value in the expression, this is the return result, and is also displayed (rendered).
::: warning Thinking in Nushell Becoming familiar with the output types of common commands will help you understand how to combine simple commands together to achieve complex results.
help <command> will show the signature, including the output type(s), for each command in Nushell.
:::
In Nushell, there are exactly two, separate, high-level stages when running code:
- Stage 1 (Parser): Parse the entire source code
- Stage 2 (Engine): Evaluate the entire source code
It can be useful to think of Nushell's parsing stage as compilation in static languages like Rust or C++. By this, we mean that all of the code that will be evaluated in Stage 2 must be known and available during the parsing stage.
::: important
However, this also means that Nushell cannot currently support an eval construct as with dynamic languages such as Bash or Python.
:::
On the other hand, the static results of Parsing are key to many features of Nushell its REPL, such as:
- Accurate and expressive error messages
- Semantic analysis for earlier and robust detection of error conditions
- IDE integration
- The type system
- The module system
- Completions
- Custom command argument parsing
- Syntax highlighting
- Real-time error highlighting
- Profiling and debugging commands
- (Future) Formatting
- (Future) Saving IR (Intermediate Representation) "compiled" results for faster execution
The static nature of Nushell often leads to confusion for users coming to Nushell from languages where an eval is available.
Consider a simple two-line file:
<line1 code>
<line2 code>
- Parsing:
- Line 1 is parsed
- Line 2 is parsed
- If parsing was successful, then Evaluation:
- Line 1 is evaluated
- Line 2 is evaluated
This helps demonstrate why the following examples cannot run as a single expression (e.g., a script) in Nushell:
::: note
The following examples use the source command, but similar conclusions apply to other commands that parse Nushell source code, such as use, overlay use, hide or source-env.
:::
Consider this scenario:
"print Hello" | save output.nu
source output.nu
# => Error: nu::parser::sourced_file_not_found
# =>
# => × File not found
# => ╭─[entry #5:2:8]
# => 1 │ "print Hello" | save output.nu
# => 2 │ source output.nu
# => · ────┬────
# => · ╰── File not found: output.nu
# => ╰────
# => help: sourced files need to be available before your script is runThis is problematic because:
- Line 1 is parsed but not evaluated. In other words,
output.nuis not created during the parsing stage, but only during evaluation. - Line 2 is parsed. Because
sourceis a parser-keyword, resolution of the sourced file is attempted during Parsing (Stage 1). Butoutput.nudoesn't even exist yet! If it does exist, then it's probably not even the correct file! This results in the error.
::: note Typing these as two separate lines in the REPL will work since the first line will be parsed and evaluated, then the second line will be parsed and evaluated.
The limitation only occurs when both are parsed together as a single expression, which could be part of a script, block, closure, or other expression.
See the REPL section in "How Nushell Code Gets Run" for more explanation. :::
Another common scenario when coming from another shell might be attempting to dynamically create a filename that will be sourced:
let my_path = "~/nushell-files"
source $"($my_path)/common.nu"
# => Error:
# => × Error: nu::shell::not_a_constant
# => │
# => │ × Not a constant.
# => │ ╭─[entry #6:2:11]
# => │ 1 │ let my_path = "~/nushell-files"
# => │ 2 │ source $"($my_path)/common.nu"
# => │ · ────┬───
# => │ · ╰── Value is not a parse-time constant
# => │ ╰────
# => │ help: Only a subset of expressions are allowed constants during parsing. Try using the 'const' command or typing the value literally.
# => │
# => ╭─[entry #6:2:8]
# => 1 │ let my_path = "~/nushell-files"
# => 2 │ source $"($my_path)/common.nu"
# => · ───────────┬───────────
# => · ╰── Encountered error during parse-time evaluation
# => ╰────Because the let assignment is not resolved until evaluation, the parser-keyword source will fail during parsing if passed a variable.
::: details Comparing Rust and C++ Imagine that the code above was written in a typical compiled language such as C++:
#include <string>
std::string my_path("foo");
#include <my_path + "/common.h">or Rust
let my_path = "foo";
use format!("{}::common", my_path);If you've ever written a simple program in any of these languages, you can see these examples aren't valid in those languages. Like Nushell, compiled languages require that all of the source code files are ready and available to the compiler beforehand.
:::
::: tip See Also
As noted in the error message, however, this can work if my_path can be defined as a constant since constants can be (and are) resolved during parsing.
const my_path = "~/nushell-files"
source $"($my_path)/common.nu"See Parse-time Constant Evaluation for more details. :::
Here's one more — Change to a different directory and then attempt to source a file in that directory.
if ('spam/foo.nu' | path exists) {
cd spam
source-env foo.nu
}
Based on what we've covered about Nushell's Parse/Eval stages, see if you can spot the problem with that example.
::: details Solution
In line 3, during Parsing, the source-env attempts to parse foo.nu. However, cd doesn't occur until Evaluation. This results in a parse-time error, since the file is not found in the current directory.
To resolve this, of course, simply use the full-path to the file to be sourced.
source-env spam/foo.nu:::
::: important For a more in-depth explanation of this section, see How Nushell Code Gets Run. :::
::: warning Thinking in Nushell Nushell is designed to use a single Parsing stage for each expression or file. This Parsing stage occurs before and is separate from Evaluation. While this enables many of Nushell's features, it also means that users need to understand the limitations it creates. :::
Another common surprise when coming from other languages is that Nushell variables are immutable by default. While Nushell has optional mutable variables, many of Nushell's commands are based on a functional-style of programming which requires immutability.
Immutable variables are also key to Nushell's par-each command, which allows you to operate on multiple values in parallel using threads.
See Immutable Variables and Choosing between mutable and immutable variables for more information.
::: warning Thinking in Nushell If you're used to relying on mutable variables, it may take some time to relearn how to code in a more functional style. Nushell has many functional features and commands that operate on and with immutable variables. Learning them will help you write code in a more Nushell-idiomatic style.
A nice bonus is the performance increase you can realize by running parts of your code in parallel with par-each.
:::
Nushell takes multiple design cues from compiled languages. One such cue is that languages should avoid global mutable state. Shells have commonly used global mutation to update the environment, but Nushell attempts to steer clear of this approach.
In Nushell, blocks control their own environment. Changes to the environment are scoped to the block where they occur.
In practice, this lets you write (as just one example) more concise code for working with subdirectories. Here's an example that builds each sub-project in the current directory:
ls | each { |row|
cd $row.name
make
}The cd command changes the PWD environment variables, but this variable change does not survive past the end of the block. This allows each iteration to start from the current directory and then enter the next subdirectory.
Having a scoped environment makes commands more predictable, easier to read, and when the time comes, easier to debug. It's also another feature that is key to the par-each command we discussed above.
Nushell also provides helper commands like load-env as a convenient way of loading multiple updates to the environment at once.
::: tip See Also Environment - Scoping :::
::: note
def --env is an exception to this rule. It allows you to create a command that changes the parent's environment.
:::
::: warning Thinking in Nushell Use scoped-environment to write more concise scripts and prevent unnecessary or unwanted global environment mutation. :::
`docs/nushell/nushell.github.io/book/types_of_data.md`:
```md
---
prev:
text: Nu Fundamentals
link: /book/nu_fundamentals.md
---
# Types of Data
Traditional Unix shell commands communicate with each other using strings of text -- One command writes text to standard output (often abbreviated `stdout`) and the other reads text from standard input (or `stdin`). This allows multiple commands to be combined together to communicate through what is called a "pipeline".
Nushell embraces this approach and expands it to include other types of data in addition to strings.
Like many programming languages, Nu models data using a set of simple, structured data types. Simple data types include integers, floats, strings, and booleans. There are also special types for dates, file sizes, and time durations.
The [`describe`](/commands/docs/describe.md) command returns the type of a data value:
```nu
42 | describe
# => int
| Type | Example |
|---|---|
| Integers | -65535 |
| Floats (decimals) | 9.9999, Infinity |
| Strings | "hole 18", 'hole 18', `hole 18`, hole18, r#'hole18'# |
| Booleans | true |
| Dates | 2000-01-01 |
| Durations | 2min + 12sec |
| File-sizes | 64mb |
| Ranges | 0..4, 0..<5, 0.., ..4 |
| Binary | 0x[FE FF] |
| Lists | [0 1 'two' 3] |
| Records | {name:"Nushell", lang: "Rust"} |
| Tables | [{x:12, y:15}, {x:8, y:9}], [[x, y]; [12, 15], [8, 9]] |
| Closures | {|e| $e + 1 | into string }, { $in.name.0 | path exists } |
| Cell-paths | $.name.0 |
| Blocks | if true { print "hello!" }, loop { print "press ctrl-c to exit" } |
| Null (Nothing) | null |
| Any | let p: any = 5 |
| Description: | Numbers without a fractional component (positive, negative, and 0) |
| Annotation: | int |
| Literal Syntax: | A decimal, hex, octal, or binary numeric value without a decimal place. E.g., -100, 0, 50, +50, 0xff (hex), 0o234 (octal), 0b10101 (binary) |
| See also: | Language Reference - Integer |
Simple Example:
10 / 2
# => 5
5 | describe
# => int| Description: | Numbers with some fractional component |
| Annotation: | float |
| Literal Syntax: | A decimal numeric value including a decimal place. E.g., 1.5, 2.0, -15.333 |
| See also: | Language Reference - Float |
Simple Example:
2.5 / 5.0
# => 0.5::: tip As in most programming languages, decimal values in Nushell are approximate.
10.2 * 5.1
# => 52.01999999999999:::
| Description: | A series of characters that represents text |
| Annotation: | string |
| Literal Syntax: | See Working with strings |
| See also: | Handling Strings |
| Language Reference - String |
As with many languages, Nushell provides multiple ways to specify String values and numerous commands for working with strings.
Simple (obligatory) example:
let audience: string = "World"
$"Hello, ($audience)"
# => Hello, World| Description: | True or False value |
| Annotation: | bool |
| Literal Syntax: | Either a literal true or false |
| See also: | Language Reference - Boolean |
Booleans are commonly the result of a comparison. For example:
let mybool: bool = (2 > 1)
$mybool
# => true
let mybool: bool = ($env.HOME | path exists)
$mybool
# => trueA boolean result is commonly used to control the flow of execution:
let num = -2
if $num < 0 { print "It's negative" }
# => It's negative| Description: | Represents a specific point in time using international standard date-time descriptors |
| Annotation: | datetime |
| Literal Syntax: | See Language Guide - Date |
Simple example:
date now
# => Mon, 12 Aug 2024 13:59:22 -0400 (now)
# Format as Unix epoch
date now | format date '%s'
# => 1723485562| Description: | Represent a unit of a passage of time |
| Annotation: | duration |
| Literal Syntax: | See Language Reference - Duration |
Durations support fractional values as well as calculations.
Simple example:
3.14day
# => 3day 3hr 21min
30day / 1sec # How many seconds in 30 days?
# => 2592000| Description: | Specialized numeric type to represent the size of files or a number of bytes |
| Annotation: | filesize |
| Literal Syntax: | See Language Reference - Filesize |
Nushell also has a special type for file sizes.
As with durations, Nushell supports fractional file sizes and calculations:
0.5kB
# => 500 B
1GiB / 1B
# => 1073741824
(1GiB / 1B) == 2 ** 30
# => trueSee the Language Reference for a complete list of units and more detail.
| Description: | Describes a range of values from a starting value to an ending value, with an optional stride. |
| Annotation: | range |
| Literal Syntax: | <start_value>..<end_value>. E.g., 1..10. |
<start_value>..<second_value>..<end_value>. E.g., 2..4..20 |
|
| See also: | Language Guide - Range |
Simple example:
1..5
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => │ 4 │ 5 │
# => ╰───┴───╯::: tip
You can also easily create lists of characters with a form similar to ranges with the command seq char as well as with dates using the seq date command.
:::
| Description: | An expression that is used to navigated to an inner value in a structured value. |
| Annotation: | cell-path |
| Literal syntax: | A dot-separated list of row (int) and column (string) IDs. E.g., name.4.5. |
Optionally, use a leading $. when needed for disambiguation, such as when assigning a cell-path to a variable |
|
| See also: | Language Reference - Cell-path |
| Navigating and Accessing Structured Data chapter. |
Simple example:
let cp = $.2
# Return list item at index 2
[ foo bar goo glue ] | get $cp
# => goo| Description: | An anonymous function, often called a lambda function, which accepts parameters and closes over (i.e., uses) variables from outside its scope |
| Annotation: | closure |
| Literal Syntax: | {|args| expressions } |
| See also: | Language Reference - Closure |
Simple example:
This closure returns a boolean result of the comparison and then uses it in a where command to return all values greater than 5.
let compare_closure = {|a| $a > 5 }
let original_list = [ 40 -4 0 8 12 16 -16 ]
$original_list | where $compare_closure
# => ╭───┬────╮
# => │ 0 │ 40 │
# => │ 1 │ 8 │
# => │ 2 │ 12 │
# => │ 3 │ 16 │
# => ╰───┴────╯Closures are a useful way to represent code that can be executed on each row of data via filters
| Description: | Represents binary data |
| Annotation: | binary |
| Literal Syntax: | 0x[ffffffff] - hex-based binary representation |
0o[1234567] - octal-based binary representation |
|
0b[10101010101] - binary-based binary representation |
|
| See also: | Language Guide - Binary |
Binary data, like the data from an image file, is a group of raw bytes.
Simple example - Confirm that a JPEG file starts with the proper identifier:
open nushell_logo.jpg
| into binary
| first 2
| $in == 0x[ff d8]
# => trueNushell includes a collection of structured data types that can contain the primitive types above. For example, instead of a single float, structured data gives us a way to represent multiple float values, such as a list of temperature readings, in the same value. Nushell supports the following structured data types:
| Description: | Ordered sequence of zero or more values of any type |
| Annotation: | list |
| Literal Syntax: | See Language Guide - List |
| See Also: | Working with Lists |
| Navigating and Accessing Structured Data |
Simple example:
[Sam Fred George]
# => ╭───┬────────╮
# => │ 0 │ Sam │
# => │ 1 │ Fred │
# => │ 2 │ George │
# => ╰───┴────────╯| Description: | Holds key-value pairs which associate string keys with various data values. |
| Annotation: | record |
| Literal Syntax: | See Language Guide - Record |
| See Also: | Working with Records |
| Navigating and Accessing Structured Data |
Simple example:
let my_record = {
name: "Kylian"
rank: 99
}
$my_record
# => ╭───────┬────────────╮
# => │ name │ Kylian │
# => │ rank │ 99 │
# => ╰───────┴────────────╯
$my_record | get name
# => Kylian| Description: | A two-dimensional container with both columns and rows where each cell can hold any basic or structured data type |
| Annotation: | table |
| See Also: | Working with Tables |
| Navigating and Accessing Structured Data | |
| Language Guide - Table |
The table is a core data structure in Nushell. As you run commands, you'll see that many of them return tables as output. A table has both rows and columns.
:::tip
Internally, tables are simply lists of records. This means that any command which extracts or isolates a specific row of a table will produce a record. For example, get 0, when used on a list, extracts the first value. But when used on a table (a list of records), it extracts a record:
[{x:12, y:5}, {x:3, y:6}] | get 0
# => ╭───┬────╮
# => │ x │ 12 │
# => │ y │ 5 │
# => ╰───┴────╯:::
| Description: | When used in a type annotation or signature, matches any type. In other words, a "superset" of other types. |
| Annotation: | any |
| Literal syntax: | N/A - Any literal value can be assigned to an any type |
| See also: | Language Reference - Any |
| Description: | A syntactic form used by some Nushell keywords (e.g., if and for) |
| Annotation: | N/A |
| Literal Syntax: | N/A |
| See also: | Language Reference - Block |
Simple example:
if true { print "It's true" }The { print "It's true" } portion above is a block.
| Description: | The nothing type is to be used to represent the absence of another value. |
| Annotation: | nothing |
| Literal Syntax: | null |
| See also: | Language Reference - Nothing |
Using the optional operator ? returns null if the requested cell-path doesn't exist:
let simple_record = { a: 5, b: 10 }
$simple_record.a?
# => 5
$simple_record.c?
# => Nothing is output
$simple_record.c? | describe
# => nothing
$simple_record.c? == null
# => true
`docs/nushell/nushell.github.io/book/variables.md`:
```md
# Variables
Nushell values can be assigned to named variables using the `let`, `const`, or `mut` keywords.
After creating a variable, we can refer to it using `$` followed by its name.
## Types of Variables
### Immutable Variables
An immutable variable cannot change its value after declaration. They are declared using the `let` keyword,
```nu
let val = 42
$val
# => 42
$val = 100
# => Error: nu::shell::assignment_requires_mutable_variable
# =>
# => × Assignment to an immutable variable.
# => ╭─[entry #10:1:1]
# => 1 │ $val = 100
# => · ──┬─
# => · ╰── needs to be a mutable variable
# => ╰────
However, immutable variables can be 'shadowed'. Shadowing means that they are redeclared and their initial value cannot be used anymore within the same scope.
let val = 42 # declare a variable
do { let val = 101; $val } # in an inner scope, shadow the variable
# => 101
$val # in the outer scope the variable remains unchanged
# => 42
let val = $val + 1 # now, in the outer scope, shadow the original variable
$val # in the outer scope, the variable is now shadowed, and
# => 43 # its original value is no longer available.A mutable variable is allowed to change its value by assignment. These are declared using the mut keyword.
mut val = 42
$val += 27
$val
# => 69There are a couple of assignment operators used with mutable variables
| Operator | Description |
|---|---|
= |
Assigns a new value to the variable |
+= |
Adds a value to the variable and makes the sum its new value |
-= |
Subtracts a value from the variable and makes the difference its new value |
*= |
Multiplies the variable by a value and makes the product its new value |
/= |
Divides the variable by a value and makes the quotient its new value |
++= |
Appends a list or a value to a variable |
::: tip Note
+=,-=,*=and/=are only valid in the contexts where their root operations are expected to work. For example,+=uses addition, so it can not be used for contexts where addition would normally fail++=requires that either the variable or the argument is a list.
:::
Closures and nested defs cannot capture mutable variables from their environment. For example
# naive method to count number of elements in a list
mut x = 0
[1 2 3] | each { $x += 1 } # error: $x is captured in a closureTo use mutable variables for such behaviour, you are encouraged to use the loops
A constant variable is an immutable variable that can be fully evaluated at parse-time. These are useful with commands that need to know the value of an argument at parse time, like source, use and plugin use. See how nushell code gets run for a deeper explanation. They are declared using the const keyword
const script_file = 'path/to/script.nu'
source $script_fileTry to use immutable variables for most use-cases.
You might wonder why Nushell uses immutable variables by default. For the first few years of Nushell's development, mutable variables were not a language feature. Early on in Nushell's development, we decided to see how long we could go using a more data-focused, functional style in the language. This experiment showed its value when Nushell introduced parallelism. By switching from each to par-each in any Nushell script, you're able to run the corresponding block of code in parallel over the input. This is possible because Nushell's design leans heavily on immutability, composition, and pipelining.
Many, if not most, use-cases for mutable variables in Nushell have a functional solution that:
- Only uses immutable variables, and as a result ...
- Has better performance
- Supports streaming
- Can support additional features such as
par-eachas mentioned above
For instance, loop counters are a common pattern for mutable variables and are built into most iterating commands. For example, you can get both each item and the index of each item using each with enumerate:
ls | enumerate | each { |elt| $"Item #($elt.index) is size ($elt.item.size)" }
# => ╭───┬───────────────────────────╮
# => │ 0 │ Item #0 is size 812 B │
# => │ 1 │ Item #1 is size 3.4 KiB │
# => │ 2 │ Item #2 is size 11.0 KiB │
# => │ 3 │ ... │
# => │ 4 │ Item #18 is size 17.8 KiB │
# => │ 5 │ Item #19 is size 482 B │
# => │ 6 │ Item #20 is size 4.0 KiB │
# => ╰───┴───────────────────────────╯You can also use the reduce command to work in the same way you might mutate a variable in a loop. For example, if you wanted to find the largest string in a list of strings, you might do:
[one, two, three, four, five, six] | reduce {|current_item, max|
if ($current_item | str length) > ($max | str length) {
$current_item
} else {
$max
}
}
threeWhile reduce processes lists, the generate command can be used with arbitrary sources such as external REST APIs, also without requiring mutable variables. Here's an example that retrieves local weather data every hour and generates a continuous list from that data. The each command can be used to consume each new list item as it becomes available.
generate {|weather_station|
let res = try {
http get -ef $'https://api.weather.gov/stations/($weather_station)/observations/latest'
} catch {
null
}
sleep 1hr
match $res {
null => {
next: $weather_station
}
_ => {
out: ($res.body? | default '' | from json)
next: $weather_station
}
}
} khot
| each {|weather_report|
{
time: ($weather_report.properties.timestamp | into datetime)
temp: $weather_report.properties.temperature.value
}
}Using filter commands with immutable variables is often far more performant than mutable variables with traditional flow-control statements such as for and while. For example:
-
Using a
forstatement to create a list of 50,000 random numbers:timeit { mut randoms = [] for _ in 1..50_000 { $randoms = ($randoms | append (random int)) } }Result: 1min 4sec 191ms 135µs 90ns
-
Using
eachto do the same:timeit { let randoms = (1..50_000 | each {random int}) }Result: 19ms 314µs 205ns
-
Using
eachwith 10,000,000 iterations:timeit { let randoms = (1..10_000_000 | each {random int}) }Result: 4sec 233ms 865µs 238ns
As with many filters, the
eachstatement also streams its results, meaning the next stage of the pipeline can continue processing without waiting for the results to be collected into a variable.For tasks which can be optimized by parallelization, as mentioned above,
par-eachcan have even more drastic performance gains.
Variable names in Nushell come with a few restrictions as to what characters they can contain. In particular, they cannot contain these characters:
. [ ( { + - * ^ / = ! < > & |
It is common for some scripts to declare variables that start with $. This is allowed, and it is equivalent to the $ not being there at all.
let $var = 42
# identical to `let var = 42`
`docs/nushell/nushell.github.io/book/working_with_lists.md`:
```md
# Working with Lists
:::tip
Lists are equivalent to the individual columns of tables. You can think of a list as essentially being a "one-column table" (with no column name). Thus, any command which operates on a column _also_ operates on a list. For instance, [`where`](/commands/docs/where.md) can be used with lists:
```nu
[bell book candle] | where ($it =~ 'b')
# => ╭───┬──────╮
# => │ 0 │ bell │
# => │ 1 │ book │
# => ╰───┴──────╯
:::
A list is an ordered collection of values.
A list is created using square brackets surrounding values separated by spaces, linebreaks, and/or commas.
For example, [foo bar baz] or [foo, bar, baz].
::: tip
Nushell lists are similar to JSON arrays. The same [ "Item1", "Item2", "Item3" ] that represents a JSON array can also be used to create a Nushell list.
:::
We can insert values into lists as they flow through the pipeline, for example let's insert the value 10 into the middle of a list:
[1, 2, 3, 4] | insert 2 10
# => [1, 2, 10, 3, 4]We can also use update to replace the 2nd element with the value 10.
[1, 2, 3, 4] | update 1 10
# => [1, 10, 3, 4]In addition to insert and update, we also have prepend and append. These let you insert to the beginning of a list or at the end of the list, respectively.
For example:
let colors = [yellow green]
let colors = ($colors | prepend red)
let colors = ($colors | append purple)
let colors = ($colors ++ ["blue"])
let colors = (["black"] ++ $colors)
$colors
# => [black red yellow green purple blue]In case you want to remove items from list, there are many ways. skip allows you skip first rows from input, while drop allows you to skip specific numbered rows from end of list.
let colors = [red yellow green purple]
let colors = ($colors | skip 1)
let colors = ($colors | drop 2)
$colors
# => [yellow]We also have last and first which allow you to take from the end or beginning of the list, respectively.
let colors = [red yellow green purple black magenta]
let colors = ($colors | last 3)
$colors
# => [purple black magenta]And from the beginning of a list,
let colors = [yellow green purple]
let colors = ($colors | first 2)
$colors
# => [yellow green]To append one or more lists together, optionally with values interspersed in between, you can also use the
spread operator (...):
let x = [1 2]
[
...$x
3
...(4..7 | take 2)
]
# => ╭───┬───╮
# => │ 0 │ 1 │
# => │ 1 │ 2 │
# => │ 2 │ 3 │
# => │ 3 │ 4 │
# => │ 4 │ 5 │
# => ╰───┴───╯To iterate over the items in a list, use the each command with a block
of Nu code that specifies what to do to each item. The block parameter (e.g. |elt| in { |elt| print $elt }) is the current list
item, but the enumerate filter can be used to provide index and item values if needed. For example:
let names = [Mark Tami Amanda Jeremy]
$names | each { |elt| $"Hello, ($elt)!" }
# Outputs "Hello, Mark!" and three more similar lines.
$names | enumerate | each { |elt| $"($elt.index + 1) - ($elt.item)" }
# Outputs "1 - Mark", "2 - Tami", etc.The where command can be used to create a subset of a list, effectively filtering the list based on a condition.
The following example gets all the colors whose names end in "e".
let colors = [red orange yellow green blue purple]
$colors | where ($it | str ends-with 'e')
# The block passed to `where` must evaluate to a boolean.
# This outputs the list [orange blue purple].In this example, we keep only values higher than 7.
let scores = [7 10 8 6 7]
$scores | where $it > 7 # [10 8]The reduce command computes a single value from a list.
It uses a block which takes 2 parameters: the current item (conventionally named elt) and an accumulator
(conventionally named acc). To specify an initial value for the accumulator, use the --fold (-f) flag.
To change elt to have index and item values, use the enumerate filter.
For example:
let scores = [3 8 4]
$"total = ($scores | reduce { |elt, acc| $acc + $elt })" # total = 15
$"total = ($scores | math sum)" # easier approach, same result
$"product = ($scores | reduce --fold 1 { |elt, acc| $acc * $elt })" # product = 96
$scores | enumerate | reduce --fold 0 { |elt, acc| $acc + $elt.index * $elt.item } # 0*3 + 1*8 + 2*4 = 16::: tip Note The following is a basic overview. For a more in-depth discussion of this topic, see the chapter, Navigating and Accessing Structured Data. :::
To access a list item at a given index, use the $name.index form where $name is a variable that holds a list.
For example, the second element in the list below can be accessed with $names.1.
let names = [Mark Tami Amanda Jeremy]
$names.1 # gives TamiIf the index is in some variable $index we can use the get command to extract the item from the list.
let names = [Mark Tami Amanda Jeremy]
let index = 1
$names | get $index # gives TamiThe length command returns the number of items in a list.
For example, [red green blue] | length outputs 3.
The is-empty command determines whether a string, list, or table is empty.
It can be used with lists as follows:
let colors = [red green blue]
$colors | is-empty # false
let colors = []
$colors | is-empty # trueThe in and not-in operators are used to test whether a value is in a list. For example:
let colors = [red green blue]
'blue' in $colors # true
'yellow' in $colors # false
'gold' not-in $colors # trueThe any command determines if any item in a list
matches a given condition.
For example:
let colors = [red green blue]
# Do any color names end with "e"?
$colors | any {|elt| $elt | str ends-with "e" } # true
# Is the length of any color name less than 3?
$colors | any {|elt| ($elt | str length) < 3 } # false
let scores = [3 8 4]
# Are any scores greater than 7?
$scores | any {|elt| $elt > 7 } # true
# Are any scores odd?
$scores | any {|elt| $elt mod 2 == 1 } # trueThe all command determines if every item in a list
matches a given condition.
For example:
let colors = [red green blue]
# Do all color names end with "e"?
$colors | all {|elt| $elt | str ends-with "e" } # false
# Is the length of all color names greater than or equal to 3?
$colors | all {|elt| ($elt | str length) >= 3 } # true
let scores = [3 8 4]
# Are all scores greater than 7?
$scores | all {|elt| $elt > 7 } # false
# Are all scores even?
$scores | all {|elt| $elt mod 2 == 0 } # falseThe flatten command creates a new list from an existing list
by adding items in nested lists to the top-level list.
This can be called multiple times to flatten lists nested at any depth.
For example:
[1 [2 3] 4 [5 6]] | flatten # [1 2 3 4 5 6]
[[1 2] [3 [4 5 [6 7 8]]]] | flatten | flatten | flatten # [1 2 3 4 5 6 7 8]The wrap command converts a list to a table. Each list value will
be converted to a separate row with a single column:
let zones = [UTC CET Europe/Moscow Asia/Yekaterinburg]
# Show world clock for selected time zones
$zones | wrap 'Zone' | upsert Time {|row| (date now | date to-timezone $row.Zone | format date '%Y.%m.%d %H:%M')}
`docs/nushell/nushell.github.io/book/working_with_records.md`:
```md
# Working with Records
:::tip
Records are roughly equivalent to the individual rows of a table. You can think of a record as essentially being a "one-row table". Thus, most commands which operate on a table row _also_ operates on a record. For instance, [`update`](/commands/docs/update.md) can be used with records:
```nu
let my_record = {
name: "Sam"
age: 30
}
$my_record | update age { $in + 1 }
# => ╭──────┬─────╮
# => │ name │ Sam │
# => │ age │ 31 │
# => ╰──────┴─────╯
Note that the my_record variable is immutable. The updated record resulting from the pipeline is printed as seen in the code block. The my_record variable still holds the original value - $my_record.age is still 30.
:::
A record is a collection of zero or more key-value pair mappings. It is similar to a JSON object, and can be created using the same syntax:
# Nushell
{ "apples": 543, "bananas": 411, "oranges": 0 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => ╰─────────┴─────╯
# JSON
'{ "apples": 543, "bananas": 411, "oranges": 0 }' | from json
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => ╰─────────┴─────╯In Nushell, the key-value pairs of a record can also be separated using spaces or line-breaks.
::: tip As records can have many fields, they are, by default, displayed vertically rather than left-to-right. To display a record left-to-right, convert it to a nuon. For example:
{
name: "Sam"
rank: 10
} | to nuon
# => {name: Sam, rank: 10}:::
As with lists, you can insert values in records. For example, let's add some pears:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| insert pears { 21 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 0 │
# => │ pears │ 21 │
# => ╰─────────┴─────╯You can also update values:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| update oranges { 100 }
# => ╭─────────┬─────╮
# => │ apples │ 543 │
# => │ bananas │ 411 │
# => │ oranges │ 100 │
# => ╰─────────┴─────╯To make a copy of a record with new fields, you can either:
-
Use the
mergecommand:let first_record = { name: "Sam", rank: 10 } $first_record | merge { title: "Mayor" } # => ╭───────┬───────╮ # => │ name │ Sam │ # => │ rank │ 10 │ # => │ title │ Mayor │ # => ╰───────┴───────╯
-
Use the spread operator (
...) to expand the first record inside a new record:let first_record = { name: "Sam", rank: 10 } { ...$first_record title: "Mayor" } # => ╭───────┬───────╮ # => │ name │ Sam │ # => │ rank │ 10 │ # => │ title │ Mayor │ # => ╰───────┴───────╯
Use the items command to iterate over each pair of key and value:
{ "apples": 543, "bananas": 411, "oranges": 0 } | items {|fruit, count| $"We have ($fruit) ($count)" }
# => ╭───┬─────────────────────╮
# => │ 0 │ We have apples 543 │
# => │ 1 │ We have bananas 411 │
# => │ 2 │ We have oranges 0 │
# => ╰───┴─────────────────────╯Alternatively, you can transpose the record into a table with columns, and then iterate over rows:
{ "apples": 543, "bananas": 411, "oranges": 0 }
| transpose fruit count
| each {|f| $"We have ($f.count) ($f.fruit)" }
# => ╭───┬─────────────────────╮
# => │ 0 │ We have 543 apples │
# => │ 1 │ We have 411 bananas │
# => │ 2 │ We have 0 oranges │
# => ╰───┴─────────────────────╯See Navigating and Accessing Structured Data for an in-depth explanation of how to access record values (and other structured data).
See Working with Tables - Remember, commands that operate on table rows will usually operate the same way on records.
`docs/nushell/nushell.github.io/book/working_with_strings.md`:
```md
# Working with Strings
As with most languages, strings are a collection of 0 or more characters that represent text. This can include file names, file paths, names of columns,
and much more. Strings are so common that Nushell offers multiple string formats to match your use-case:
## String Formats at a Glance
| Format of string | Example | Escapes | Notes |
| ---------------------------------------------------- | ----------------------- | ------------------------- | ---------------------------------------------------------------------- |
| [Single-quoted string](#single-quoted-strings) | `'[^\n]+'` | None | Cannot contain single quotes within the string |
| [Double-quoted string](#double-quoted-strings) | `"The\nEnd"` | C-style backslash escapes | All literal backslashes must be escaped |
| [Raw strings](#raw-strings) | `r#'Raw string'#` | None | May include single quotes |
| [Bare word string](#bare-word-strings) | `ozymandias` | None | Can only contain "word" characters; Cannot be used in command position |
| [Backtick string](#backtick-quoted-strings) | <code>\`[^\n]+\`</code> | None | Bare string that can include whitespace. Cannot contain any backticks |
| [Single-quoted interpolation](#string-interpolation) | `$'Captain ($name)'` | None | Cannot contain any `'` or unmatched `()` |
| [Double-quoted interpolation](#string-interpolation) | `$"Captain ($name)"` | C-style backslash escapes | All literal backslashes and `()` must be escaped |
## Single-quoted Strings
The simplest string in Nushell is the single-quoted string. This string uses the `'` character to surround some text. Here's the text for hello world as a single-quoted string:
```nu
'hello world'
# => hello world
'The
end'
# => The
# => end
Single-quoted strings don't do anything to the text they're given, making them ideal for holding a wide range of text data.
For more complex strings, Nushell also offers double-quoted strings. These strings use the " character to surround text. They also support the ability escape characters inside the text using the \ character.
For example, we could write the text hello followed by a new line and then world, using escape characters and a double-quoted string:
"hello\nworld"
# => hello
# => worldEscape characters let you quickly add in a character that would otherwise be hard to type.
Nushell currently supports the following escape characters:
\"- double-quote character\'- single-quote character\\- backslash\/- forward slash\b- backspace\f- formfeed\r- carriage return\n- newline (line feed)\t- tab\u{X...}- a single unicode character, where X... is 1-6 hex digits (0-9, A-F)
To create a \0 (NUL) character, you may use \u{0} or
string interpolation with (char nul).
Raw strings behave the same as a single quoted strings, except that raw strings
may also contain single quotes. This is possible because raw strings are enclosed
by a starting r#' and a closing '#. This syntax should look familiar to users
of Rust.
r#'Raw strings can contain 'quoted' text.'#
# => Raw strings can contain 'quoted' text.Additional # symbols can be added to the start and end of the raw string to enclose
one less than the same number of # symbols next to a ' symbol in the string. This can
be used to nest raw strings:
r###'r##'This is an example of a raw string.'##'###
# => r##'This is an example of a raw string.'##Like other shell languages (but unlike most other programming languages) strings consisting of a single 'word' can also be written without any quotes:
print hello
# => hello
[hello] | describe
# => list<string>But be careful - if you use a bare word plainly on the command line (that is, not inside a data structure or used as a command parameter) or inside round brackets ( ), it will be interpreted as an external command:
hello
# => Error: nu::shell::external_command
# =>
# => × External command failed
# => ╭─[entry #5:1:1]
# => 1 │ hello
# => · ──┬──
# => · ╰── executable was not found
# => ╰────
# => help: program not foundAlso, many bare words have special meaning in nu, and so will not be interpreted as a string:
true | describe
# => bool
[true] | describe
# => list<bool>
[trueX] | describe
# => list<string>
trueX | describe
# => Error: nu::shell::external_command
# =>
# => × External command failed
# => ╭─[entry #5:1:1]
# => 1 │ trueX | describe
# => · ──┬──
# => · ╰── executable was not found
# => ╰────
# => help: program not foundSo, while bare strings are useful for informal command line usage, when programming more formally in nu, you should generally use quotes.
Bare word strings, by their nature, cannot include spaces or quotes. As an alternative, Nushell also includes backtick-quoted
strings using the ` character. In most cases, these should operate the same as a bare word string.
For instance, as with a bare word, a backtick-quoted string in the first position of an expression will be interpreted as a command or path. For example:
# Run the external ls binary found on the path
`ls`
# Move up one directory
`..`
# Change to the "my dir" subdirectory, if it exists
`./my dir`Backtick-quoted strings can be useful for combining globs with files or directories which include spaces:
ls `./my dir/*`Backtick-quoted strings cannot contain unmatched backticks in the string itself. For example:
echo ````
``
echo ```
# Unterminated string which will start a new line in the CLIYou can place the ^ sigil in front of any string (including a variable) to have Nushell execute the string as if it was an external command:
^'C:\Program Files\exiftool.exe'
let foo = 'C:\Program Files\exiftool.exe'
^$fooYou can also use the run-external command for this purpose, which provides additional flags and options.
There are various ways to pre, or append strings. If you want to add something to the beginning of each string closures are a good option:
['foo', 'bar'] | each {|s| '~/' ++ $s} # ~/foo, ~/bar
['foo', 'bar'] | each {|s| '~/' + $s} # ~/foo, ~/barYou can also use a regex to replace the beginning or end of a string:
['foo', 'bar'] | str replace -r '^' '~/'# ~/foo, ~/bar
['foo', 'bar'] | str replace -r '$' '~/'# foo~/, bar~/If you want to get one string out of the end then str join is your friend:
"hello" | append "world!" | str join " " # hello world!You can also use reduce:
1..10 | reduce -f "" {|elt, acc| $acc + ($elt | into string) + " + "} # 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +Though in the cases of strings, especially if you don't have to operate on the strings, it's usually easier and more correct (notice the extra + at the end in the example above) to use str join.
Finally you could also use string interpolation, but that is complex enough that it is covered in its own subsection below.
More complex string use cases also need a new form of string: string interpolation. This is a way of building text from both raw text and the result of running expressions. String interpolation combines the results together, giving you a new string.
String interpolation uses $" " and $' ' as ways to wrap interpolated text.
For example, let's say we have a variable called $name and we want to greet the name of the person contained in this variable:
let name = "Alice"
$"greetings, ($name)"
# => greetings, AliceBy wrapping expressions in (), we can run them to completion and use the results to help build the string.
String interpolation has both a single-quoted, $' ', and a double-quoted, $" ", form. These correspond to the single-quoted and double-quoted strings: single-quoted string interpolation doesn't support escape characters while double-quoted string interpolation does.
As of version 0.61, interpolated strings support escaping parentheses, so that the ( and ) characters may be used in a string without Nushell trying to evaluate what appears between them:
$"2 + 2 is (2 + 2) \(you guessed it!)"
# => 2 + 2 is 4 (you guessed it!)Interpolated strings can be evaluated at parse time, but if they include values whose formatting depends
on your configuration and your config.nu hasn't been loaded yet, they will use the default configuration.
So if you have something like this in your config.nu, x will be "2.0 KB" even if your config says to use
MB for all file sizes (datetimes will similarly use the default config).
const x = $"(2kb)"The split row command creates a list from a string based on a delimiter.
"red,green,blue" | split row ","
# => ╭───┬───────╮
# => │ 0 │ red │
# => │ 1 │ green │
# => │ 2 │ blue │
# => ╰───┴───────╯The split column command will create a table from a string based on a delimiter. This applies generic column names to the table.
"red,green,blue" | split column ","
# => ╭───┬─────────┬─────────┬─────────╮
# => │ # │ column1 │ column2 │ column3 │
# => ├───┼─────────┼─────────┼─────────┤
# => │ 0 │ red │ green │ blue │
# => ╰───┴─────────┴─────────┴─────────╯Finally, the split chars command will split a string into a list of characters.
'aeiou' | split chars
# => ╭───┬───╮
# => │ 0 │ a │
# => │ 1 │ e │
# => │ 2 │ i │
# => │ 3 │ o │
# => │ 4 │ u │
# => ╰───┴───╯The str command
Many string functions are subcommands of the str command. You can get a full list using help str.
For example, you can look if a string contains a particular substring using str contains:
"hello world" | str contains "o wo"
# => true(You might also prefer, for brevity, the =~ operator (described below).)
You can trim the sides of a string with the str trim command. By default, the str trim commands trims whitespace from both sides of the string. For example:
' My string ' | str trim
# => My stringYou can specify on which side the trimming occurs with the --right and --left options. (-r and -l being the short-form options respectively)
To trim a specific character, use --char <Character> or -c <Character> to specify the character to trim.
Here's an example of all the options in action:
'=== Nu shell ===' | str trim -r -c '='
# => === Nu shellSubstrings are slices of a string. They have a startpoint and an endpoint. Here's an example of using a substring:
'Hello World!' | str index-of 'o'
# => 4
'Hello World!' | str index-of 'r'
# => 8
'Hello World!' | str substring 4..8
# => o WoWith the fill command you can add padding to a string. Padding adds characters to string until it's a certain length. For example:
'1234' | fill -a right -c '0' -w 10
# => 0000001234
'1234' | fill -a left -c '0' -w 10 | str length
# => 10This can be done easily with the str reverse command.
'Nushell' | str reverse
# => llehsuN
['Nushell' 'is' 'cool'] | str reverse
# => ╭───┬─────────╮
# => │ 0 │ llehsuN │
# => │ 1 │ si │
# => │ 2 │ looc │
# => ╰───┴─────────╯With the parse command you can parse a string into columns. For example:
'Nushell 0.80' | parse '{shell} {version}'
# => ╭───┬─────────┬─────────╮
# => │ # │ shell │ version │
# => ├───┼─────────┼─────────┤
# => │ 0 │ Nushell │ 0.80 │
# => ╰───┴─────────┴─────────╯
'where all data is structured!' | parse --regex '(?P<subject>\w*\s?\w+) is (?P<adjective>\w+)'
# => ╭───┬──────────┬────────────╮
# => │ # │ subject │ adjective │
# => ├───┼──────────┼────────────┤
# => │ 0 │ all data │ structured │
# => ╰───┴──────────┴────────────╯If a string is known to contain comma-separated, tab-separated or multi-space-separated data, you can use from csv, from tsv or from ssv:
"acronym,long\nAPL,A Programming Language" | from csv
# => ╭───┬─────────┬────────────────────────╮
# => │ # │ acronym │ long │
# => ├───┼─────────┼────────────────────────┤
# => │ 0 │ APL │ A Programming Language │
# => ╰───┴─────────┴────────────────────────╯
"name duration\nonestop.mid 4:06" | from ssv
# => ╭───┬─────────────┬──────────╮
# => │ # │ name │ duration │
# => ├───┼─────────────┼──────────┤
# => │ 0 │ onestop.mid │ 4:06 │
# => ╰───┴─────────────┴──────────╯
"rank\tsuit\nJack\tSpades\nAce\tClubs" | from tsv
# => ╭───┬──────┬────────╮
# => │ # │ rank │ suit │
# => ├───┼──────┼────────┤
# => │ 0 │ Jack │ Spades │
# => │ 1 │ Ace │ Clubs │
# => ╰───┴──────┴────────╯In addition to the standard == and != operators, a few operators exist for specifically comparing strings to one another.
Those familiar with Bash and Perl will recognise the regex comparison operators:
'APL' =~ '^\w{0,3}$'
# => true
'FORTRAN' !~ '^\w{0,3}$'
# => trueTwo other operators exist for simpler comparisons:
'JavaScript' starts-with 'Java'
# => true
'OCaml' ends-with 'Caml'
# => trueThere are multiple ways to convert strings to and from other types.
- Using
into string. e.g.123 | into string - Using string interpolation. e.g.
$'(123)'
- Using
into <type>. e.g.'123' | into int
You can color strings with the ansi command. For example:
$'(ansi purple_bold)This text is a bold purple!(ansi reset)'ansi purple_bold makes the text a bold purple
ansi reset resets the coloring to the default.
::: tip
You should always end colored strings with ansi reset
:::
`docs/nushell/nushell.github.io/book/working_with_tables.md`:
```md
# Working with Tables
[[toc]]
## Overview
One of the common ways of seeing data in Nu is through a table. Nu comes with a number of commands for working with tables to make it convenient to find what you're looking for, and for narrowing down the data to just what you need.
To start off, let's get a table that we can use:
```nu
ls
# => ───┬───────────────┬──────┬─────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼───────────────┼──────┼─────────┼────────────
# => 0 │ files.rs │ File │ 4.6 KB │ 5 days ago
# => 1 │ lib.rs │ File │ 330 B │ 5 days ago
# => 2 │ lite_parse.rs │ File │ 6.3 KB │ 5 days ago
# => 3 │ parse.rs │ File │ 49.8 KB │ 1 day ago
# => 4 │ path.rs │ File │ 2.1 KB │ 5 days ago
# => 5 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => 6 │ signature.rs │ File │ 1.2 KB │ 5 days ago
# => ───┴───────────────┴──────┴─────────┴────────────
::: tip Changing how tables are displayed
Nu will try to expands all table's structure by default. You can change this behavior by changing the display_output hook.
See hooks for more information.
:::
We can sort a table by calling the sort-by command and telling it which columns we want to use in the sort. Let's say we wanted to sort our table by the size of the file:
ls | sort-by size
# => ───┬───────────────┬──────┬─────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼───────────────┼──────┼─────────┼────────────
# => 0 │ lib.rs │ File │ 330 B │ 5 days ago
# => 1 │ signature.rs │ File │ 1.2 KB │ 5 days ago
# => 2 │ path.rs │ File │ 2.1 KB │ 5 days ago
# => 3 │ files.rs │ File │ 4.6 KB │ 5 days ago
# => 4 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => 5 │ lite_parse.rs │ File │ 6.3 KB │ 5 days ago
# => 6 │ parse.rs │ File │ 49.8 KB │ 1 day ago
# => ───┴───────────────┴──────┴─────────┴────────────We can sort a table by any column that can be compared. For example, we could also have sorted the above using the "name", "accessed", or "modified" columns.
For more info on sorting, see Sorting.
::: tip Note The following is a basic overview. For a more in-depth discussion of this topic, see the chapter, Navigating and Accessing Structured Data. :::
We can select data from a table by choosing to select specific columns or specific rows. Let's select a few columns from our table to use:
ls | select name size
# => ───┬───────────────┬─────────
# => # │ name │ size
# => ───┼───────────────┼─────────
# => 0 │ files.rs │ 4.6 KB
# => 1 │ lib.rs │ 330 B
# => 2 │ lite_parse.rs │ 6.3 KB
# => 3 │ parse.rs │ 49.8 KB
# => 4 │ path.rs │ 2.1 KB
# => 5 │ shapes.rs │ 4.7 KB
# => 6 │ signature.rs │ 1.2 KB
# => ───┴───────────────┴─────────This helps to create a table that's more focused on what we need. Next, let's say we want to only look at the 5 smallest files in this directory:
ls | sort-by size | first 5
# => ───┬──────────────┬──────┬────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼──────────────┼──────┼────────┼────────────
# => 0 │ lib.rs │ File │ 330 B │ 5 days ago
# => 1 │ signature.rs │ File │ 1.2 KB │ 5 days ago
# => 2 │ path.rs │ File │ 2.1 KB │ 5 days ago
# => 3 │ files.rs │ File │ 4.6 KB │ 5 days ago
# => 4 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => ───┴──────────────┴──────┴────────┴────────────You'll notice we first sort the table by size to get to the smallest file, and then we use the first 5 to return the first 5 rows of the table.
You can also skip rows that you don't want. Let's skip the first two of the 5 rows we returned above:
ls | sort-by size | first 5 | skip 2
# => ───┬───────────┬──────┬────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼───────────┼──────┼────────┼────────────
# => 0 │ path.rs │ File │ 2.1 KB │ 5 days ago
# => 1 │ files.rs │ File │ 4.6 KB │ 5 days ago
# => 2 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => ───┴───────────┴──────┴────────┴────────────We've narrowed it to three rows we care about.
Let's look at a few other commands for selecting data. You may have wondered why the rows of the table are numbers. This acts as a handy way to get to a single row. Let's sort our table by the file name and then pick one of the rows with the select command using its row number:
ls | sort-by name
# => ───┬───────────────┬──────┬─────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼───────────────┼──────┼─────────┼────────────
# => 0 │ files.rs │ File │ 4.6 KB │ 5 days ago
# => 1 │ lib.rs │ File │ 330 B │ 5 days ago
# => 2 │ lite_parse.rs │ File │ 6.3 KB │ 5 days ago
# => 3 │ parse.rs │ File │ 49.8 KB │ 1 day ago
# => 4 │ path.rs │ File │ 2.1 KB │ 5 days ago
# => 5 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => 6 │ signature.rs │ File │ 1.2 KB │ 5 days ago
# => ───┴───────────────┴──────┴─────────┴────────────
ls | sort-by name | select 5
# => ───┬───────────────┬──────┬─────────┬────────────
# => # │ name │ type │ size │ modified
# => ───┼───────────────┼──────┼─────────┼────────────
# => 0 │ shapes.rs │ File │ 4.7 KB │ 5 days ago
# => ───┴───────────────┴──────┴─────────┴────────────So far, we've worked with tables by trimming the table down to only what we need. Sometimes we may want to go a step further and only look at the values in the cells themselves rather than taking a whole column. Let's say, for example, we wanted to only get a list of the names of the files. For this, we use the get command:
ls | get name
# => ───┬───────────────
# => 0 │ files.rs
# => 1 │ lib.rs
# => 2 │ lite_parse.rs
# => 3 │ parse.rs
# => 4 │ path.rs
# => 5 │ shapes.rs
# => 6 │ signature.rs
# => ───┴───────────────We now have the values for each of the filenames.
This might look like the select command we saw earlier, so let's put that here as well to compare the two:
ls | select name
# => ───┬───────────────
# => # │ name
# => ───┼───────────────
# => 0 │ files.rs
# => 1 │ lib.rs
# => 2 │ lite_parse.rs
# => 3 │ parse.rs
# => 4 │ path.rs
# => 5 │ shapes.rs
# => 6 │ signature.rs
# => ───┴───────────────These look very similar! Let's see if we can spell out the difference between these two commands to make it clear:
select- creates a new table which includes only the columns specifiedget- returns the values inside the column specified as a list
:::tip
The arguments provided to select and get are cell-paths, a fundamental part of Nu's query language. For a more in-depth discussion of cell-paths and other navigation topics, see the next chapter, Navigating and Accessing Structured Data.
:::
In addition to selecting data from a table, we can also update what the table has. We may want to combine tables, add new columns, or edit the contents of a cell. In Nu, rather than editing in place, each of the commands in the section will return a new table in the pipeline.
We can concatenate tables using append:
let first = [[a b]; [1 2]]
let second = [[a b]; [3 4]]
$first | append $second
# => ───┬───┬───
# => # │ a │ b
# => ───┼───┼───
# => 0 │ 1 │ 2
# => 1 │ 3 │ 4
# => ───┴───┴───If the column names are not identical then additionally columns and values will be created as necessary:
let first = [[a b]; [1 2]]
let second = [[a b]; [3 4]]
let third = [[a c]; [3 4]]
$first | append $second | append $third
# => ───┬───┬────┬────
# => # │ a │ b │ c
# => ───┼───┼────┼────
# => 0 │ 1 │ 2 │ ❎
# => 1 │ 3 │ 4 │ ❎
# => 2 │ 3 │ ❎ │ 4
# => ───┴───┴────┴────You can also use the ++ operator as an inline replacement for append:
$first ++ $second ++ $third
# => ───┬───┬────┬────
# => # │ a │ b │ c
# => ───┼───┼────┼────
# => 0 │ 1 │ 2 │ ❎
# => 1 │ 3 │ 4 │ ❎
# => 2 │ 3 │ ❎ │ 4
# => ───┴───┴────┴───We can use the merge command to merge two (or more) tables together
let first = [[a b]; [1 2]]
let second = [[c d]; [3 4]]
$first | merge $second
# => ───┬───┬───┬───┬───
# => # │ a │ b │ c │ d
# => ───┼───┼───┼───┼───
# => 0 │ 1 │ 2 │ 3 │ 4
# => ───┴───┴───┴───┴───Let's add a third table:
let third = [[e f]; [5 6]]We could join all three tables together like this:
$first | merge $second | merge $third
# => ───┬───┬───┬───┬───┬───┬───
# => # │ a │ b │ c │ d │ e │ f
# => ───┼───┼───┼───┼───┼───┼───
# => 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6
# => ───┴───┴───┴───┴───┴───┴───Or we could use the reduce command to dynamically merge all tables:
[$first $second $third] | reduce {|elt, acc| $acc | merge $elt }
# => ───┬───┬───┬───┬───┬───┬───
# => # │ a │ b │ c │ d │ e │ f
# => ───┼───┼───┼───┼───┼───┼───
# => 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6
# => ───┴───┴───┴───┴───┴───┴───We can use the insert command to add a new column to the table. Let's look at an example:
open rustfmt.toml
# => ─────────┬──────
# => edition │ 2018
# => ─────────┴──────Let's add a column called "next_edition" with the value 2021:
open rustfmt.toml | insert next_edition 2021
# => ──────────────┬──────
# => edition │ 2018
# => next_edition │ 2021
# => ──────────────┴──────This visual may be slightly confusing, because it looks like what we've just done is add a row. In this case, remember: rows have numbers, columns have names. If it still is confusing, note that appending one more row will make the table render as expected:
open rustfmt.toml | insert next_edition 2021 | append {edition: 2021 next_edition: 2024}
# => ───┬─────────┬──────────────
# => # │ edition │ next_edition
# => ───┼─────────┼──────────────
# => 0 │ 2018 │ 2021
# => 1 │ 2021 │ 2024
# => ───┴─────────┴──────────────Notice that if we open the original file, the contents have stayed the same:
open rustfmt.toml
# => ─────────┬──────
# => edition │ 2018
# => ─────────┴──────Changes in Nu are functional changes, meaning that they work on values themselves rather than trying to cause a permanent change. This lets us do many different types of work in our pipeline until we're ready to write out the result with any changes we'd like if we choose to. Here we could write out the result using the save command:
open rustfmt.toml | insert next_edition 2021 | save rustfmt2.toml
open rustfmt2.toml
# => ──────────────┬──────
# => edition │ 2018
# => next_edition │ 2021
# => ──────────────┴──────In a similar way to the insert command, we can also use the update command to change the contents of a column to a new value. To see it in action let's open the same file:
open rustfmt.toml
# => ─────────┬──────
# => edition │ 2018
# => ─────────┴──────And now, let's update the edition to point at the next edition we hope to support:
open rustfmt.toml | update edition 2021
# => ─────────┬──────
# => edition │ 2021
# => ─────────┴──────You can also use the upsert command to insert or update depending on whether the column already exists.
You can use move to move columns in the table. For example, if we wanted to move the "name" column from ls after the "size" column, we could do:
ls | move name --after size
# => ╭────┬──────┬─────────┬───────────────────┬──────────────╮
# => │ # │ type │ size │ name │ modified │
# => ├────┼──────┼─────────┼───────────────────┼──────────────┤
# => │ 0 │ dir │ 256 B │ Applications │ 3 days ago │
# => │ 1 │ dir │ 256 B │ Data │ 2 weeks ago │
# => │ 2 │ dir │ 448 B │ Desktop │ 2 hours ago │
# => │ 3 │ dir │ 192 B │ Disks │ a week ago │
# => │ 4 │ dir │ 416 B │ Documents │ 4 days ago │
# => ...You can also rename columns in a table by passing it through the rename command. If we wanted to run ls and rename the columns, we can use this example:
ls | rename filename filetype filesize date
# => ╭────┬───────────────────┬──────────┬──────────┬──────────────╮
# => │ # │ filename │ filetype │ filesize │ date │
# => ├────┼───────────────────┼──────────┼──────────┼──────────────┤
# => │ 0 │ Applications │ dir │ 256 B │ 3 days ago │
# => │ 1 │ Data │ dir │ 256 B │ 2 weeks ago │
# => │ 2 │ Desktop │ dir │ 448 B │ 2 hours ago │
# => │ 3 │ Disks │ dir │ 192 B │ a week ago │
# => │ 4 │ Documents │ dir │ 416 B │ 4 days ago │
# => ...You can also reject columns in a table by passing it through the reject command. If we wanted to run ls and delete the columns, we can use this example:
ls -l / | reject readonly num_links inode created accessed modified
# => ╭────┬────────┬─────────┬─────────┬───────────┬──────┬───────┬────────╮
# => │ # │ name │ type │ target │ mode │ uid │ group │ size │
# => ├────┼────────┼─────────┼─────────┼───────────┼──────┼───────┼────────┤
# => │ 0 │ /bin │ symlink │ usr/bin │ rwxrwxrwx │ root │ root │ 7 B │
# => │ 1 │ /boot │ dir │ │ rwxr-xr-x │ root │ root │ 1.0 KB │
# => │ 2 │ /dev │ dir │ │ rwxr-xr-x │ root │ root │ 4.1 KB │
# => │ 3 │ /etc │ dir │ │ rwxr-xr-x │ root │ root │ 3.6 KB │
# => │ 4 │ /home │ dir │ │ rwxr-xr-x │ root │ root │ 12 B │
# => │ 5 │ /lib │ symlink │ usr/lib │ rwxrwxrwx │ root │ root │ 7 B │
# => │ 6 │ /lib64 │ symlink │ usr/lib │ rwxrwxrwx │ root │ root │ 7 B │
# => │ 7 │ /mnt │ dir │ │ rwxr-xr-x │ root │ root │ 0 B │
# => ...You've noticed that every table, by default, starts with a column with the heading #. This column can operate in one of two modes:
-
Internal #
- The default mode
- Nushell provides a 0-based, consecutive index
- Always corresponds to the cell-path row-number, where
select 0will return the first item in the list, andselect <n-1>returns the nth item - Is a display of an internal representation only. In other words, it is not accessible by column name. For example,
get indexwill not work, norget #
-
"Index"-Renamed #
-
When a column named "index" is created, either directly or as a side-effect of another command, then this
indexcolumn takes the place of the#column in the table display. In the table output, the column header is still#, but the name of the column is nowindex.Example:
ls | each { insert index { 1000 }} | first 5 # => ╭──────┬─────────────────┬──────┬─────────┬──────────────╮ # => │ # │ name │ type │ size │ modified │ # => ├──────┼─────────────────┼──────┼─────────┼──────────────┤ # => │ 1000 │ CNAME │ file │ 15 B │ 9 months ago │ # => │ 1000 │ CONTRIBUTING.md │ file │ 4.3 KiB │ 9 hours ago │ # => │ 1000 │ LICENSE │ file │ 1.0 KiB │ 9 months ago │ # => │ 1000 │ README.md │ file │ 2.2 KiB │ 3 weeks ago │ # => │ 1000 │ assets │ dir │ 4.0 KiB │ 9 months ago │ # => ╰──────┴─────────────────┴──────┴─────────┴──────────────╯
- If an
indexkey is added to each row in the table, then it can be accessed viaselectandget:
ls | each { insert index { 1000 }} | first 5 | select index name # => ╭──────┬─────────────────╮ # => │ # │ name │ # => ├──────┼─────────────────┤ # => │ 1000 │ CNAME │ # => │ 1000 │ CONTRIBUTING.md │ # => │ 1000 │ LICENSE │ # => │ 1000 │ README.md │ # => │ 1000 │ assets │ # => ╰──────┴─────────────────╯
-
On the other hand, if some rows have an
indexkey and others don't, the result is no longer a table; it is alist<any>due to the different record types:ls | upsert 3.index { "--->" } | first 5 # => ╭──────┬─────────────────┬──────┬─────────┬──────────────╮ # => │ # │ name │ type │ size │ modified │ # => ├──────┼─────────────────┼──────┼─────────┼──────────────┤ # => │ 0 │ CNAME │ file │ 15 B │ 9 months ago │ # => │ 1 │ CONTRIBUTING.md │ file │ 4.3 KiB │ 9 hours ago │ # => │ 2 │ LICENSE │ file │ 1.0 KiB │ 9 months ago │ # => │ ---> │ README.md │ file │ 2.2 KiB │ 3 weeks ago │ # => │ 4 │ assets │ dir │ 4.0 KiB │ 9 months ago │ # => ╰──────┴─────────────────┴──────┴─────────┴──────────────╯ ls | upsert 3.index { "--->" } | first 5 | describe # => list<any> (stream) ls | upsert 3.index { "--->" } | select index name # Error: cannot find column 'index' ls | upsert 3.index { "--->" } | select index? name | first 5 # => ╭──────┬─────────────────╮ # => │ # │ name │ # => ├──────┼─────────────────┤ # => │ │ CNAME │ # => │ │ CONTRIBUTING.md │ # => │ │ LICENSE │ # => │ ---> │ README.md │ # => │ │ assets │ # => ╰──────┴─────────────────╯
- If an
-
As demonstrated in the example above, any rows (records) in the table without an
indexkey will continue to display the internal representation.
-
A useful pattern for converting an internal # into an index for all rows, while maintaining the original numbering, is:
ls | enumerate | flattenWhile the results look the same, the index is now decoupled from the internal cell-path. For example:
ls | enumerate | flatten | sort-by modified | first 5
# => ╭────┬──────────────┬──────┬─────────┬──────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├────┼──────────────┼──────┼─────────┼──────────────┤
# => │ 0 │ CNAME │ file │ 15 B │ 9 months ago │
# => │ 2 │ LICENSE │ file │ 1.0 KiB │ 9 months ago │
# => │ 4 │ assets │ dir │ 4.0 KiB │ 9 months ago │
# => │ 17 │ lefthook.yml │ file │ 1.1 KiB │ 9 months ago │
# => │ 24 │ snippets │ dir │ 4.0 KiB │ 9 months ago │
# => ╰────┴──────────────┴──────┴─────────┴──────────────╯
ls | enumerate | flatten | sort-by modified | select 4
# => ╭────┬──────────┬──────┬─────────┬──────────────╮
# => │ # │ name │ type │ size │ modified │
# => ├────┼──────────┼──────┼─────────┼──────────────┤
# => │ 24 │ snippets │ dir │ 4.0 KiB │ 9 months ago │
# => ╰────┴──────────┴──────┴─────────┴──────────────╯The sort-by modified now also sorts the index along with the rest of the columns.
let table = [
[additions deletions delta ];
[ 10 20 -10 ]
[ 15 5 10 ]
[ 8 6 2 ]]
let totals_row = ($table | math sum | insert index {"Totals"})
$table | append $totals_row
# => ╭────────┬───────────┬───────────┬───────╮
# => │ # │ additions │ deletions │ delta │
# => ├────────┼───────────┼───────────┼───────┤
# => │ 0 │ 10 │ 20 │ -10 │
# => │ 1 │ 15 │ 5 │ 10 │
# => │ 2 │ 8 │ 6 │ 2 │
# => │ Totals │ 33 │ 31 │ 2 │
# => ╰────────┴───────────┴───────────┴───────╯The table command is used to render structured data. This includes:
- Tables
- Lists
- Records
- Ranges
Perhaps contrary to initial assumptions, the result of rendering these types is a string. For example:
[ Nagasaki Ghent Cambridge Izmir Graz Lubango ] | table | describe
# => string (stream)Other data types are passed through the table command unchanged.
With no arguments, the output rendered from the table command will often display the same as unrendered form. For example:
[ Nagasaki Ghent Cambridge Izmir Graz Lubango ]
# => ╭───┬───────────╮
# => │ 0 │ Nagasaki │
# => │ 1 │ Ghent │
# => │ 2 │ Cambridge │
# => │ 3 │ Izmir │
# => │ 4 │ Graz │
# => │ 5 │ Lubango │
# => ╰───┴───────────╯
[ Nagasaki Ghent Cambridge Izmir Graz Lubango ] | table
# => ╭───┬───────────╮
# => │ 0 │ Nagasaki │
# => │ 1 │ Ghent │
# => │ 2 │ Cambridge │
# => │ 3 │ Izmir │
# => │ 4 │ Graz │
# => │ 5 │ Lubango │
# => ╰───┴───────────╯This can be useful when you need to store the rendered view of structured data as a string. For example, to remove all ANSI formatting (colors) from a table:
ls | table | ansi stripThe table command also has multiple options for changing the rendering of a table, such as:
-eto expand data that would normally be collapsed when rendering. Comparescope modules | tabletoscope modules | table -e.-i falseto hide theindex/#column-a 5to abbreviate the table to just the first and last 5 entries- And more
`docs/nushell/nushell.github.io/commands/README.md`:
```md
# Command Reference
If you're new to Nushell, [the quick tour](/book/quick_tour.md) can show you the most important commands. You don't need to know them all!
To see all commands from inside Nushell, run [`help commands`](/commands/docs/help.md).
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.indexOf('/commands/docs/') >= 0)
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<tr>
<th>Command</th>
<th>Categories</th>
<th>Description</th>
<th>Feature</th>
</tr>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.categories }}</td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</table>
docs/nushell/nushell.github.io/commands/categories/bits.md:
---
editLink: false
contributors: false
---
# Bits
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('bits'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/bytes.md:
---
editLink: false
contributors: false
---
# Bytes
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('bytes'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/chart.md:
---
editLink: false
contributors: false
---
# Chart
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('chart'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/conversions.md:
---
editLink: false
contributors: false
---
# Conversions
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('conversions'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/core.md:
---
editLink: false
contributors: false
---
# Core
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('core'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/database.md:
---
editLink: false
contributors: false
---
# Database
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('database'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/dataframe.md:
---
editLink: false
contributors: false
---
# Dataframe
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('dataframe'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/dataframe_or_lazyframe.md:
---
editLink: false
contributors: false
---
# Dataframe Or Lazyframe
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('dataframe or lazyframe'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/date.md:
---
editLink: false
contributors: false
---
# Date
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('date'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/debug.md:
---
editLink: false
contributors: false
---
# Debug
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('debug'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/default.md:
---
editLink: false
contributors: false
---
# Default
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('default'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/deprecated.md:
---
editLink: false
contributors: false
---
# Deprecated
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('deprecated'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/env.md:
---
editLink: false
contributors: false
---
# Env
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('env'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/experimental.md:
---
editLink: false
contributors: false
---
# Experimental
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('experimental'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/expression.md:
---
editLink: false
contributors: false
---
# Expression
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('expression'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/filesystem.md:
---
editLink: false
contributors: false
---
# Filesystem
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('filesystem'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/filters.md:
---
editLink: false
contributors: false
---
# Filters
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('filters'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/formats.md:
---
editLink: false
contributors: false
---
# Formats
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('formats'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/generators.md:
---
editLink: false
contributors: false
---
# Generators
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('generators'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/hash.md:
---
editLink: false
contributors: false
---
# Hash
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('hash'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/history.md:
---
editLink: false
contributors: false
---
# History
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('history'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/lazyframe.md:
---
editLink: false
contributors: false
---
# Lazyframe
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('lazyframe'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/math.md:
---
editLink: false
contributors: false
---
# Math
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('math'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/misc.md:
---
editLink: false
contributors: false
---
# Misc
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('misc'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/network.md:
---
editLink: false
contributors: false
---
# Network
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('network'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/path.md:
---
editLink: false
contributors: false
---
# Path
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('path'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/platform.md:
---
editLink: false
contributors: false
---
# Platform
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('platform'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/plugin.md:
---
editLink: false
contributors: false
---
# Plugin
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('plugin'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/prompt.md:
---
editLink: false
contributors: false
---
# Prompt
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('prompt'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/random.md:
---
editLink: false
contributors: false
---
# Random
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('random'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/removed.md:
---
editLink: false
contributors: false
---
# Removed
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('removed'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/shells.md:
---
editLink: false
contributors: false
---
# Shells
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('shells'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/strings.md:
---
editLink: false
contributors: false
---
# Strings
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('strings'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/system.md:
---
editLink: false
contributors: false
---
# System
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('system'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/categories/viewers.md:
---
editLink: false
contributors: false
---
# Viewers
<script>
import pages from '@temp/pages'
export default {
computed: {
commands() {
return pages
.filter(p => p.path.includes('/commands/docs/'))
.filter(p => p.frontmatter.categories.includes('viewers'))
.sort((a,b) => (a.title > b.title) ? 1 : ((b.title > a.title) ? -1 : 0));
}
}
}
</script>
<table>
<thead>
<tr>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr v-for="command in commands">
<td><a :href="$withBase(command.path)">{{ command.title }}</a></td>
<td style="white-space: pre-wrap;">{{ command.frontmatter.usage }}</td>
</tr>
</tbody>
</table>
docs/nushell/nushell.github.io/commands/docs/alias.md:
---
title: alias
categories: |
core
version: 0.107.0
core: |
Alias a command (with optional flags) to a new name.
usage: |
Alias a command (with optional flags) to a new name.
editLink: false
contributors: false
---
<!-- This file is automatically generated. Please edit the command in https://github.com/nushell/nushell instead. -->
# `alias` for [core](/commands/categories/core.md)
<div class='command-title'>Alias a command (with optional flags) to a new name.</div>
## Signature
```> alias {flags} (name) (initial_value)```
## Parameters
- `name`: Name of the alias.
- `initial_value`: Equals sign followed by value.
## Input/output types:
| input | output |
| ------- | ------- |
| nothing | nothing |
## Examples
Alias ll to ls -l
```nu
> alias ll = ls -l
This command is a parser keyword. For details, check: https://www.nushell.sh/book/thinking_in_nu.html
`docs/nushell/nushell.github.io/commands/docs/all.md`:
```md
---
title: all
categories: |
filters
version: 0.107.0
filters: |
Test if every element of the input fulfills a predicate expression.
usage: |
Test if every element of the input fulfills a predicate expression.
editLink: false
contributors: false
---
<!-- This file is automatically generated. Please edit the command in https://github.com/nushell/nushell instead. -->
# `all` for [filters](/commands/categories/filters.md)
<div class='command-title'>Test if every element of the input fulfills a predicate expression.</div>
## Signature
```> all {flags} (predicate)```
## Parameters
- `predicate`: A closure that must evaluate to a boolean.




