OILS / doc / ref / chap-builtin-cmd.md View on Github | oilshell.org

1373 lines, 892 significant
1---
2in_progress: yes
3body_css_class: width40 help-body
4default_highlighter: oils-sh
5preserve_anchor_case: yes
6---
7
8Builtin Commands
9===
10
11This chapter in the [Oils Reference](index.html) describes builtin commands for OSH and YSH.
12
13<div id="toc">
14</div>
15
16## Memory
17
18### append
19
20Append word arguments to a list:
21
22 var mylist = :| hello |
23
24 append *.py (mylist) # append all Python files
25
26 var myflags = []
27 append -- -c 'echo hi' (myflags) # -- to avoid ambiguity
28
29It's a shortcut for:
30
31 call myflags->append('-c')
32 call myflags->append('echo hi')
33
34### pp
35
36Pretty prints interpreter state. Some of these are implementation details,
37subject to change.
38
39Examples:
40
41 pp proc # print all procs and their doc comments
42
43 var x = :| one two |
44 pp cell x # dump the "guts" of a cell, which is a location for a value
45
46 pp asdl (x) # dump the ASDL "guts"
47
48 pp line (x) # single-line stable format, for spec tests
49
50## Handle Errors
51
52### try
53
54Run a block of code, stopping at the first error. In other words, shopt
55`errexit` is enabled.
56
57Set the `_status` variable to the exit status of the block, and return 0.
58
59 try {
60 ls /nonexistent
61 }
62 if (_status !== 0) {
63 echo 'ls failed'
64 }
65
66Handle expression errors:
67
68 try {
69 var x = 42 / 0
70 }
71
72And errors from compound commands:
73
74 try {
75 ls | wc -l
76 diff <(sort left.txt) <(sort right.txt)
77 }
78
79The case statement can be useful:
80
81 try {
82 grep PATTERN FILE.txt
83 }
84 case (_status) {
85 (0) { echo 'found' }
86 (1) { echo 'not found' }
87 (else) { echo "grep returned status $_status" }
88 }
89
90The `try` builtin may also set the `_error` register.
91
92### boolstatus
93
94Runs a command and requires the exit code to be 0 or 1.
95
96 if boolstatus egrep '[0-9]+' myfile { # may abort
97 echo 'found' # status 0 means found
98 } else {
99 echo 'not found' # status 1 means not found
100 }
101
102### error
103
104The `error` builtin interrupts the shell program.
105
106 error 'Missing /tmp' # program fails with status 10
107
108Override the default status of `10` with a named argument:
109
110 error 'Missing /tmp' (status=99)
111
112In YSH, it's customary to use `error` instead of `return 1`, since it provides
113more information:
114
115 proc p {
116 if ! test -d /tmp {
117 error 'Missing /tmp' # more descriptive than return
118 }
119 echo hi
120 }
121
122Handle the error with the `try` builtin:
123
124 try {
125 p
126 }
127 if (_status !== 0) {
128 echo $[_error.message] # => Missing /tmp
129 }
130
131The integer `_status` is always set, and the Dict `_error` is set for all
132"structured" errors, which includes errors raised by the `try` builtin.
133
134Special properties of `_error`:
135
136- `_error.message` - the positional string arg
137- `_error.status` - the named `status` arg, or the default 10
138
139You can attach other, arbitrary properties to the error:
140
141 error 'Oops' (path='foo.json')
142
143They are attached to `_error`:
144
145 try {
146 error 'Oops' (path='foo.json')
147 }
148 if (_status !== 0) {
149 echo $[_error.path] # => foo.json
150 }
151
152## Shell State
153
154### ysh-cd
155
156It takes a block:
157
158 cd / {
159 echo $PWD
160 }
161
162### ysh-shopt
163
164It takes a block:
165
166 shopt --unset errexit {
167 false
168 echo 'ok'
169 }
170
171### shvar
172
173Execute a block with a global variable set.
174
175 shvar IFS=/ {
176 echo "ifs is $IFS"
177 }
178 echo "ifs restored to $IFS"
179
180### ctx
181
182Execute a block with a shared "context" that can be updated using the `ctx`
183built-in.
184
185 var mydict = {}
186 ctx push (mydict) {
187 # = mydict => {}
188 ctx set (mykey='myval')
189 }
190 # = mydict => { mykey: 'myval' }
191
192The context can be modified with `ctx set (key=val)`, which updates or inserts
193the value at the given key.
194
195The context can also be updated with `ctx emit field (value)`.
196
197 ctx push (mydict) {
198 # = mydict => {}
199 ctx emit mylist (0)
200 # = mydict => { mylist: [0] }
201 ctx emit mylist (1)
202 }
203 # = mydict => { mylist: [0, 1] }
204
205Contexts can be nested, resulting in a stack of contexts.
206
207 ctx push (mydict1) {
208 ctx set (dict=1)
209 ctx push (mydict2) {
210 ctx set (dict=2)
211 }
212 }
213 # = mydict1 => { dict: 1 }
214 # = mydict2 => { dict: 2 }
215
216`ctx` is useful for creating DSLs, such as a mini-parseArgs.
217
218 proc parser (; place ; ; block_def) {
219 var p = {}
220 ctx push (p, block_def)
221 call place->setValue(p)
222 }
223
224 proc flag (short_name, long_name; type; help) {
225 ctx emit flag ({short_name, long_name, type, help})
226 }
227
228 proc arg (name) {
229 ctx emit arg ({name})
230 }
231
232 parser (&spec) {
233 flag -t --tsv (Bool, help='Output as TSV')
234 flag -r --recursive (Bool, help='Recurse into the given directory')
235 flag -N --count (Int, help='Process no more than N files')
236 arg path
237 }
238
239### push-registers
240
241Save global registers like $? on a stack. It's useful for preventing plugins
242from interfering with user code. Example:
243
244 status_42 # returns 42 and sets $?
245 push-registers { # push a new frame
246 status_43 # top of stack changed here
247 echo done
248 } # stack popped
249 echo $? # 42, read from new top-of-stack
250
251Current list of registers:
252
253 Regex data underlying BASH_REMATCH, _group(), _start(), _end()
254 $?
255 _status # set by the try builtin
256 PIPESTATUS # aka _pipeline_status
257 _process_sub_status
258
259
260## Modules
261
262### runproc
263
264Runs a named proc with the given arguments. It's often useful as the only top
265level statement in a "task file":
266
267 proc p {
268 echo hi
269 }
270 runproc @ARGV
271
272Like 'builtin' and 'command', it affects the lookup of the first word.
273
274### module
275
276Registers a name in the global module dict. Returns 0 if it doesn't exist, or
2771 if it does.
278
279Use it like this in executable files:
280
281 module main || return 0
282
283And like this in libraries:
284
285 module myfile.ysh || return 0
286
287### is-main
288
289The `is-main` builtin returns 1 (false) if the current file was executed with
290the `source` builtin.
291
292In the "main" file, including `-c` or `stdin` input, it returns 0 (true).
293
294Use it like this:
295
296 if is-main {
297 runproc @ARGV
298 }
299
300### use
301
302TODO
303
304Reuse code from other files, respecting namespaces.
305
306 use lib/foo.ysh # relative import, i.ie implicit $_this_dir?
307 # makes name 'foo' available
308
309Bind a specific name:
310
311 use lib/foo.ysh (&myvar) # makes 'myvar' available
312
313Bind multiple names:
314
315 use lib/foo.ysh (&myvar) {
316 var log, die
317 }
318
319Maybe:
320
321 use lib/foo.ysh (&myvar) {
322 var mylog = myvar.log
323 }
324
325Also a declaration
326
327 use --extern grep sed
328
329## I/O
330
331### ysh-read
332
333YSH adds long flags to shell's `read`:
334
335 read --all # whole file including newline, in $_reply
336 read --all (&x) # fills $x
337
338And a convenience:
339
340 read -0 # read until NUL, synonym for read -r -d ''
341
342TODO: We used to have `read --line`, but buffered I/O doesn't mix with shell
343I/O, which is reads directly from file descriptors.
344
345<!--
346
347buffered, line-oriented I/O
348
349 read --line # fills $_reply var with line
350 read --line (&x) # fills $x (&x is a place)
351
352 read --line --with-eol # keep the \n
353
354You may want to use `fromJ8()` or `fromJson()` after reading a line.
355
356TODO: read --netstr
357-->
358
359<!--
360
361Problem with read --json -- there's also https://jsonlines.org, which allows
362
363 {"my": "line"}
364
365That can be done with
366
367 while read --line {
368 var record = fromJson(_reply)
369 }
370
371This is distinct from:
372
373 while read --line --j8 {
374 echo $_reply
375 }
376
377This allows unquoted. Maybe it should be read --j8-line
378
379What about write? These would be the same:
380
381 write --json -- $s
382 write --j8 -- $s
383
384 write -- $[toJson(s)]
385 write -- $[toJson8(s)]
386
387 write --json -- @strs
388 write --j8 -- @strs
389
390 write -- @[toJson(s) for s in strs]
391 write -- @[toJson8(s) for s in strs]
392
393It's an argument for getting rid --json and --j8? I already implemented them,
394but it makes the API smaller.
395
396I guess the main thing would be to AVOID quoting sometimes?
397
398 $ write --j8 -- unquoted
399 unquoted
400
401 $ write --j8 -- $'\'' '"'
402 "'"
403 "\""
404
405I think this could be the shell style?
406
407 $ write --shell-str -- foo bar baz
408
409Or it could be
410
411 $ write -- @[toShellString(s) for s in strs]
412
413I want this to be "J8 Lines", but it can be done in pure YSH. It's not built
414into the interpreter.
415
416 foo/bar
417 "hi"
418b'hi'
419u'hi'
420
421But what about
422
423 Fool's Gold
424a'hi' # This feels like an error?
425a"hi" # what about this?
426
427Technically we CAN read those as literal strings
428-->
429
430### write
431
432write fixes problems with shell's `echo` builtin.
433
434The default separator is a newline, and the default terminator is a
435newline.
436
437Examples:
438
439 write -- ale bean # write two lines
440
441 write -n -- ale bean # synonym for --end '', like echo -n
442 write --sep '' --end '' -- a b # write 2 bytes
443 write --sep $'\t' --end $'\n' -- a b # TSV line
444
445You may want to use `toJson8()` or `toJson()` before writing:
446
447 write -- $[toJson8(mystr)]
448 write -- $[toJson(mystr)]
449
450
451<!--
452 write --json -- ale bean # JSON encode, guarantees two lines
453 write --j8 -- ale bean # J8 encode, guarantees two lines
454-->
455
456
457### fork
458
459The preferred alternative to shell's `&`.
460
461 fork { sleep 1 }
462 wait -n
463
464### forkwait
465
466The preferred alternative to shell's `()`. Prefer `cd` with a block if possible.
467
468 forkwait {
469 not_mutated=zzz
470 }
471 echo $not_mutated
472
473
474
475## Data Formats
476
477### json
478
479Write JSON:
480
481 var d = {name: 'bob', age: 42}
482 json write (d)
483
484Read JSON:
485
486 echo hi | json read # fills $_reply by default
487
488Or use an explicit place:
489
490 var x = ''
491 json read (&x) < myfile.txt
492
493Related: [json-encode-err]() and [json-decode-error]()
494
495### json8
496
497Like `json`, but on the encoding side:
498
499- Falls back to `b'\yff'` instead of lossy Unicode replacement char
500
501On decoding side:
502
503- Understands `b'' u''` strings
504
505Related: [json8-encode-err]() and [json8-decode-error]()
506
507## Testing
508
509TODO: describe
510
511## External Lang
512
513TODO: when
514
515
516## I/O
517
518These builtins take input and output. They're often used with redirects.
519
520### read
521
522 read FLAG* VAR*
523
524Read a line from stdin, split it into tokens with the `$IFS` algorithm,
525and assign the tokens to the given variables. When no VARs are given,
526assign to `$REPLY`.
527
528Note: When writing ySH, prefer the extensions documented in
529[ysh-read](#ysh-read). The `read` builtin is confusing because `-r` needs to
530be explicitly enabled.
531
532Flags:
533
534 -a ARRAY assign the tokens to elements of this array
535 -d CHAR use DELIM as delimiter, instead of newline
536 -n NUM read up to NUM characters, respecting delimiters
537 -p STR print the string PROMPT before reading input
538 -r raw mode: don't let backslashes escape characters
539 -s silent: do not echo input coming from a terminal
540 -t NUM time out and fail after TIME seconds
541 -t 0 returns whether any input is available
542 -u FD read from file descriptor FD instead of 0 (stdin)
543
544 <!-- -N NUM read up to NUM characters, ignoring delimiters -->
545 <!-- -e use readline to obtain the line
546 -i STR use STR as the initial text for readline -->
547
548### echo
549
550 echo FLAG* ARG*
551
552Prints ARGs to stdout, separated by a space, and terminated by a newline.
553
554Flags:
555
556 -e enable interpretation of backslash escapes
557 -n omit the trailing newline
558<!-- -E -->
559
560See [char-escapes]($osh-help).
561
562### printf
563
564 printf FLAG* FMT ARG*
565
566Formats values and prints them. The FMT string contain three types of objects:
567
5681. Literal Characters
5692. Character escapes like `\t`. See [char-escapes]($osh-help).
5703. Percent codes like `%s` that specify how to format each each ARG.
571
572If not enough ARGS are passed, the empty string is used. If too many are
573passed, the FMT string will be "recycled".
574
575Flags:
576
577 -v VAR Write output in variable VAR instead of standard output.
578
579Format specifiers:
580
581 %% Prints a single "%".
582 %b Interprets backslash escapes while printing.
583 %q Prints the argument escaping the characters needed to make it reusable
584 as shell input.
585 %d Print as signed decimal number.
586 %i Same as %d.
587 %o Print as unsigned octal number.
588 %u Print as unsigned decimal number.
589 %x Print as unsigned hexadecimal number with lower-case hex-digits (a-f).
590 %X Same as %x, but with upper-case hex-digits (A-F).
591 %f Print as floating point number.
592 %e Print as a double number, in "±e" format (lower-case e).
593 %E Same as %e, but with an upper-case E.
594 %g Interprets the argument as double, but prints it like %f or %e.
595 %G Same as %g, but print it like %E.
596 %c Print as a single char, only the first character is printed.
597 %s Print as string
598 %n The number of characters printed so far is stored in the variable named
599 in the argument.
600 %a Interprets the argument as double, and prints it like a C99 hexadecimal
601 floating-point literal.
602 %A Same as %a, but print it like %E.
603 %(FORMAT)T Prints date and time, according to FORMAT as a format string
604 for strftime(3). The argument is the number of seconds since
605 epoch. It can also be -1 (current time, also the default value
606 if there is no argument) or -2 (shell startup time).
607
608### readarray
609
610Alias for `mapfile`.
611
612### mapfile
613
614 mapfile FLAG* ARRAY?
615
616Reads lines from stdin into the variable named ARRAY (default
617`${MAPFILE[@]}`).
618
619Flags:
620
621 -t Remove the trailing newline from every line
622<!--
623 -d CHAR use CHAR as delimiter, instead of the default newline
624 -n NUM copy up to NUM lines
625 -O NUM begins copying lines at the NUM element of the array
626 -s NUM discard the first NUM lines
627 -u FD read from FD file descriptor instead of the standard input
628 -C CMD run CMD every NUM lines specified in -c
629 -c NUM every NUM lines, the CMD command in C will be run
630-->
631
632## Run Code
633
634These builtins accept shell code and run it.
635
636### source
637
638 source SCRIPT ARG*
639
640Executes SCRIPT with given ARGs in the context of the current shell. It will
641modify existing variables.
642
643### eval
644
645 eval ARG+
646
647Creates a string by joining ARGs with a space, then runs it as a shell command.
648
649Example:
650
651 # Create the string echo "hello $name" and run it.
652 a='echo'
653 b='"hello $name"'
654 eval $a $b
655
656Tips:
657
658- Using `eval` can confuse code and user-supplied data, leading to [security
659issues][].
660- Prefer passing single string ARG to `eval`.
661
662[security issues]: https://mywiki.wooledge.org/BashFAQ/048
663
664YSH eval:
665
666 var myblock = ^(echo hi)
667 eval (myblock) # => hi
668
669
670### trap
671
672 trap FLAG* CMD SIGNAL*
673
674Registers the shell string CMD to be run after the SIGNALs are received. If
675the CMD is empty, then the signal is ignored.
676
677Flags:
678
679 -l Lists all signals and their signal number
680 -p Prints a list of the installed signal handlers
681
682Tip:
683
684Prefer passing the name of a shell function to `trap`.
685
686## Set Options
687
688The `set` and `shopt` builtins set global shell options. YSH code should use
689the more natural `shopt`.
690
691### set
692
693 set FLAG* ARG*
694
695Sets global shell options. Short style:
696
697 set -e
698
699Long style:
700
701 set -o errexit
702
703Set the arguments array:
704
705 set -- 1 2 3
706
707### shopt
708
709 shopt FLAG* OPTION* BLOCK?
710
711Sets global shell options.
712
713Flags:
714
715 -s --set Turn the named options on
716 -u --unset Turn the named options off
717 -p Print option values
718 -q Return 0 if the option is true, else 1
719
720Examples:
721
722 shopt --set errexit
723
724You can set or unset multiple options with the groups `strict:all`,
725`ysh:upgrade`, and `ysh:all`.
726
727If a block is passed, then the mutated options are pushed onto a stack, the
728block is executed, and then options are restored to their original state.
729
730## Working Dir
731
732These 5 builtins deal with the working directory of the shell.
733
734### cd
735
736 cd FLAG* DIR
737
738Changes the working directory of the current shell process to DIR.
739
740If DIR isn't specified, change to `$HOME`. If DIR is `-`, change to `$OLDPWD`
741(a variable that the sets to the previous working directory.)
742
743Flags:
744
745 -L Follow symbolic links, i.e. change to the TARGET of the symlink.
746 (default).
747 -P Don't follow symbolic links.
748
749### pwd
750
751 pwd FLAG*
752
753Prints the current working directory.
754
755Flags:
756
757 -L Follow symbolic links if present (default)
758 -P Don't follow symbolic links. Print the link instead of the target.
759
760### pushd
761
762<!--pushd FLAGS DIR-->
763 pushd DIR
764<!--pushd +/-NUM-->
765
766Add DIR to the directory stack, then change the working directory to DIR.
767Typically used with `popd` and `dirs`.
768
769<!--FLAGS:
770 -n Don't change the working directory, just manipulate the stack
771NUM:
772 Rotates the stack the number of places specified. Eg, given the stack
773 '/foo /bar /baz', where '/foo' is the top of the stack, pushd +1 will move
774 it to the bottom, '/bar /baz /foo'-->
775
776### popd
777
778 popd
779
780Removes a directory from the directory stack, and changes the working directory
781to it. Typically used with `pushd` and `dirs`.
782
783### dirs
784
785 dirs FLAG*
786
787Shows the contents of the directory stack. Typically used with `pushd` and
788`popd`.
789
790Flags:
791
792 -c Clear the dir stack.
793 -l Show the dir stack, but with the real path instead of ~.
794 -p Show the dir stack, but formatted as one line per entry.
795 -v Like -p, but numbering each line.
796
797## Completion
798
799These builtins implement our bash-compatible autocompletion system.
800
801### complete
802
803Registers completion policies for different commands.
804
805### compgen
806
807Generates completion candidates inside a user-defined completion function.
808
809It can also be used in scripts, i.e. outside a completion function.
810
811### compopt
812
813Changes completion options inside a user-defined completion function.
814
815### compadjust
816
817Adjusts `COMP_ARGV` according to specified delimiters, and optionally set
818variables cur, prev, words (an array), and cword. May also set 'split'.
819
820This is an OSH extension that makes it easier to run the bash-completion
821project.
822
823### compexport
824
825Complete an entire shell command string. For example,
826
827 compexport -c 'echo $H'
828
829will complete variables like `$HOME`. And
830
831 compexport -c 'ha'
832
833will complete builtins like `hay`, as well as external commands.
834
835
836## Shell Process
837
838These builtins mutate the state of the shell process.
839
840### exec
841
842 exec BIN_PATH ARG*
843
844Replaces the running shell with the binary specified, which is passed ARGs.
845BIN_PATH must exist on the file system; i.e. it can't be a shell builtin or
846function.
847
848### umask
849
850 umask MODE?
851
852Sets the bit mask that determines the permissions for new files and
853directories. The mask is subtracted from 666 for files and 777 for
854directories.
855
856Oils currently supports writing masks in octal.
857
858If no MODE, show the current mask.
859
860### times
861
862 times
863
864Shows the user and system time used by the shell and its child processes.
865
866## Child Process
867
868### jobs
869
870 jobs
871
872Shows all jobs running in the shell and their status.
873
874### wait
875
876 wait FLAG* ARG
877
878Wait for processes to exit.
879
880If the ARG is a PID, wait only for that job, and return its status.
881
882If there's no ARG, wait for all child processes.
883
884<!--
885The ARG can be a PID (tracked by the kernel), or a job number (tracked by the
886shell). Specify jobs with the syntax `%jobnumber`.
887-->
888
889Flags:
890
891 -n Wait for the next process to exit, rather than a specific process.
892
893Wait can be interrupted by a signal, in which case the exit code indicates the
894signal number.
895
896### fg
897
898 fg JOB?
899
900Returns a job running in the background to the foreground. If no JOB is
901specified, use the latest job.
902
903<!--<h4 id="bg">bg</h4>
904
905The bg builtin resumes suspend job, while keeping it in the background.
906
907bg JOB?
908
909JOB:
910 Job ID to be resumed in the background. If none is specified, the latest job
911 is chosen. -->
912
913## External
914
915### test
916
917 test OP ARG
918 test ARG OP ARG
919 [ OP ARG ] # [ is an alias for test that requires closing ]
920 [ ARG OP ARG ]
921
922Evaluates a conditional expression and returns 0 (true) or 1 (false).
923
924Note that [ is the name of a builtin, not an operator in the language. Use
925'test' to avoid this confusion.
926
927String expressions:
928
929 -n STR True if STR is not empty.
930 'test STR' is usually equivalent, but discouraged.
931 -z STR True if STR is empty.
932 STR1 = STR2 True if the strings are equal.
933 STR1 != STR2 True if the strings are not equal.
934 STR1 < STR2 True if STR1 sorts before STR2 lexicographically.
935 STR1 > STR2 True if STR1 sorts after STR2 lexicographically.
936 Note: < and > should be quoted like \< and \>
937
938File expressions:
939
940 -a FILE Synonym for -e.
941 -b FILE True if FILE is a block special file.
942 -c FILE True if FILE is a character special file.
943 -d FILE True if FILE is a directory.
944 -e FILE True if FILE exists.
945 -f FILE True if FILE is a regular file.
946 -g FILE True if FILE has the sgid bit set.
947 -G FILE True if current user's group is also FILE's group.
948 -h FILE True if FILE is a symbolic link.
949 -L FILE True if FILE is a symbolic link.
950 -k FILE True if FILE has the sticky bit set.
951 -O FILE True if current user is the file owner.
952 -p FILE True if FILE is a named pipe (FIFO).
953 -r FILE True if FILE is readable.
954 -s FILE True if FILE has size bigger than 0.
955 -S FILE True if FILE is a socket file.
956 -t FD True if file descriptor FD is open and refers to a terminal.
957 -u FILE True if FILE has suid bit set.
958 -w FILE True if FILE is writable.
959 -x FILE True if FILE is executable.
960 FILE1 -nt FILE2 True if FILE1 is newer than FILE2 (mtime).
961 FILE1 -ot FILE2 True if FILE1 is older than FILE2 (mtime).
962 FILE1 -ef FILE2 True if FILE1 is a hard link to FILE2.
963<!-- -N FILE True if FILE was modified since last read (mtime newer than atime).-->
964
965Arithmetic expressions coerce arguments to integers, then compare:
966
967 INT1 -eq INT2 True if they're equal.
968 INT1 -ne INT2 True if they're not equal.
969 INT1 -lt INT2 True if INT1 is less than INT2.
970 INT1 -le INT2 True if INT1 is less or equal than INT2.
971 INT1 -gt INT2 True if INT1 is greater than INT2.
972 INT1 -ge INT2 True if INT1 is greater or equal than INT2.
973
974Other expressions:
975
976 -o OPTION True if the shell option OPTION is set.
977 -v VAR True if the variable VAR is set.
978
979The test builtin also supports POSIX conditionals like -a, -o, !, and ( ), but
980these are discouraged.
981
982<!-- -R VAR True if the variable VAR has been set and is a nameref variable. -->
983
984Oils supports these long flags:
985
986 --dir same as -d
987 --exists same as -e
988 --file same as -f
989 --symlink same as -L
990
991### getopts
992
993 getopts SPEC VAR ARG*
994
995A single iteration of flag parsing. The SPEC is a sequence of flag characters,
996with a trailing `:` to indicate that the flag takes an argument:
997
998 ab # accept -a and -b
999 xy:z # accept -x, -y arg, and -z
1000
1001The input is `"$@"` by default, unless ARGs are passed.
1002
1003On each iteration, the flag character is stored in VAR. If the flag has an
1004argument, it's stored in `$OPTARG`. When an error occurs, VAR is set to `?`
1005and `$OPTARG` is unset.
1006
1007Returns 0 if a flag is parsed, or 1 on end of input or another error.
1008
1009Example:
1010
1011 while getopts "ab:" flag; do
1012 case $flag in
1013 a) flag_a=1 ;;
1014 b) flag_b=$OPTARG" ;;
1015 '?') echo 'Invalid Syntax'; break ;;
1016 esac
1017 done
1018
1019Notes:
1020- `$OPTIND` is initialized to 1 every time a shell starts, and is used to
1021 maintain state between invocations of `getopts`.
1022- The characters `:` and `?` can't be flags.
1023
1024### kill
1025
1026Unimplemented.
1027
1028<!-- Note: 'kill' accepts job control syntax -->
1029
1030## Introspection
1031
1032<h3 id="help" class="osh-topic ysh-topic" oils-embed="1">
1033 help
1034</h3>
1035
1036<!-- pre-formatted for help builtin -->
1037
1038```
1039Usage: help TOPIC?
1040
1041Examples:
1042
1043 help # this help
1044 help echo # help on the 'echo' builtin
1045 help com-sub # help on command sub $(date)
1046
1047 help oils-usage # identical to oils-for-unix --help
1048 help osh-usage # osh --help
1049 help ysh-usage # ysh --help
1050```
1051
1052### hash
1053
1054 hash
1055
1056Display information about remembered commands.
1057
1058 hash FLAG* CMD+
1059
1060Determine the locations of commands using `$PATH`, and remember them.
1061
1062Flag:
1063
1064 -r Discard all remembered locations.
1065<!-- -d Discard the remembered location of each NAME.
1066 -l Display output in a format reusable as input.
1067 -p PATH Inhibit path search, PATH is used as location for NAME.
1068 -t Print the full path of one or more NAME.-->
1069
1070### type
1071
1072 type FLAG* NAME+
1073
1074Print the type of each NAME, if it were the first word of a command. Is it a
1075shell keyword, builtin command, shell function, alias, or executable file on
1076$PATH?
1077
1078Flags:
1079
1080 -a Show all possible candidates, not just the first one
1081 -f Don't search for shell functions
1082 -P Only search for executable files
1083 -t Print a single word: alias, builtin, file, function, or keyword
1084
1085<!-- TODO:
1086- procs are counted as shell functions, should be their own thing
1087- Hay nodes ('hay define x') also live in the first word namespace, and should
1088 be recognized
1089-->
1090
1091Modeled after the [bash `type`
1092builtin](https://www.gnu.org/software/bash/manual/bash.html#index-type).
1093
1094## Word Lookup
1095
1096### command
1097
1098 command FLAG* CMD ARG*
1099
1100Look up CMD as a shell builtin or executable file, and execute it with the
1101given ARGs. That is, the lookup ignores shell functions named CMD.
1102
1103Flags:
1104
1105 -v Instead of executing CMD, print a description of it.
1106 Similar to the 'type' builtin.
1107<!-- -p Use a default value for PATH that is guaranteed to find all of the
1108 standard utilities.
1109 -V Print a more verbose description of CMD.-->
1110
1111### builtin
1112
1113 builtin CMD ARG*
1114
1115Look up CMD as a shell builtin, and execute it with the given ARGs. That is,
1116the lookup ignores shell functions and executables named CMD.
1117
1118## Interactive
1119
1120### alias
1121
1122 alias NAME=CODE
1123
1124Make NAME a shortcut for executing CODE, e.g. `alias hi='echo hello'`.
1125
1126 alias NAME
1127
1128Show the value of this alias.
1129
1130 alias
1131
1132Show a list of all aliases.
1133
1134Tips:
1135
1136Prefer shell functions like:
1137
1138 ls() {
1139 command ls --color "$@"
1140 }
1141
1142to aliases like:
1143
1144 alias ls='ls --color'
1145
1146Functions are less likely to cause parsing problems.
1147
1148- Quoting like `\ls` or `'ls'` disables alias expansion
1149- To remove an existing alias, use [unalias]($osh-help).
1150
1151### unalias
1152
1153 unalias NAME
1154
1155Remove the alias NAME.
1156
1157<!--Flag:
1158
1159 -a Removes all existing aliases.-->
1160
1161### history
1162
1163 history FLAG*
1164
1165Display and manipulate the shell's history entries.
1166
1167 history NUM
1168
1169Show the last NUM history entries.
1170
1171Flags:
1172
1173 -c Clears the history.
1174 -d POS Deletes the history entry at position POS.
1175<!-- -a
1176 -n
1177 -r
1178 -w
1179 -p
1180 -s -->
1181
1182
1183## Unsupported
1184
1185### enable
1186
1187Bash has this, but OSH won't implement it.
1188
1189
1190## Args Parser
1191
1192YSH includes a command-line argument parsing utility called `parseArgs`. This
1193is intended to be used for command-line interfaces to YSH programs.
1194
1195To use it, first import `args.ysh`:
1196
1197 source --builtin args.ysh
1198
1199Then, create an argument parser **spec**ification:
1200
1201 parser (&spec) {
1202 flag -v --verbose (help="Verbosely") # default is Bool, false
1203
1204 flag -P --max-procs ('int', default=-1, help='''
1205 Run at most P processes at a time
1206 ''')
1207
1208 flag -i --invert ('bool', default=true, help='''
1209 Long multiline
1210 Description
1211 ''')
1212
1213 arg src (help='Source')
1214 arg dest (help='Dest')
1215
1216 rest files
1217 }
1218
1219Finally, parse `ARGV` (or any other array of strings) with:
1220
1221 var args = parseArgs(spec, ARGV)
1222
1223The returned `args` is a `Dict` containing key-value pairs with the parsed
1224values (or defaults) for each flag and argument. For example, given
1225`ARGV = :| mysrc -P 12 mydest a b c |`, `args` would be:
1226
1227 {
1228 "verbose": false,
1229 "max-procs": 12,
1230 "invert": true,
1231 "src": "mysrc",
1232 "dest": "mydest",
1233 "files": ["a", "b", "c"]
1234 }
1235
1236### parser
1237
1238`parseArgs()` requires a parser specification to indicate how to parse the
1239`ARGV` array. This specification should be constructed using the `parser` proc.
1240
1241 parser (&spec) {
1242 flag -f --my-flag
1243 arg myarg
1244 rest otherArgs
1245 }
1246
1247In the above example, `parser` takes in a place `&spec`, which will store the
1248resulting specification and a block which is evaluated to build that
1249specification.
1250
1251Inside of a `parser` block, you should call the following procs:
1252
1253- `flag` to add `--flag` options
1254- `arg` to add positional arguments
1255- `rest` to capture remaining positional arguments into a list
1256
1257`parser` will validate the parser specification for errors such as duplicate
1258flag or argument names.
1259
1260 parser (&spec) {
1261 flag -n --name
1262 flag -n --name # Duplicate!
1263 }
1264
1265 # => raises "Duplicate flag/arg name 'name' in spec" (status = 3)
1266
1267### flag
1268
1269`flag` should be called within a `parser` block.
1270
1271 parser (&spec) {
1272 flag -v --verbose
1273 }
1274
1275The above example declares a flag "--verbose" and a short alias "-v".
1276`parseArgs()` will then store a boolean value under `args.verbose`:
1277- `true` if the flag was passed at least once
1278- `false` otherwise
1279
1280Flags can also accept values. For example, if you wanted to accept an integer count:
1281
1282 parser (&spec) {
1283 flag -N --count ('int')
1284 }
1285
1286Calling `parseArgs` with `ARGV = :| -n 5 |` or `ARGV = :| --count 5 |` will
1287store the integer `5` under `args.count`. If the user passes in a non-integer
1288value like `ARGV = :| --count abc |`, `parseArgs` will raise an error.
1289
1290Default values for an argument can be set with the `default` named argument.
1291
1292 parser (&spec) {
1293 flag -N --count ('int', default=2)
1294
1295 # Boolean flags can be given default values too
1296 flag -O --optimize ('bool', default=true)
1297 }
1298
1299 var args = parseArgs(spec, :| -n 3 |)
1300 # => args.count = 2
1301 # => args.optimize = true
1302
1303Each name passed to `flag` must be unique to that specific `parser`. Calling
1304`flag` with the same name twice will raise an error inside of `parser`.
1305
1306<!-- TODO: how can we explicitly pass false to a boolean flag? -->
1307<!-- TODO: how about --no-XXXX variants of flags? -->
1308
1309### arg
1310
1311`arg` should be called within a `parser` block.
1312
1313 parser (&spec) {
1314 arg query
1315 arg path
1316 }
1317
1318The above example declares two positional arguments called "query" and "path".
1319`parseArgs()` will then store strings under `args.query` and `args.path`. Order
1320matters, so the first positional argument will be stored to `query` and the
1321second to `path`. If not enough positional arguments are passed, then
1322`parseArgs` will raise an error.
1323
1324Similar to `flag`, each `arg` name must be unique. Calling `arg` with the same
1325name twice will cause `parser` to raise an error.
1326
1327### rest
1328
1329`rest` should be called within a `parser` block.
1330
1331 parser (&spec) {
1332 arg query
1333 rest files
1334 }
1335
1336Capture zero or more positional arguments not already captured by `arg`. So,
1337for `ARGV = :| hello file.txt message.txt README.md |`, we would have
1338`args.query = "file.txt"` and `args.files = ["file.txt", "message.txt",
1339"README.md"]`.
1340
1341Without rest, passing extraneous arguments will raise an error in
1342`parseArgs()`.
1343
1344`rest` can only be called _once_ within a `parser`. Calling it multiple times
1345will raise an error in `parser`.
1346
1347### parseArgs()
1348
1349Given a parser specification `spec` produced by `parser`, parse a list of
1350strings (usually `ARGV`.)
1351
1352 var args = parseArgs(spec, ARGV)
1353
1354The returned `args` is a dictionary mapping the names of each `arg`, `flag` and
1355`rest` to their captured values. (See the example at the [start of this
1356topic](#Args-Parser).)
1357
1358`parseArgs` will raise an error if the `ARGV` is invalid per the parser
1359specification. For example, if it's missing a required positional argument:
1360
1361 parser (&spec) {
1362 arg path
1363 }
1364
1365 var args = parseArgs(spec, [])
1366 # => raises an error about the missing 'path' (status = 2)
1367
1368<!--
1369TODO: Document chaining parsers / sub-commands
1370 - Either will allow parser nesting
1371 - Or can use `rest rest` and `parseArgs` again on `rest`
1372TODO: Document the help named argument. Punting while we do not generate help messages
1373-->