You might be interested in an experiment of mine, https://github.com/tonyg/racket-something/blob/master/src/so..., which combines an indentation-based reader with a handler for unbound variables to permit fluid interoperation between Racket procedures and external programs.
Here's an example (https://github.com/tonyg/racket-something/blob/master/exampl...):
#lang something/shell
// Simple demos
ls -la $HOME | grep "^d" | fgrep -v "."
ls -la | wc -l | read-line |> string-split |> car |> string->number |> \
printf "There are ~a lines here." | sed -e "s: are : seem to be :"
(newline)
def ps-output
pipeline
ps -wwwax
preserve-header 1 {: grep "racket" }
space-separated-columns [string->number]
|> csv-expr->table
print ps-output
def message-box text:
whiptail --title "Testing" --ok-button "OK" --msgbox text 8 50
message-box "This is pretty cool."
`ls`, `grep`, `whiptail` and so on are unbound Racket variables, and the macros forming part of the `something/shell` dialect replace them with calls to the external programs, sorting out the plumbing, pipes and so on. `read-line`, `car`, `string-split`, etc are ordinary Racket functions from the standard libraries.(EDIT): Here's a screencast of an interactive session with the shell. Very simple, but shows some of the basics: https://asciinema.org/a/83450