Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 107 additions & 31 deletions book/moving_around.md
Original file line number Diff line number Diff line change
@@ -1,41 +1,117 @@
# Moving around your system
# Moving around the system

Early shells allow you to move around your filesystem and run commands, and modern shells like Nu allow you to do the same. Let's take a look at some of the common commands you might use when interacting with your 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

@[code](@snippets/moving_around/ls_example.sh)

As we've seen in other chapters, [`ls`](/commands/docs/ls.md) is a command for viewing the contents of a path. Nu will return the contents as a table that we can use.
As seen in other chapters, the [`ls`](/commands/docs/ls.md) command returns the contents of a directory. Nushell's `ls` will return the contents as a [table](types_of_data.html#tables).

The [`ls`](/commands/docs/ls.md) 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"
The [`ls`](/commands/docs/ls.md) 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"

@[code](@snippets/moving_around/ls_shallow_glob_example.sh)

## Glob patterns (wildcards)

The asterisk (\*) in the above optional argument "\*.md" is sometimes called a wildcard or a glob. It lets us match anything. You could read the glob "\*.md" as "match any filename, so long as it ends with '.md' "
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*`.

In Nushell, we also support double `*` to talk about traversing deeper paths that are nested inside of other directories. For example, `ls **/*` will list all the non-hidden paths nested under the current directory.
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.

@[code](@snippets/moving_around/ls_deep_glob_example.sh)
```nu
ls **/*.md
╭───┬───────────────────────────────┬──────┬──────────┬──────────────╮
│ # │ name │ type │ size │ modified │
├───┼───────────────────────────────┼──────┼──────────┼──────────────┤
│ 0 │ CODE_OF_CONDUCT.md │ file │ 3.4 KiB │ 5 months ago │
│ 1 │ CONTRIBUTING.md │ file │ 11.0 KiB │ a month ago │
│ 2 │ README.md │ file │ 12.0 KiB │ a month ago │
│ 3 │ SECURITY.md │ file │ 2.6 KiB │ 5 hours ago │
│ 4 │ benches/README.md │ file │ 249 B │ 2 months ago │
│ 5 │ crates/README.md │ file │ 795 B │ 5 months ago │
│ 6 │ crates/nu-cli/README.md │ file │ 388 B │ 5 hours ago │
│ 7 │ crates/nu-cmd-base/README.md │ file │ 262 B │ 5 hours ago │
│ 8 │ crates/nu-cmd-extra/README.md │ file │ 669 B │ 2 months ago │
│ 9 │ crates/nu-cmd-lang/README.md │ file │ 1.5 KiB │ a month ago │
╰───┴───────────────────────────────┴──────┴──────────┴──────────────╯
```

Here, we're looking for any file that ends with ".md", and the two asterisks further say "in any directory starting from here".
Here, we're looking for any file that ends with ".md". The double-asterisks further specify _"in any directory starting from here."_

In other shells (like bash), glob expansion happens in the shell and the invoked program (`ls` in the example above) receives a list of matched files. In Nushell however, the string you enter is passed "as is" to the command, and some commands (like `ls`, `mv`, `cp` and `rm`) interpret their input string as a glob pattern. For example the [`ls` command's help page](https://www.nushell.sh/commands/docs/ls.html) shows that it takes the parameter: `pattern: the glob pattern to use (optional)`.
Nushell's globbing syntax not only supports `*`, but also matching [single characters with `?` and character groups with `[...]`](https://docs.rs/nu-glob/latest/nu_glob/struct.Pattern.html).

Globbing syntax in these commands not only supports `*`, but also matching [single characters with `?` and character groups with `[...]`](https://docs.rs/nu-glob/latest/nu_glob/struct.Pattern.html). Note that this is a more limited syntax than what the dedicated [`glob` Nushell command](https://www.nushell.sh/commands/docs/glob.html) supports.
Escaping the `*`, `?`, and `[]` patterns works by enclosing them in a single-quoted, double-quoted, or
[raw string](working_with_strings.md#raw-strings). For example, to show the contents of a directory named
`[slug]`, use `ls "[slug]"` or `ls '[slug]'`.

Escaping `*`, `?`, `[]` works by quoting them with single quotes or double quotes. To show the contents of a directory named `[slug]`, use `ls "[slug]"` or `ls '[slug]'`.
Note that backtick quote doesn't escape glob, for example: <code>cp \`test dir/\*\`</code> will copy all files inside `test dir` to current directory.
However, _backtick_ quoted strings do not escape globs. For example, compare the following scenarios:

If you pass a variable to a command that support globbing like this: `let f = "a[bc]d.txt"; rm $f`. It won't expand the glob pattern, only a file named `a[bc]d.txt` will be removed. Normally it's what you want, but if you want to expand the glob pattern, there are 3 ways to achieve it:
1. Unquoted: Glob pattern

1. using spread operator along with `glob` command: `let f = "a[bc]d.txt"; rm ...(glob $f)`. This way is recommended because it's expressed most explicitly, but it doesn't work with `ls` and `du` command, for the case, you can
2. using `into glob` command: `let f = "a[bc]d.txt"; ls ($f | into glob)`. It's useful for `ls` and `du` commands.
3. annotate variable with `glob` type: `let f: glob = "a[bc]d.txt"; rm $f`. It's simple to write, but doesn't work with external command like `^rm $f`.
An unquoted [bare word string](working_with_strings.html#bare-word-strings) with glob characters is interpreted as a glob pattern, so the following will remove all files in the current directory that contain
`myfile` as any part of the filename:

```nu
rm *myfile*
```

2. Quoted: String literal with asterisks

When quoting with single or double quotes, or using a [raw string](working_with_strings.html#raw-strings), 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 with `myfile` in the name are not affected:

```nu
rm "*myfile*"
```

3. Backtick-quoted: Glob pattern

Asterisks (and other glob patterns) within a [backtick-quoted string](working_with_strings.html#backtick-quoted-strings) 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 `myfile` as part of the filename

```nu
rm `*myfile*`
```

::: tip
Nushell also includes a dedicated [`glob` command](https://www.nushell.sh/commands/docs/glob.html) with support for more complex globbing scenarios.
:::

### Converting strings to globs

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:

1. `into glob`

The [`into glob` command](/commands/docs/into_glob.html) can be used to convert a string (and other types) into a glob. For instance:

```nu
# 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
```

2. The spread operator combined with the [`glob` command](/commands/docs/glob.html):

The [`glob` command](/commands/docs/glob.html) (note: not the same as `into glob`) produces a [`list`](types_of_data.html#lists) of filenames that match the glob pattern. This list can be expanded and passed to filesystem commands using the [spread operator](operators.html#spread-operator):

```nu
# 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)*")
```

3. Force `glob` type via annotation:

```nu
# 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
```

## Changing the current directory

Expand All @@ -53,6 +129,10 @@ You can also add additional dots to go up additional directory levels:

@[code](@snippets/book/moving_around/multiple_cd_levels.nu)

::: tip
Multi-dot shortcuts are available to both internal Nushell [filesystem commands](//commands/categories/filesystem.html) 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:

@[code](@snippets/book/moving_around/relative_cd_levels.nu)
Expand All @@ -63,22 +143,18 @@ Changing the directory with [`cd`](/commands/docs/cd.md) changes the `PWD` envir

## Filesystem commands

Nu also provides some basic filesystem commands that work cross-platform.

We can move an item from one place to another using the [`mv`](/commands/docs/mv.md) command:

@[code](@snippets/moving_around/mv_example.sh)

We can copy an item from one location to another with the [`cp`](/commands/docs/cp.md) command:

@[code](@snippets/moving_around/cp_example.sh)

We can remove an item with the [`rm`](/commands/docs/rm.md) command:
Nu also provides some basic [filesystem commands](/commands/categories/filesystem.html) that work cross-platform such as:

@[code](@snippets/moving_around/rm_example.sh)
- [`mv`](/commands/docs/mv.md) to rename or move a file or directory to a new location
- [`cp`](/commands/docs/cp.md) to copy an item to a new location
- [`rm`](/commands/docs/rm.md) to remove items from the filesystem
- [`mkdir`](/commands/docs/mkdir.md) to create a new directory

The three commands also can use the glob capabilities we saw earlier with [`ls`](/commands/docs/ls.md).
::: 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:

Finally, we can create a new directory using the [`mkdir`](/commands/docs/mkdir.md) command:
- 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](quick_tour.html), they are documented in the Nushell help system. Running `help <command>` or `<command> --help` will display the Nushell documentation for the command.

@[code](@snippets/moving_around/mkdir_example.sh)
While the use of the Nushell built-in versions is typically recommended, it is possible to access the Linux binaries. See [Escaping to system](escaping.html#escaping-to-the-system) for details.