2018-07-09 23:11:50 +01:00
|
|
|
\input texinfo
|
|
|
|
@c -*- mode: texinfo; -*-
|
|
|
|
|
|
|
|
@c %**start of header
|
|
|
|
@setfilename gash.info
|
|
|
|
@documentencoding UTF-8
|
|
|
|
@settitle Gash Reference Manual
|
|
|
|
@c %**end of header
|
|
|
|
|
|
|
|
@include version.texi
|
|
|
|
|
|
|
|
@copying
|
|
|
|
Copyright @copyright{} 2018 Rutger EW van Beusekom@*
|
2018-12-08 07:36:36 +00:00
|
|
|
Copyright @copyright{} 2018 Jan (janneke) Nieuwenhuizen@*
|
2019-06-01 03:11:14 +01:00
|
|
|
Copyright @copyright{} 2019 Timothy Sample@*
|
2018-07-09 23:11:50 +01:00
|
|
|
|
|
|
|
Permission is granted to copy, distribute and/or modify this document
|
|
|
|
under the terms of the GNU Free Documentation License, Version 1.3 or
|
|
|
|
any later version published by the Free Software Foundation; with no
|
|
|
|
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
|
|
|
|
copy of the license is included in the section entitled ``GNU Free
|
|
|
|
Documentation License''.
|
|
|
|
@end copying
|
|
|
|
|
|
|
|
@dircategory Basics
|
|
|
|
@direntry
|
|
|
|
* Gash: (gash). Guile As SHell.
|
2019-06-01 03:11:14 +01:00
|
|
|
* gash: (gash)Invoking gash. Running Gash, a shell written in Scheme.
|
2018-07-09 23:11:50 +01:00
|
|
|
@end direntry
|
|
|
|
|
|
|
|
@titlepage
|
|
|
|
@title Gash Reference Manual
|
2019-06-01 03:11:14 +01:00
|
|
|
@subtitle A POSIX-compatible shell written in Guile Scheme.
|
2018-12-08 07:36:36 +00:00
|
|
|
@author The Gash developers
|
2018-07-09 23:11:50 +01:00
|
|
|
|
|
|
|
@page
|
|
|
|
@vskip 0pt plus 1filll
|
|
|
|
Edition @value{EDITION} @*
|
|
|
|
@value{UPDATED} @*
|
|
|
|
|
|
|
|
@insertcopying
|
|
|
|
@end titlepage
|
|
|
|
|
|
|
|
@contents
|
|
|
|
|
|
|
|
@c *********************************************************************
|
|
|
|
@node Top
|
|
|
|
@top Gash
|
|
|
|
|
|
|
|
This document describes Gash version @value{VERSION}, a
|
2019-06-01 03:11:14 +01:00
|
|
|
POSIX-compatible shell written in Guile Scheme.
|
2018-07-09 23:11:50 +01:00
|
|
|
|
|
|
|
@menu
|
|
|
|
* Introduction:: What is Gash about?
|
2019-06-01 03:11:14 +01:00
|
|
|
* Using Gash:: How to use Gash as a shell.
|
|
|
|
* Hacking with Gash:: How to use Gash as a Scheme library.
|
2018-07-09 23:11:50 +01:00
|
|
|
* GNU Free Documentation License:: The license of this manual.
|
|
|
|
@end menu
|
|
|
|
|
|
|
|
@c *********************************************************************
|
|
|
|
@node Introduction
|
|
|
|
@chapter Introduction
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
Gash is a POSIX-compatible shell written in Guile Scheme. It has two
|
|
|
|
goals: bootstrapping GNU Bash and exposing the shell to Scheme. In
|
|
|
|
the following sections we will unpack that definition and explain all
|
|
|
|
of its constituent parts.
|
|
|
|
|
|
|
|
|
|
|
|
@node What is a POSIX-compatible shell?
|
|
|
|
@section What is a POSIX-compatible shell?
|
|
|
|
|
|
|
|
When talking about operating systems, a @dfn{shell} refers to the
|
|
|
|
day-to-day user interface that a person would use to interact with the
|
|
|
|
system. That's what you might learn in computer science school, but
|
|
|
|
in popular usage ``shell'' refers to a very particular type of
|
|
|
|
interface---the one that developed along with the Unix operating
|
|
|
|
system. The Unix shell is a command-line interface designed mostly
|
|
|
|
for invoking programs and connecting programs together. Although we
|
|
|
|
provide a whirlwind tour of shell features in @ref{Using Gash}, this
|
|
|
|
manual is not a good general introduction to using a Unix-like shell.
|
|
|
|
For that, try
|
|
|
|
@url{http://write.flossmanuals.net/command-line/introduction/,
|
|
|
|
Introduction to the Command Line}.
|
|
|
|
|
|
|
|
So now we know what a @emph{Unix} shell is, but what is a
|
|
|
|
@emph{POSIX-compatible} shell? Over time, Unix inspired many similar
|
|
|
|
systems, all of which were mostly the same, but not quite compatible
|
|
|
|
with one another. What worked on one Unix-like operating system might
|
|
|
|
not work on another. To make it easier to write portable software,
|
|
|
|
everyone got together and described a minimal set of features that all
|
|
|
|
Unix-like systems should provide. They called it the @dfn{Portable
|
|
|
|
Operating System Interface}, or @dfn{POSIX} for short. Being a
|
|
|
|
@emph{POSIX-compatible} shell means that Gash should have at least all
|
|
|
|
of the features required by POSIX.@footnote{Note that Gash is merely
|
|
|
|
POSIX-compatible and @emph{not} POSIX-certified. This means we try
|
|
|
|
our best, but we have not gotten an official stamp of approval from
|
|
|
|
The Open Group, who owns the POSIX trademark and administers a
|
|
|
|
certification program.}
|
|
|
|
|
|
|
|
|
|
|
|
@node What is Guile Scheme?
|
|
|
|
@section What is Guile Scheme?
|
|
|
|
|
|
|
|
@dfn{Scheme} is a cool programming language from the 70's. It is a
|
|
|
|
very conceptually simple dialect of @dfn{Lisp}---a cool programming
|
|
|
|
language from the 50's. Why are we interested in using such an old
|
|
|
|
programming language? There are two reasons. The first is that even
|
|
|
|
by modern standards, Scheme is a very expressive, high-level language.
|
|
|
|
Writing in such a language is a pleasure. The second reason is that
|
|
|
|
Scheme's simplicity makes it well-suited to the bootstrapping task
|
|
|
|
(@pxref{What of Bash and its bootstraps?}).
|
|
|
|
|
|
|
|
@dfn{Guile} is a particular implementation of Scheme. It is the
|
|
|
|
official extension language of the GNU project. There are many
|
|
|
|
implementations of Scheme, but Guile is our choice mainly out of a
|
|
|
|
desire to integrate nicely with the GNU Guix project, which is also
|
|
|
|
written in Guile.
|
|
|
|
|
|
|
|
|
|
|
|
@node What of Bash and its bootstraps?
|
|
|
|
@section What of Bash and its bootstraps?
|
|
|
|
|
|
|
|
One of the most important functions of Gash is to bootstrap GNU Bash
|
|
|
|
(@pxref{What is Bash?,,, bash, Bash Reference Manual}).
|
|
|
|
@dfn{Bootstrapping} is, briefly, going from having no software to
|
|
|
|
having software. What makes it hard is that you usually use software
|
|
|
|
to build software, and that snake has a habit of eating its own tail.
|
|
|
|
For example, Bash uses shell scripts to describe how it should be
|
|
|
|
built, so you need a shell to build a shell. What if this other shell
|
|
|
|
needs shell scripts too? Then you have a problem. Normally this
|
|
|
|
problem goes unnoticed, because you already have a shell that you got
|
|
|
|
from somebody else, and that somebody got their shell from somebody,
|
|
|
|
and this process works its way back to the dawn of time---er, the dawn
|
|
|
|
of Unix time, which is the early 70's---when the first shell emerged
|
|
|
|
from the primordial soup.
|
|
|
|
|
|
|
|
Where you really run head-long into this problem is when you have a
|
|
|
|
complete, fine-grained description of a system's software dependency
|
|
|
|
graph, like they have over at the GNU Guix project. Then, if you
|
|
|
|
follow the arrows back as far as you can, you can't miss the fact that
|
|
|
|
the first step in building the system is ``download many megabytes of
|
|
|
|
inscrutable binary programs from the Internet.'' There are a handful
|
|
|
|
of people working to change this, and Gash is a small part of this
|
|
|
|
larger project. For all the details, see the
|
|
|
|
@url{https://bootstrappable.org/, Bootstrappable Builds website}.
|
|
|
|
|
|
|
|
|
|
|
|
@node What does Scheme want from the shell?
|
|
|
|
@section What does Scheme want from the shell?
|
|
|
|
|
|
|
|
There is a long history of mixing Scheme and the shell. Most
|
|
|
|
famously, there is @url{https://scsh.net/, Scsh}, which is a hybrid
|
|
|
|
language that tries to combine the brevity of the shell with the
|
|
|
|
flexibility of Scheme. There are a few other projects that mix Scheme
|
|
|
|
or Lisp with shell syntax, and many projects mixing the shell with
|
|
|
|
other popular programming languages. A common goal of many of these
|
|
|
|
projects is to provide a way forward for shell scripts that have
|
|
|
|
become so large they are difficult to maintain. Having a language
|
|
|
|
that has a similar syntax to shell scripts but provides more ways to
|
|
|
|
structure code would allow these scripts to continue to grow cleanly.
|
|
|
|
|
|
|
|
On the other hand, it can be useful to provide a shell-like interface
|
|
|
|
for Scheme environments. For instance, the Guix System boots into
|
|
|
|
Guile and if anything goes wrong, it presents the user with a Scheme
|
|
|
|
REPL. It would be nice to be able to drive Guile using shell syntax,
|
|
|
|
since most of what you need to do in that context is manipulate files
|
|
|
|
and execute external utilities. Another Guix example is adding
|
|
|
|
build-system phases that are just shell scripts.@footnote{This one is
|
|
|
|
a little heretical, but I imagine it being attractive as Guix becomes
|
|
|
|
more popular.}
|
|
|
|
|
|
|
|
Ultimately, the future of Gash is yet to be explored, but there are
|
|
|
|
plenty of spaces it could gracefully move into. Patches are welcome!
|
|
|
|
|
|
|
|
@c *********************************************************************
|
|
|
|
@node Using Gash
|
|
|
|
@chapter Using Gash
|
|
|
|
|
|
|
|
Gash is a mostly complete POSIX-compatible shell, so it should mostly
|
|
|
|
work as you would expect. However, it is missing a few POSIX features
|
|
|
|
(@pxref{Missing features}), and does not really have any features
|
|
|
|
beyond POSIX. Specifically, many of the ``Bashisms'' that you may be
|
|
|
|
familiar with are not available.@footnote{@xref{Major Differences From
|
|
|
|
The Bourne Shell,,, bash, Bash Reference Manual}.}
|
|
|
|
|
|
|
|
In the following we cover the major features that work very well in
|
|
|
|
Gash. You should feel confident using these features as you would in
|
|
|
|
any other POSIX-compatible shell.
|
|
|
|
|
|
|
|
All kinds of command invocation should just work. This includes
|
|
|
|
setting temporary variables, redirects, and here-documents. All of
|
|
|
|
the POSIX-specified ``special'' built-ins are available except for
|
|
|
|
@code{times}. Only a handful of other built-ins are provided (like
|
|
|
|
@code{cd} and @code{pwd}).
|
|
|
|
|
|
|
|
All of the control structures are available and full-featured. This
|
|
|
|
means @code{for}, @code{while}, @code{until}, @code{if}, and
|
|
|
|
@code{case}. Functions and subshells also work fine.
|
|
|
|
|
|
|
|
Field splitting and quoting is well-tested and should work even in
|
|
|
|
exceptional cases, like @code{"$@@"} and when @code{$IFS} is
|
|
|
|
manipulated.
|
|
|
|
|
|
|
|
You can set variables and mark them read-only or exported. Many
|
2020-08-21 21:07:16 +01:00
|
|
|
special variables are available, and all of the variable operators
|
|
|
|
(like @code{$@{VARIABLE+alternate@}}) work.
|
2019-06-01 03:11:14 +01:00
|
|
|
|
|
|
|
Both types of command substitution work (that is, @code{$(...)} and
|
|
|
|
@code{`...`}), and can even be nested.
|
|
|
|
|
|
|
|
In general, Gash should not surprise you too much, and should run most
|
|
|
|
POSIX-compatible shell scripts.
|
|
|
|
|
2018-07-09 23:11:50 +01:00
|
|
|
|
|
|
|
@node Invoking Gash
|
|
|
|
@section Invoking Gash
|
|
|
|
|
|
|
|
The @command{gash} command is the sh interpreter.
|
|
|
|
|
|
|
|
@example
|
|
|
|
gash @var{option}@dots{} @file{FILE}
|
|
|
|
@end example
|
|
|
|
|
|
|
|
The @var{option}s can be among the following:
|
|
|
|
|
|
|
|
@table @code
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
@item -c@r{, }--command=@var{string}
|
|
|
|
Evaluate @var{string}, and then exit.
|
|
|
|
|
|
|
|
@item -e@r{, }--errexit
|
|
|
|
Exit immediately on any error.
|
2018-07-09 23:11:50 +01:00
|
|
|
|
|
|
|
@item -h@r{, }--help
|
|
|
|
Display help on invoking Gash, and then exit.
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
@item -p@r{, }--parse
|
|
|
|
Rather than evaluate a script, parse it and output its syntax tree.
|
|
|
|
|
2018-07-09 23:11:50 +01:00
|
|
|
@item -v@r{, }--version
|
|
|
|
Display the current version of Gash, and then exit.
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
@item -x@r{, }--xtrace
|
|
|
|
Print each command that is executed.
|
|
|
|
|
2018-07-09 23:11:50 +01:00
|
|
|
@end table
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
|
2020-08-21 20:49:24 +01:00
|
|
|
@node Using Gash from the Guile REPL
|
|
|
|
@section Using Gash from the Guile REPL
|
|
|
|
|
|
|
|
Gash defines a language specification that extends Guile, allowing you
|
|
|
|
to use shell syntax from the REPL. This is accomplished by using the
|
|
|
|
@code{language} REPL command:
|
|
|
|
|
|
|
|
@example
|
|
|
|
scheme@atchar{}(guile-user)> ,language sh
|
|
|
|
Happy hacking with Guile as Shell! To switch back, type `,L scheme'.
|
|
|
|
sh@atchar{}(guile-user)> echo "Hello Gash!"
|
|
|
|
Hello Gash!
|
|
|
|
$1 = 0
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
2019-06-01 03:11:14 +01:00
|
|
|
@node Missing features
|
|
|
|
@section Missing features
|
|
|
|
|
|
|
|
In this section, we cover some of the features that are currently
|
|
|
|
missing in Gash. Particularly, we look at the features specified by
|
|
|
|
POSIX that have not yet been implemented. This list is not
|
|
|
|
exhaustive, but covers the most glaring omissions.
|
|
|
|
|
|
|
|
@itemize @bullet
|
|
|
|
|
|
|
|
@item
|
|
|
|
Arithmetic substitution.
|
|
|
|
|
|
|
|
@item
|
2020-08-21 21:15:17 +01:00
|
|
|
Job control.
|
2019-06-01 03:11:14 +01:00
|
|
|
|
|
|
|
@item
|
|
|
|
Alias creation and substitution.
|
|
|
|
|
|
|
|
@item
|
|
|
|
Certain special variables. This includes @code{$ENV}; all the
|
|
|
|
localization variables like @code{$LANG}, @code{$LC_*}, and
|
|
|
|
@code{$NLSPATH}; the parent process ID variable (@code{$PPID}); and
|
|
|
|
the prompt variables (@code{$PS*}).
|
|
|
|
|
|
|
|
@item
|
|
|
|
Tilde expansion.
|
|
|
|
|
|
|
|
@item
|
|
|
|
Multi-line commands from the readline interface. If you press
|
|
|
|
@key{Enter} in the middle of a command (e.g., from within an
|
|
|
|
unfinished quotation) Gash will not ask for more input but rather
|
|
|
|
treat it as a syntax error and exit with an error message.
|
|
|
|
|
|
|
|
@end itemize
|
|
|
|
|
|
|
|
These features will be added to a future version of Gash. If you
|
|
|
|
would like to contribute to Gash by implementing one of these
|
|
|
|
features, please get in touch by writing to
|
|
|
|
@url{mailto:gash-devel@@nongnu.org, gash-devel@@nongnu.org}.
|
|
|
|
|
|
|
|
@c *********************************************************************
|
|
|
|
@node Hacking with Gash
|
|
|
|
@chapter Hacking with Gash
|
|
|
|
|
|
|
|
Have you ever wanted to write Scheme code that can understand and
|
|
|
|
manipulate shell scripts on the fly? Have you ever wanted your Scheme
|
|
|
|
scripts to have the breezy convenience of a shell script? You've come
|
|
|
|
to the right place! In this chapter we will look at all the tools
|
|
|
|
that Gash offers for exploring and illuminating the dark canyon that
|
|
|
|
separates Scheme and the shell.
|
|
|
|
|
|
|
|
|
|
|
|
@node Parsing shell scripts
|
|
|
|
@section Parsing shell scripts
|
|
|
|
|
|
|
|
If you're reading this, you probably have some familiarity with
|
|
|
|
Scheme, and you have probably heard of Scheme's @code{read} procedure.
|
|
|
|
Gash exports a very similar function from the @code{(gash parser)}
|
|
|
|
module. It's called @code{read-sh}, and as you may have guessed, it
|
|
|
|
reads shell code instead of Scheme code.
|
|
|
|
|
|
|
|
With Scheme code, the internal and external representations of the
|
|
|
|
code are basically the same, but this is not true with shell code.
|
|
|
|
You will have to learn and understand Gash's particular internal
|
|
|
|
representation of shell code to use it effectively. Fortunately, it
|
|
|
|
is designed to be familiar to a Scheme programmer, and we provide many
|
|
|
|
examples here to get you up to speed quickly. At the end of this
|
|
|
|
section, a formal definition of the internal representation is given
|
|
|
|
for reference.
|
|
|
|
|
|
|
|
@defun{read-sh [port]}
|
|
|
|
Read a complete shell command from the input port @var{port} if it is
|
|
|
|
specified, or from the current input port if @var{port} is not
|
|
|
|
specified. A @dfn{complete command} is essentially something an
|
|
|
|
interactive shell would be able to execute immediately without
|
|
|
|
prompting for more input. Technically, it corresponds to the
|
|
|
|
@code{complete_command} production rule defined in the POSIX
|
|
|
|
specification.
|
|
|
|
@end defun
|
|
|
|
|
|
|
|
For convenience, we also export the following procedure.
|
|
|
|
|
|
|
|
@defun{read-sh-all [port]}
|
|
|
|
Read all complete shell commands from the input port @var{port} if it
|
|
|
|
is specified, or from the current input port if @var{port} is not
|
|
|
|
specified. This works much like writing your own loop using
|
|
|
|
@code{read-sh}, but it should be more efficient. As a bonus, it
|
|
|
|
ensures that the result is always a list of commands.
|
|
|
|
@end defun
|
|
|
|
|
|
|
|
|
|
|
|
@node Internal representation examples
|
|
|
|
@subsection Internal representation examples
|
|
|
|
|
|
|
|
The easiest way to understand Gash's internal representation of shell
|
|
|
|
is through examples, so let's take a look at a few. To simplify the
|
|
|
|
examples, we are going abuse notation a little bit. Instead of
|
|
|
|
writing
|
|
|
|
|
|
|
|
@example
|
|
|
|
(call-with-input-string "@var{SHELL-EXAMPLE}" read-sh)
|
|
|
|
@result{}
|
|
|
|
@var{RESULT}
|
|
|
|
@end example
|
|
|
|
|
|
|
|
we will simply write
|
|
|
|
|
|
|
|
@example
|
|
|
|
@var{SHELL-EXAMPLE}
|
|
|
|
@result{}
|
|
|
|
@var{RESULT}
|
|
|
|
@end example
|
|
|
|
|
|
|
|
The major benefit of doing this is that we do not have to escape the
|
|
|
|
shell examples to be proper Scheme strings.
|
|
|
|
|
|
|
|
|
|
|
|
@subsubheading Simple commands
|
|
|
|
|
|
|
|
The parser can read simple commands and split them into their
|
|
|
|
constituent parts:
|
|
|
|
|
|
|
|
@example
|
|
|
|
echo foo
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" "foo")
|
|
|
|
|
|
|
|
echo $FOO
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-ref> "FOO"))
|
|
|
|
|
|
|
|
echo foo$BAR baz
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" ("foo" (<sh-ref> "BAR")) "baz")
|
|
|
|
@end example
|
|
|
|
|
|
|
|
It preserves quotes, since they are significant for field splitting
|
|
|
|
and escaping special characters:
|
|
|
|
|
|
|
|
@example
|
|
|
|
echo "$FOO"
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-quote> (<sh-ref> "FOO")))
|
|
|
|
|
|
|
|
echo *
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" "*")
|
|
|
|
|
|
|
|
echo '*'
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-quote> "*"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
It represents redirects and temporary variable assignments with a nod
|
|
|
|
to Scheme:
|
|
|
|
|
|
|
|
@example
|
|
|
|
CC=gcc CFLAGS='-O2 -g' make
|
|
|
|
@result{}
|
|
|
|
(<sh-exec-let> (("CC" "gcc")
|
|
|
|
("CFLAGS" (<sh-quote> "-O2 -g")))
|
|
|
|
"make")
|
|
|
|
|
|
|
|
wc < gash.texi
|
|
|
|
@result{}
|
|
|
|
(<sh-with-redirects> ((< 0 "gash.texi"))
|
|
|
|
(<sh-exec> "wc"))
|
|
|
|
|
|
|
|
POSIXLY_CORRECT=y sed N < file
|
|
|
|
@result{}
|
|
|
|
(<sh-with-redirects> ((< 0 "file"))
|
|
|
|
(<sh-exec-let> (("POSIXLY_CORRECT" "y"))
|
|
|
|
"sed" "N"))
|
|
|
|
|
|
|
|
;; Watch out for this one, though!
|
|
|
|
echo foo >| file
|
|
|
|
@result{}
|
|
|
|
(<sh-with-redirects> ((>! 1 "file"))
|
|
|
|
(<sh-exec> "echo" "foo"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
Here documents get processed by the parser, so they only have one
|
|
|
|
form:
|
|
|
|
|
|
|
|
@example
|
|
|
|
cat <<EOF > hello.scm
|
|
|
|
(display "Hello world!\n")
|
|
|
|
EOF
|
|
|
|
@result{}
|
2021-04-17 02:35:42 +01:00
|
|
|
(<sh-with-redirects> ((<< 0 (<sh-quote>
|
|
|
|
"(display \"Hello world!\\n\")\n"))
|
2019-06-01 03:11:14 +01:00
|
|
|
(> 1 "hello.scm"))
|
|
|
|
(<sh-exec> "cat"))
|
|
|
|
|
|
|
|
cat <<EOF > hello.sh
|
|
|
|
hi=Howdy; echo $hi world!
|
|
|
|
EOF
|
|
|
|
@result{}
|
|
|
|
(<sh-with-redirects> ((<< 0 (<sh-quote>
|
2021-04-17 02:35:42 +01:00
|
|
|
("hi=Howdy; echo " (<sh-ref> "hi")
|
|
|
|
" world!\n")))
|
2019-06-01 03:11:14 +01:00
|
|
|
(> 1 "hello.sh"))
|
|
|
|
(<sh-exec> "cat"))
|
|
|
|
|
|
|
|
;; That's not what we meant! Let's try again.
|
|
|
|
cat <<\EOF > hello.sh
|
|
|
|
hi=Howdy; echo $hi world!
|
|
|
|
EOF
|
|
|
|
@result{}
|
|
|
|
(<sh-with-redirects> ((<< 0 (<sh-quote> "hi=Howdy; echo $hi world!\n"))
|
|
|
|
(> 1 "hello.sh"))
|
|
|
|
(<sh-exec> "cat"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
|
|
|
@subsubheading Compound commands
|
|
|
|
|
|
|
|
Loops look like they do on the shell:
|
|
|
|
|
|
|
|
@example
|
|
|
|
while test $GASH = confusing
|
|
|
|
do
|
|
|
|
read-the-manual
|
|
|
|
done
|
|
|
|
@result{}
|
|
|
|
(<sh-while> (<sh-exec> "test" (<sh-ref> "GASH") "=" "confusing")
|
|
|
|
(<sh-exec> "read-the-manual"))
|
|
|
|
|
|
|
|
until cows come home; do wait; done
|
|
|
|
@result{}
|
|
|
|
(<sh-until> (<sh-exec> "cows" "come" "home")
|
|
|
|
(<sh-exec> "wait"))
|
|
|
|
|
|
|
|
for cat in Murray Flash Boots
|
|
|
|
do
|
|
|
|
pet $cat
|
|
|
|
done
|
|
|
|
@result{}
|
|
|
|
(<sh-for> ("cat" ("Murray" "Flash" "Boots"))
|
|
|
|
(<sh-exec> "pet" (<sh-ref> "cat")))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
If-statements and case-statements are a bit more like Scheme:
|
|
|
|
|
|
|
|
@example
|
|
|
|
if test $FLAVOR = chocolate
|
|
|
|
then
|
|
|
|
echo hooray!
|
|
|
|
elif test $FLAVOR = vanilla
|
|
|
|
then
|
|
|
|
echo yum!
|
|
|
|
else
|
|
|
|
echo too fancy!
|
|
|
|
fi
|
|
|
|
@result{}
|
|
|
|
(<sh-cond>
|
|
|
|
((<sh-exec> "test" (<sh-ref> "FLAVOR") "=" "chocolate")
|
|
|
|
(<sh-exec> "echo" "hooray!"))
|
|
|
|
((<sh-exec> "test" (<sh-ref> "FLAVOR") "=" "vanilla")
|
|
|
|
(<sh-exec> "echo" "yum!"))
|
|
|
|
(<sh-else>
|
|
|
|
(<sh-exec> "echo" "too" "fancy!")))
|
|
|
|
|
|
|
|
case $FLAVOR
|
|
|
|
in
|
|
|
|
*mint*) echo blech! ;;
|
|
|
|
*peanut*) echo so good! ;;
|
|
|
|
*maple*) echo maybe! ;;
|
|
|
|
*) echo sure why not! ;;
|
|
|
|
esac
|
|
|
|
@result{}
|
|
|
|
(<sh-case> (<sh-ref> "FLAVOR")
|
|
|
|
(("*mint*") (<sh-exec> "echo" "blech!"))
|
|
|
|
(("*peanut*") (<sh-exec> "echo" "so" "good!"))
|
|
|
|
(("*maple*") (<sh-exec> "echo" "maybe!"))
|
|
|
|
(("*") (<sh-exec> "echo" "sure" "why" "not!")))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
Brace groups and subshells are pretty straight-forward:
|
|
|
|
|
|
|
|
@example
|
|
|
|
@{ echo jump to the left;
|
|
|
|
echo step to the right; @}
|
|
|
|
@result{}
|
|
|
|
(<sh-begin>
|
|
|
|
(<sh-exec> "echo" "jump" "to" "the" "left")
|
|
|
|
(<sh-exec> "echo" "step" "to" "the" "right"))
|
|
|
|
|
|
|
|
(cd tmp; cat ephemera.txt)
|
|
|
|
@result{}
|
|
|
|
(<sh-subshell>
|
|
|
|
(<sh-exec> "cd" "tmp")
|
|
|
|
(<sh-exec> "cat" "ephemera.txt"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
@subsubheading Function definitions
|
|
|
|
|
|
|
|
Function definitions look more like Lisp:
|
|
|
|
|
|
|
|
@example
|
|
|
|
dance() @{
|
|
|
|
echo swing your hips now!
|
|
|
|
@}
|
|
|
|
@result{}
|
|
|
|
(<sh-defun> "dance"
|
|
|
|
(<sh-exec> "echo" "swing" "your" "hips" "now!"))
|
|
|
|
|
|
|
|
;; Even this weird thing works!
|
|
|
|
dance_to_the_beat() @{
|
|
|
|
sed 's/beat/swing hips/g'
|
|
|
|
@} < beats.txt
|
|
|
|
@result{}
|
|
|
|
(<sh-defun> "dance_to_the_beat"
|
|
|
|
(<sh-with-redirects> ((< 0 "beats.txt"))
|
|
|
|
(<sh-exec> "sed" (<sh-quote> "s/beat/move hips/g"))))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
|
|
|
@subsubheading Pipelines and asynchronous commands
|
|
|
|
|
|
|
|
Pipelines collect commands into a list:
|
|
|
|
|
|
|
|
@example
|
|
|
|
source | sink
|
|
|
|
@result{}
|
|
|
|
(<sh-pipeline> (<sh-exec> "source") (<sh-exec> "sink"))
|
|
|
|
|
|
|
|
cut -d ' ' -f 4 < ice-cream.txt \
|
|
|
|
| grep chocolate \
|
|
|
|
| sed 's/mint/peanut butter/g'
|
|
|
|
@result{}
|
|
|
|
(<sh-pipeline>
|
2021-04-17 02:31:45 +01:00
|
|
|
(<sh-with-redirects> ((< 0 "ice-cream.txt"))
|
2019-06-01 03:11:14 +01:00
|
|
|
(<sh-exec> "cut" "-d" (<sh-quote> " ") "-f" "4"))
|
|
|
|
(<sh-exec> "grep" "chocolate")
|
|
|
|
(<sh-exec> "sed" (<sh-quote> "s/mint/peanut butter/g")))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
Asynchronous commands are simply regular commands wrapped with
|
|
|
|
@code{<sh-async>}:
|
|
|
|
|
|
|
|
@example
|
|
|
|
helpful-daemon &
|
|
|
|
@result{}
|
|
|
|
(<sh-async> (<sh-exec> "helpful-daemon"))
|
|
|
|
|
|
|
|
;; So-so dancing.
|
|
|
|
swing-hips ; flail-arms
|
|
|
|
@result{}
|
|
|
|
(<sh-begin>
|
|
|
|
(<sh-exec> "swing-hips")
|
|
|
|
(<sh-exec> "flail-arms"))
|
|
|
|
|
|
|
|
;; Great dancing.
|
|
|
|
swing-hips & flail-arms
|
|
|
|
@result{}
|
|
|
|
(<sh-begin>
|
|
|
|
(<sh-async> (<sh-exec> "swing-hips"))
|
|
|
|
(<sh-exec> "flail-arms"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
|
|
|
@subsubheading Boolean expressions
|
|
|
|
|
|
|
|
The logical ``and'' and ``or'' operators are binary, left-associative
|
|
|
|
and have equal precedence:
|
|
|
|
|
|
|
|
@example
|
|
|
|
foo && bar || baz
|
|
|
|
@result{}
|
|
|
|
(<sh-or> (<sh-and> (<sh-exec> "foo")
|
|
|
|
(<sh-exec> "bar"))
|
|
|
|
(<sh-exec> "baz"))
|
|
|
|
|
|
|
|
foo || bar && baz
|
|
|
|
@result{}
|
|
|
|
(<sh-and> (<sh-or> (<sh-exec> "foo")
|
|
|
|
(<sh-exec> "bar"))
|
|
|
|
(<sh-exec> "baz"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
The logical ``not'' operator wraps a command or pipeline:
|
|
|
|
|
|
|
|
@example
|
|
|
|
! false
|
|
|
|
@result{}
|
|
|
|
(<sh-not> (<sh-exec> "false"))
|
|
|
|
|
|
|
|
! cat flavors.txt | grep mint
|
|
|
|
@result{}
|
|
|
|
(<sh-not> (<sh-pipeline>
|
|
|
|
(<sh-exec> "cat" "flavors.txt")
|
|
|
|
(<sh-exec> "grep" "mint")))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
|
|
|
@subsubheading Variables and command substitution
|
|
|
|
|
|
|
|
Variable operators have names inspired by Scheme:
|
|
|
|
|
|
|
|
@example
|
|
|
|
echo $@{FOO+bar@}
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-ref-and> "FOO" "bar"))
|
|
|
|
|
|
|
|
echo $@{FOO:+bar@}
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-ref-and*> "FOO" "bar"))
|
|
|
|
|
|
|
|
echo $@{FOO-bar@}
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-ref-or> "FOO" "bar"))
|
|
|
|
|
|
|
|
echo $@{FOO=bar@}
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-ref-or!> "FOO" "bar"))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
Command substitution is a simple wrapper:
|
|
|
|
|
|
|
|
@example
|
|
|
|
echo $(cat message.txt)
|
|
|
|
@result{}
|
|
|
|
(<sh-exec> "echo" (<sh-cmd-sub> (<sh-exec> "cat" "message.txt")))
|
|
|
|
@end example
|
|
|
|
|
|
|
|
That's it for examples. By this point, you should have a pretty good
|
|
|
|
sense for what Gash is going to give you. If you need to know more,
|
|
|
|
you can always do your own experiments or read the formal definition
|
|
|
|
in the next section.
|
|
|
|
|
|
|
|
|
|
|
|
@node Internal representation definition
|
|
|
|
@subsection Internal representation definition
|
|
|
|
|
|
|
|
The internal representation of shell code in Gash has the following
|
|
|
|
form. (If Gash ever returns something that does not have this form,
|
|
|
|
please file a bug!)
|
|
|
|
|
|
|
|
@example
|
|
|
|
sync ::= exp
|
|
|
|
| ('<sh-async> exp)
|
|
|
|
|
|
|
|
exp ::= pipe
|
|
|
|
| ('<sh-and> exp1 exp2)
|
|
|
|
| ('<sh-or> exp1 exp2)
|
|
|
|
| ('<sh-not> pipe)
|
|
|
|
|
|
|
|
pipe ::= cmd*
|
|
|
|
| ('<sh-pipeline> cmd* ...)
|
|
|
|
|
|
|
|
cmd* ::= cmd
|
|
|
|
| ('<sh-defun> name sync ...)
|
|
|
|
|
|
|
|
cmd ::= ('<sh-exec> word ...)
|
|
|
|
| ('<sh-exec-let> ((var var-word) ...) word ...)
|
|
|
|
| ('<sh-with-redirects> (redir ...) [cmd])
|
|
|
|
| ('<sh-set!> (var word) ...)
|
|
|
|
| ('<sh-subshell> sync ...)
|
|
|
|
| ('<sh-for> (name (word ...)) sync ...)
|
|
|
|
| ('<sh-case> word ((pattern-word ...) sync ...) ...)
|
|
|
|
| ('<sh-cond> (list sync ...) ... [('<sh-else> sync ...)])
|
|
|
|
| ('<sh-while> list sync ...)
|
|
|
|
| ('<sh-until> list sync ...)
|
|
|
|
| ('<sh-begin> sync ...)
|
|
|
|
|
|
|
|
redir ::= ('> fdes word)
|
|
|
|
| ('< fdes word)
|
|
|
|
| ('>& fdes word)
|
|
|
|
| ('<& fdes word)
|
|
|
|
| ('>> fdes word)
|
|
|
|
| ('<> fdes word)
|
|
|
|
| ('>! fdes word)
|
|
|
|
| ('<< fdes word)
|
|
|
|
|
|
|
|
word ::= string
|
|
|
|
| (word ...)
|
|
|
|
| ('<sh-quote> word)
|
|
|
|
| ('<sh-cmd-sub> sync ...)
|
|
|
|
| ('<sh-ref> var)
|
|
|
|
| ('<sh-ref-or> var [word])
|
|
|
|
| ('<sh-ref-or*> var [word])
|
|
|
|
| ('<sh-ref-or!> var [word])
|
|
|
|
| ('<sh-ref-or!*> var [word])
|
|
|
|
| ('<sh-ref-assert> var [word])
|
|
|
|
| ('<sh-ref-assert*> var [word])
|
|
|
|
| ('<sh-ref-and> var [word])
|
|
|
|
| ('<sh-ref-and*> var [word])
|
|
|
|
| ('<sh-ref-except-min> var [word])
|
|
|
|
| ('<sh-ref-except-max> var [word])
|
|
|
|
| ('<sh-ref-skip-min> var [word])
|
|
|
|
| ('<sh-ref-skip-max> var [word])
|
|
|
|
| ('<sh-ref-length> var)
|
|
|
|
|
|
|
|
var ::= string
|
|
|
|
| ("LINENO" integer)
|
|
|
|
@end example
|
|
|
|
|
|
|
|
|
|
|
|
@node Using shell-like constructs from Scheme
|
|
|
|
@section Using shell-like constructs from Scheme
|
|
|
|
|
|
|
|
Gash also has some features for writing Scheme code with shell-like
|
|
|
|
semantics. Indeed, this is how Gash works internally. It interprets
|
|
|
|
shell code into calls into its shell interface. The two modules that
|
|
|
|
provide this interface are @code{(gash shell)} and @code{(gash
|
|
|
|
environment)}. The shell module provides control structures with
|
|
|
|
shell semantics. For instance, you can use @code{sh:for} to update a
|
|
|
|
given shell variable and run a thunk for each element of a list of
|
|
|
|
strings. The environment module allows you to manipulate shell
|
|
|
|
variables and functions, as well as a number of other things.
|
|
|
|
|
|
|
|
Truthfully, these interfaces are not ready for public consumption yet.
|
|
|
|
They are in need of some careful redesign to be useful outside of Gash
|
|
|
|
itself. If this sounds interesting to you, please get in touch by
|
|
|
|
writing to @url{mailto:gash-devel@@nongnu.org,
|
|
|
|
gash-devel@@nongnu.org}.
|
|
|
|
|
|
|
|
|
2018-07-09 23:11:50 +01:00
|
|
|
@c *********************************************************************
|
|
|
|
@node GNU Free Documentation License
|
|
|
|
@appendix GNU Free Documentation License
|
|
|
|
@include fdl-1.3.texi
|
|
|
|
|
|
|
|
@bye
|
|
|
|
|
|
|
|
@c Local Variables:
|
|
|
|
@c ispell-local-dictionary: "american";
|
|
|
|
@c End:
|