Finally, we can do Unix shell programming in Scala Li Haoyi, of Scala.js fame, has succeeded in fulfilling the dream of replacing Bash / Perl / Awk / Python with a modern programming language.
http://www.lihaoyi.com/AmmoniteTop features:
- Everything in this shell is compiled to JVM, then run
- Everything is statically type-checked - including command flags, file paths, pieces of paths, or output of "ls"
- Autocomplete and syntax highlighting: both for file paths and for Scala values
- Since this is just a JAR library and a class loader, we get the full power of the Scala programming language, not just a subset
- We can use any Scala or Java libraries from Maven repositories; they will be downloaded and locally cached
- We can write "Ammonite/Scala scripts" and import them into your shell or run as standalone applications
- The Ammonite/Scala shell can be embedded in third-party applications or used as a remote debug console over ssh
I really would like to forget
bash and start using
amm. I am going to give it a serious try!
First steps0. Install:
mkdir ~/.ammonite && curl -L -o ~/.ammonite/predef.sc https://git.io/vo4wx
sudo curl -L -o /usr/local/bin/amm https://git.io/vKSOR && sudo chmod +x /usr/local/bin/amm
Run the Ammonite/Scala shell:
amm
1. There are three kinds of things we can do in Amm:
- write plain Scala code; no need to write boilerplate "objects" to wrap plain values or functions
import scala.util.Try
val x = Try( 1/0 )
def f(x:Int) = x+1
- run a built-in shell command: ls, cd, ...
- run an external command: git status, ...
2. Filesystem paths and the names of external commands must be typed. Special syntax is used for these things; here are some basic examples.
The following are equivalent:
cd (Path("/private/tmp"))
cd! Path("/private/tmp")
cd! Path("private/tmp", root)
cd! root/"private"/"tmp"
cd! root/'private/'tmp
cd! Path("tmp", root/'private)
Return to the user's $HOME directory:
cd! Path(sys.env("HOME"))
The following are equivalent:
cd! up
cd! Path("..", wd)
Similar syntax applies to other built-in commands, such as ls, rm, mkdir, cp, mv, read, write.
Note that wd and cwd are Path values, not commands.
For external commands, the % or %% prefix needs to be used. Command arguments can be symbols or strings.
The following are equivalent:
%git("status")
%git('status)
%git "status"
%git 'status
3. There are some special helpers for going over files in directories, reading lines from files, etc.
4. Output of one expression can be "piped" into another (this is just syntactic sugar: | for map, |? for filter, || for flatMap, etc.)
List all files in current directory, filter pathnames by regular expression using built-in `grep`:
ls! wd |? grep! "li.*e".r
This command will go over all files in current directory with .csv extension; for each file, read all lines as strings:
@ ls! wd |? (_.ext == "csv") | read.lines
res80: Seq[Vector[String]] = List(
Vector(
"""
...
(The result was truncated in the example above.)
Note that we got a data structure
Seq[Vector[String]], corresponding to the sequence of all files, and for each file, a
Vector of its lines. Now we can use the full power of Scala collections to transform this data structure.
So far this sounds very promising! I'm going to try living in `amm` instead of `bash` and start writing `amm` shell scripts.