You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit ca4ed42
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: book/externs.md
+163Lines changed: 163 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -58,6 +58,169 @@ export extern "git add" [
58
58
]
59
59
```
60
60
61
+
## Structured Output
62
+
63
+
Many external commands print machine-readable output when you pass a certain flag, such as `git status --porcelain` or `pip list --format=json`. Getting data from them means remembering that flag and the command that parses its output, every time. The [`@structured`](/commands/docs/attr_structured.md) attribute puts both in the `extern` declaration, so you write them once:
- A list of arguments that make the command print machine-readable output. Each item in the list is one argument, so `[--porcelain]` adds one argument and `[-o json]` adds two, `-o` and `json`. Quote an item that contains spaces or `|`. Otherwise Nushell splits it into several items.
73
+
- A block that turns that output into data. The block receives the command's output as `$in`.
74
+
75
+
With this declaration, `git status` still prints git's usual text when you run it by itself:
76
+
77
+
```nu
78
+
git status
79
+
# => On branch main
80
+
# => Changes to be committed:
81
+
# => (use "git restore --staged <file>..." to unstage)
82
+
# => new file: INSTALL.md
83
+
# =>
84
+
# => Changes not staged for commit:
85
+
# => (use "git add <file>..." to update what will be committed)
86
+
# => (use "git restore <file>..." to discard changes in working directory)
87
+
# => modified: README.md
88
+
# =>
89
+
# => Untracked files:
90
+
# => (use "git add <file>..." to include in what will be committed)
91
+
# => notes.txt
92
+
```
93
+
94
+
Pipe it into a Nushell command, and Nushell runs `git status --porcelain` instead and passes the output through the block. You get a table:
95
+
96
+
```nu
97
+
git status | where status =~ M
98
+
# => ╭───┬────────┬───────────╮
99
+
# => │ # │ status │ file │
100
+
# => ├───┼────────┼───────────┤
101
+
# => │ 0 │ M │ README.md │
102
+
# => ╰───┴────────┴───────────╯
103
+
```
104
+
105
+
You don't need a filter to get the table. When you want all of the data, pipe the command into [`collect`](/commands/docs/collect.md). It's an internal command, so the output gets decoded, and it passes its input through unchanged:
106
+
107
+
```nu
108
+
git status | collect
109
+
# => ╭───┬────────┬────────────╮
110
+
# => │ # │ status │ file │
111
+
# => ├───┼────────┼────────────┤
112
+
# => │ 0 │ A │ INSTALL.md │
113
+
# => │ 1 │ M │ README.md │
114
+
# => │ 2 │ ?? │ notes.txt │
115
+
# => ╰───┴────────┴────────────╯
116
+
```
117
+
118
+
Saving the output in a variable decodes it too:
119
+
120
+
```nu
121
+
let changes = git status
122
+
$changes | get file
123
+
# => ╭───┬────────────╮
124
+
# => │ 0 │ INSTALL.md │
125
+
# => │ 1 │ README.md │
126
+
# => │ 2 │ notes.txt │
127
+
# => ╰───┴────────────╯
128
+
```
129
+
130
+
When a command can print JSON, the block only needs [`from json`](/commands/docs/from_json.md):
131
+
132
+
```nu
133
+
@structured [--format=json] { from json }
134
+
extern "pip list" []
135
+
136
+
pip list | first 3
137
+
# => ╭───┬───────────┬─────────╮
138
+
# => │ # │ name │ version │
139
+
# => ├───┼───────────┼─────────┤
140
+
# => │ 0 │ contourpy │ 1.4.0 │
141
+
# => │ 1 │ cycler │ 0.12.1 │
142
+
# => │ 2 │ fonttools │ 4.66.1 │
143
+
# => ╰───┴───────────┴─────────╯
144
+
```
145
+
146
+
### When Nushell Decodes the Output
147
+
148
+
Nushell decides by looking at where the output of the extern goes. It adds the `@structured` arguments and runs the block when Nushell itself uses the output:
149
+
150
+
- Piped into an internal command: `git status | where status =~ M`, or `git status | collect` to get all of the data
151
+
- Saved in a variable: `let changes = git status`
152
+
- Used in a subexpression: `(git status).file`
153
+
154
+
In every other case, the command runs exactly as you typed it. That includes output that goes:
155
+
156
+
- To the terminal: `git status`
157
+
- Through `print`: `git status | print`
158
+
- To a file: `git status o> status.txt` or `git status | save status.txt`
159
+
- Into `complete`, `tee`, or `ignore`: `git status | complete`
160
+
- Into another external command or `run-external`: `git status | ^less`
161
+
- Into the same pipe as stderr: `git status o+e>| str trim`
162
+
163
+
The same rules apply when the extern is the last command of a custom command or a `do` block. After `def gs [] { git status }`, running `gs` prints git's text, and `gs | length` returns `3`, the number of changed files.
164
+
165
+
::: tip
166
+
Every internal command counts, including commands that work on text. `git status | lines` sends the decoded table to `lines`, not git's text. To get the text, call the command with the caret sigil, which always runs it as written:
167
+
168
+
```nu
169
+
^git status | lines | first 1
170
+
# => ╭───┬────────────────╮
171
+
# => │ 0 │ On branch main │
172
+
# => ╰───┴────────────────╯
173
+
```
174
+
175
+
:::
176
+
177
+
### Adding Your Own Arguments
178
+
179
+
Nushell puts the `@structured` arguments right after the command name, before any arguments you type. This extern gives `git log` an output format that is easy to split into columns:
Because your arguments come last, a flag that changes the output format replaces the one from `@structured`. The block then gets output it wasn't written for. Here `--oneline` wins, so each whole line lands in the `hash` column:
197
+
198
+
```nu
199
+
git log --oneline -n 1 | first
200
+
# => ╭──────┬──────────────────────────╮
201
+
# => │ hash │ 73e9a8f Print a greeting │
202
+
# => ╰──────┴──────────────────────────╯
203
+
```
204
+
205
+
To use a different format, call the command with the caret sigil, such as `^git log --oneline`.
206
+
207
+
### More About `@structured`
208
+
209
+
- The block can read `$env`, constants, and `let` variables defined before the `extern`, just like the body of a `def`. It can't use `mut` variables.
210
+
-`@structured` only works on `extern`. Putting it on a `def` is a parse error.
211
+
- If an `extern` has more than one `@structured`, Nushell uses the last one.
212
+
- When the command fails, its error message is printed. What happens next depends on the block. A block that reads all of the output, such as `from json`, stops the pipeline with a non-zero exit code error that points at the call. A block that streams the output, such as `lines`, passes on whatever the command printed and doesn't raise an error.
213
+
214
+
`@structured` also works with `export extern`, so a module can share these declarations:
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.
Copy file name to clipboardExpand all lines: book/running_externals.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -71,7 +71,7 @@ let program = "git"
71
71
^$program status
72
72
```
73
73
74
-
See [Strings as external commands](working_with_strings.md#strings-as-external-commands) for the supported string forms. An [`extern` declaration](externs.md) can add type checking and completions for a known external command; calling it follows the same argument rules described here.
74
+
See [Strings as external commands](working_with_strings.md#strings-as-external-commands) for the supported string forms. An [`extern` declaration](externs.md) can add type checking and completions for a known external command, and its [`@structured`](externs.md#structured-output) attribute can turn the command's output into data. Calling it follows the same argument rules described here.
When the output of the extern is used by nushell (piped into an internal command or collected into a variable), the arguments are added right after the command's name and the output is decoded with the block. When the output goes to the terminal, `print`, a file, `ignore`, `save`, `tee`, `complete` or another external command (`run-external` included), the command runs as written. The same holds when the extern is the last command of a `do` block or a custom command. Calling the command with a caret, like `^git status`, always runs it as written.
74
+
75
+
The arguments come before the user's own, so a format flag the user passes can override them and break the decode, as in `kubectl get pods -o wide`. Use a caret call for that. `tee` passes the command's own output on to the commands after it. When an extern has more than one `@structured`, the last one is used.
76
+
77
+
Like a `def` body, the block can read `$in`, `$env`, constants and the `let` variables declared before the extern, but not `mut` variables.
0 commit comments