Skip to content

mitchpaulus/mshell

Repository files navigation

mshell

Warning

This is still very much an experiment. I still am changing significant parts of the syntax and semantics.

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.

Examples

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. You can run these examples like:

msh file_with_contents.msh < input_file_to_process.txt
awk -f file_with_contents.awk < input_file_to_process.txt
# OR (using 1st example)
msh -c ':: len wl' < input_file_to_process.txt
awk 'END { print NR }' < input_file_to_process.txt

Note that you'll also need the environment variable MSHSTDLIB pointing to the file at lib/std.msh.

# 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 uw

# 6. Print every input line in which the last field is more than 4
# $NF > 4
:: (wsplit :-1: toFloat 4 >) filter uw

# 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 uw

# 11. Print every line longer than 80 characters
# length($0) > 80
:: (len 80 >) filter uw

# 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 }
wt (:1:, :0: wjoin wl) each

# 14. Exchange the first two fields of every line and then print the line
# { temp = $1; $1 = $2; $2 = temp; print }
wt (:1:, :0:, 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

TODO

  • Dictionaries
  • Improved error messages
  • Type checking

References/Inspirations