mshell
is my personal scripting language, meant to replace short shell scripts (< ~100 LOC) and other shell one-liners.
A concatenative language is a good fit for this purpose, as it allows for easy composition of simple functions and pipelines.
The goal is to provide most of the simple Gnu utilities as part of the language, while making calls to external programs and pipelines simple and easy.
Future goals are to even add some type safety.
Best way to understand purpose and syntax of mshell
is to see it in action. Here are some examples.
Better Awk One-liners. Examples from the awk
book, translated to mshell
# 1. Print the total number of input lines:
# END { print NR }
.. len wl
# 2. Print the 10th input line:
# NR == 10
.. :9: wl
# 3. Print the last field of every input line:
# { print $NF }
wt (:-1: wl) each
# 4. Print the last field of the last input line:
# { field = $NF }
# END { print field }
wt :-1: :-1: wl
# 5. Print every input line with more than four fields
# NF > 4
.. (wsplit len 4 >) filter (wl) each
# 6. Print every input line in which the last field is more than 4
# $NF > 4
.. (wsplit :-1: toFloat 4 >) filter (wl) each
# 7. Print the total number of fields in all input lines
# { nf = nf + NF }
# END { print nf }
.. (wsplit len) map sum wl
# 8. Print the total number of lines that contain 'Beth'
# /Beth/ { nlines = nlines + 1 }
# END { print nlines }
.. ("Beth" in) filter len wl
# 9. Print the largest first field and the line that contains it (assumes some $1 is positive):
# $1 > max { max = $1; line = $0 }
# END { print max, line }
-99999999 max! "" max-line!
..
(
dup line! # Store line
wsplit :0: toFloat dup first-item! # Store first item
[(@max >) (@first-item max! @line max-line!)] if
) each
@max str w " " w @max-line wl
# 10. Print every line that has at least one field
# NF > 0
.. (wsplit len 0 >) filter (wl) each
# 11. Print every line longer than 80 characters
# length($0) > 80
.. (len 80 >) filter (wl) each
# 12. Print the number of fields in every line followed by the line itself
# { print NF, $0 }
.. (dup wsplit len w " " w wl) each
# 13. Print the first two fields in opposite order, of every line
# { print $2, $1 }
.. (wsplit dup :0: swap :1: w " " w wl) each
# 14. Exchange the first two fields of every line and then print the line
# { temp = $1; $1 = $2; $2 = temp; print }
wt
(
[]
over :1: append
over :0: append
swap 2: +
wjoin wl
) each
# 15. Print every line with the first field replaced by the line number
# { $1 = NR; print }
wt 1 lineNum!
(
@lineNum str 0 setAt
wjoin wl
@lineNum 1 + lineNum!
) each
# 16. Print every line after erasing the second field
# { $2 = ""; print }
wt ("" 1 setAt wjoin wl) each
# 17. Print in reverse order the fields of every line
# { for (i = NF; i > 0; i = i - 1) printf (i == 1 ? "%s" : "%s "), $i
# printf "\n"
# }
wt (reverse wjoin wl) each
# 18. Print the sums of the fields of every line
# { sum = 0
# for (i = 1; i <= NF; i = i + 1) sum = sum + $i
# print sum
# }
wt ((toFloat) map sum str wl) each
# 19. Add up all fields in all lines and print the sum
# { for (i = 1; i <= NF; i = i + 1) sum = sum + $i }
# END { print sum }
wt ((toFloat) map sum) sum str wl
# 20. Print every line after replacing each field by its absolute value
# { for (i = 1; i <= NF; i = i + 1) $i = ($i < 0) ? -$i : $i; print }
wt ((toFloat abs str) map wjoin wl) each
Simpler execution of common shell idioms
Objective | sh |
mshell |
---|---|---|
Print the number of files in the current directory | ls | wc -l |
"*" glob len wl |
find /xargs |
find . -t x -name '*.sh' -print0 | xargs -0 mycommand |
[mycommand [find . -t x -name "*.sh"]]o; |
head |
head -n 10 |
.. :10 uw |
tail |
tail -n 10 |
.. :-10 uw |
wc |
wc -l |
.. len wl |
grep |
grep 'pattern' |
.. ("pattern" in) filter uw |
cut |
cut -d ';' -f 2 |
.. (";" split :1: wl) each |
- Type checking
- Improved error messages
- Dictionaries
- Built-in coreuitls,
cp
,mv
,rm
,mkdir