mdshell - an interactive shell for writing Markdown document

Stuff that has yet to be sorted into a category.
Post Reply
Message
Author
User avatar
sc0ttman
Posts: 2812
Joined: Wed 16 Sep 2009, 05:44
Location: UK

mdshell - an interactive shell for writing Markdown document

#1 Post by sc0ttman »

mdshell

An interactive shell for generating Markdown documents, with
support for embedding executable commands in your markdown!

Usage: mdshell <path/to/file.md>

Write the file line by line.
Hit ENTER 3 times to exit and save the file.

Demo:
Image

More info:

I wanted a way to create Markdown documents in the terminal, and to
embed shell commands in the Markdown, so I could generate the final
file dynamically - the results of the embedded shell commands are
saved in the final file.


The script:

Code: Select all

#!/bin/bash

# sc0ttman 
# Based on an example script by Stéphane Chazelas.

if [ ! "$1" ] || [ "$1" = "-h" ] || [ "$1" = "-help" ] || [ "$1" = "--help" ];then
cat << HELP_MSG

 An interactive shell for writing Markdown documents, with
 support for embedding bash sub-shells \$() in your Markdown!

 Usage:  mdshell <path-to-file> # creates the file if it doesn't exist

 Note backtick subshells (\`\`) not supported, only \$()

 Example:   Add \$(uname) in your Markdown and the saved
            file will contain your system info.

HELP_MSG
exit
fi

# define default settings

# define run-time vars used by this program
prev_line=none
line_is_bash=false
line_was_bash=false
multi_line_string=false
was_multi_line_string=false
command=''
command_line_count=0

if [ ! -f "$1" ];then
  mkdir -p "$(dirname "$1")"
  touch "$1"
fi

while :
do
  # the user just hit ENTER, so read the line they input
  #  -e  use readline
  #  -r  dont escape backslashes include them as literals chars
  read -er line

  # if line starts with ``` we know it's a markdown line, not part of a bash sub-shell
  [ "$(echo "$line" | grep '^```')" != "" ] && line_is_bash=false

  # if the line contains $( then the user is starting a bash sub-shell on this line
  [ "$(echo "$line" | grep -m1 '$(')" != "" ] && line_is_bash=true

  # if the line is not bash, then it's also not a multi line string
  #if [ "$line_is_bash" = false ];then
  #  multi_line_string=false
  #fi

  # if the previous last (one before last entered) was part of a multi-line string,
  # then this line probably is too, and so it's part of a bash command
  [ "$was_multi_line_string" = true ] && line_was_bash=true

  # the the line given was bash, not markdown, we need to interpret it
  if [ "$line_is_bash" = true ];then

    # count the bumber of double quotes, and chck if that number is even
    quote_count=$(echo "$line" | tr -cd '"' | wc -c)
    quote_count_is_even=$(( ${quote_count} %2 ))

    # if $line has quotes, and an odd number of them, we moved in/out of a string
    if [ $quote_count -gt 0 ] && [ $quote_count_is_even -ne 0 ];then
      # toggle whether in a string or not
      if [ "$multi_line_string" = true ];then
        multi_line_string=false    # toggle it
      else
        multi_line_string=true     # toggle it
      fi
    fi

    # while we are in a bash sub-shell, lets save each line in the $command var
    if [ "$command" = "" ];then
      command="$line"
    else
      command="$command\n$line"
    fi
    command_line_count=$(($command_line_count + 1))

    # check if the command has a closing parenthesis ) - cos then we might be ending the sub-shell
    subshell_has_ended="$(echo "$command" | grep -Eq ')' && echo true || echo false)"

    # if line is part of a multi string, it's been saved into $command, so skip
    if [ "$multi_line_string" = true ];then

      line_is_bash=true

    # else if we detected the end of a sub-shell, lets evaluate it, get its output
    # and then save that to our markdown file, instead of the bash commands themselves
    elif [ "$subshell_has_ended"  = true ];then

      # strip any leading chars up to the sub-shell invocation '$(' and
      # strip any chars after the sub-shell, and
      # keep only the command
      pre_text="$(echo "$command" | sed -e 's/$(.*//' -e 's/)$//')"
      post_text="$(echo "$command" | sed 's/.*$(.*)//g')"
      if [ "$pre_text"  != "" ] || [ "$post_text"  != "" ];then
        command="$(echo "$command" | sed -e "s/^$pre_text//g" -e "s/$post_text//")"
        [ "$post_text" = "$command" ] && post_text=""
      fi

      # if previous line was not part of a string, then it each was a separate command
      if [ "${was_multi_line_string}" = false ];then
        # each line is a separate command, so replace newlines with semi-colons
        result="$(eval $(echo -e "${command//\\/\\\\}" | sed s'/\\n/;/g' | tr -d '`' | sed -e 's/$(//g' -e 's/)$//g') 2>/dev/null)"
        retval=$?
      else
        result="$(eval $(echo -e "${command//\\/\\\\}" | tr -d '`' | sed -e 's/$(//g' -e 's/)$//g') 2>/dev/null)"
        retval=$?
      fi
      [ "$was_multi_line_string" = false ] && line_is_bash=false
      [ "$multi_line_string" = false ] && line_is_bash=false
      [ $retval -eq 0 ] && text="$text\n$pre_text$result$post_text" && line_is_bash=false
    fi

  elif [ "$line_is_bash" = false ];then

      command_line_count=0
      command=""
      text="$text\n$line"
      multi_line_string=false
      [ -z "${prev_line}" ] && [ -z "$line" ] && break

  fi

  ###### done working out what was in $line #######

#  xmessage "
#  was multi-line string: $was_multi_line_string
#  multi-line string:     $multi_line_string
#  line_was_bash:         $line_was_bash
#  line_is_bash:          $line_is_bash
#  command line count:    '${command_line_count}'
#  command:               '${command//\\/\\\\}'
#  result:                '${result}'
#  "

  was_multi_line_string=${multi_line_string}
  line_was_bash=${line_is_bash}
  prev_line="$line"
  [ ${retval:-1} -eq 0 ] && result=''
done

echo -e "$text" > "$1"

echo
echo '------------------------------'
echo
echo "File $1 created:"
echo
less -X "$1"

unset prev_line
unset line_is_bash
unset multi_line_string
unset quote_count_is_even
unset file
unset text
unset command
unset command_line_count
unset result
unset retval

exit 0
Last edited by sc0ttman on Mon 04 Feb 2019, 22:52, edited 4 times in total.
[b][url=https://bit.ly/2KjtxoD]Pkg[/url], [url=https://bit.ly/2U6dzxV]mdsh[/url], [url=https://bit.ly/2G49OE8]Woofy[/url], [url=http://goo.gl/bzBU1]Akita[/url], [url=http://goo.gl/SO5ug]VLC-GTK[/url], [url=https://tiny.cc/c2hnfz]Search[/url][/b]

User avatar
sc0ttman
Posts: 2812
Joined: Wed 16 Sep 2009, 05:44
Location: UK

#2 Post by sc0ttman »

Eventually this might form part of a larger CLI based program for writing a blog, or
possibly a puppy linux wiki/documentation builder.

However, first I would very much like to clean up the code in 'mdshell'.

It works, though breakable, but I think it works 'by accident or luck' .... I'm not
sure the code works as intended, and might be way more complex and convoluted
than needed ... although I get the results I want (somehow).

Although the approach I took will always be 'dumb', so don't expect too much.

------

And just FYI, here is a great script for converting markdown to HTML:

https://daringfireball.net/projects/markdown/
[b][url=https://bit.ly/2KjtxoD]Pkg[/url], [url=https://bit.ly/2U6dzxV]mdsh[/url], [url=https://bit.ly/2G49OE8]Woofy[/url], [url=http://goo.gl/bzBU1]Akita[/url], [url=http://goo.gl/SO5ug]VLC-GTK[/url], [url=https://tiny.cc/c2hnfz]Search[/url][/b]

Post Reply