Gar 是一个 Bash 脚本程序,用于治理 Markdown 文档我的项目,可将 Markdown 文档汇合其转化为 HTML 文档汇合。Gar 的运行,依赖 pandoc,git,tree 以及一个能在 Shell(命令行)里关上指定网页文件的网页浏览器。Gar 默认将 Firefox 作为网页浏览器,然而可在文档我的项目根目录的 gar.conf 文件中指定其它符合要求的网页浏览器。

文档我的项目初始化

命令:gar init 文档我的项目名

例如:

$ gar init demo[master (root-commit) 6f7dd1c] init 1 file changed, 2 insertions(+) create mode 100644 gar.conf

以下命令可察看 gar init 发明了什么:

$ cd demo$ ls -a  ..  gar.conf  .git  output  source  图片
$ gar treedemo├── gar.conf├── output├── source└── 图片
$ git logcommit 6f7dd1c23c0cc8b18eb84b6a5236605eebd2bfbf (HEAD -> master)Author: xxx <xxx@yyy.zzz>Date:   Tue Mar 9 07:30:53 2021 +0800    init

文档我的项目初始化后,文档的撰写和编辑工作次要在 source 目录进行。Gar 将 Markdown 文档转化为 HTML 文档后,放在 output 子目录内。

文集创立与删除

进入 source 目录:

$ cd source

创立文集 foo:

$ gar new-class foo

可应用 gar tree 查看文档我的项目的目录变动,察看 gar new-class 发明了什么:

$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo└── 图片    └── foo

可一次创立多个文集:

$ gar new-class a b c

后果为:

$ gar treedemo├── gar.conf├── output│   ├── a│   ├── b│   ├── c│   └── foo├── source│   ├── a│   ├── b│   ├── c│   └── foo└── 图片    ├── a    ├── b    ├── c    └── foo

删除文集:

$ gar remove-class a b c$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo└── 图片    └── foo

可在文集里创立子文集:

$ cd foo$ gar new-class a$ gar treedemo├── gar.conf├── output│   └── foo│       └── a├── source│   └── foo│       └── a└── 图片    └── foo        └── a

可创立文集档次序列:

$ gar new-class b/c/d/e/f$ gar treedemo├── gar.conf├── output│   └── foo│       ├── a│       └── b│           └── c│               └── d│                   └── e│                       └── f├── source│   └── foo│       ├── a│       └── b│           └── c│               └── d│                   └── e│                       └── f└── 图片    └── foo        ├── a        └── b            └── c                └── d                    └── e                        └── f

将试验复盘:

$ gar remove-class a b$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo└── 图片    └── foo

提醒,目前工作目录仍为 source/foo。

创立和删除文档

在文集目录内,应用 gar new-post 创立内容为空的文档。例如,在 source/foo 内创立 test.md 文档:

$ gar new-post test.md[master 6a894eb] Added test.md 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 source/foo/test.md

查看产生了什么:

$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo│       └── test.md└── 图片    └── foo        └── test
$ git logcommit 6a894eb44cf66e95b4ff938e05c019b7218039e0 (HEAD -> master)Author: xxx <xxx@yyy.zzz>Date:   Tue Mar 9 08:16:49 2021 +0800    Added test.mdcommit 6f7dd1c23c0cc8b18eb84b6a5236605eebd2bfbfAuthor: xxx <xxx@yyy.zzz>Date:   Tue Mar 9 07:30:53 2021 +0800    init

每次创立文档时,Gar 会调用 git 记录文档创立历史。

可一次创立多份空文档:

$ gar new-post a.md b.md c.md[master 25e7d65] Added a.md b.md c.md 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 source/foo/a.md create mode 100644 source/foo/b.md create mode 100644 source/foo/c.md
$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo│       ├── a.md│       ├── b.md│       ├── c.md│       └── test.md└── 图片    └── foo        ├── a        ├── b        ├── c        └── test

应用 gar remove-post 可删除当前工作目录下的文档。以下命令可将上述创立的文档一举删除:

$ gar remove-post test.md a.md b.md c.md[master 189da8b] Remove test.md a.md b.md c.md 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 source/foo/a.md delete mode 100644 source/foo/b.md delete mode 100644 source/foo/c.md delete mode 100644 source/foo/test.md

每次删除文档,git 会记录文档的删除历史。

通过上述操作后,这个试验性的文档我的项目又复盘为:

$ gar treedemo├── gar.conf├── output│   └── foo├── source│   └── foo└── 图片    └── foo

网页的生成和预览

记住,以后的工作目录仍然是 source/foo。上面的命令从新创立 test.md:

$ gar new-post test.md

而后用文本编辑器关上 test.md,减少以下内容:

<!--.. title: Hello Gar!-->这只是一份无用的示例文档。

应用 gar convert 命令可将文档 test.md 转换为网页文件 test.html,并将其置于 文档我的项目根目录/output/foo 目录内:

$ gar convert test.md

查看文档目录产生的变动:

$ gar treedemo├── gar.conf├── output│   └── foo│       └── test.html├── source│   └── foo│       └── test.md└── 图片    └── foo        └── test

假使工作目录(以后文集)内有多份文档,也能够一次性将其转换为一组网页文件,例如:

$ gar convert test.md a.md b.md c.md

应用 gar preview 命令,可将文档转化为网页文件,并由 Gar 默认的网页浏览器关上:

$ gar preview test.md

gar preview 不反对多份文档一次性转换和预览。

附录

Gar 的全副代码:

#!/bin/bashSCRIPT_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)GAR_CONF=gar.confSOURCE=sourceIMAGES=图片OUTPUT=outputfunction error_msg {    echo $1    exit -1}function goto_root {    if [ ! -e $GAR_CONF ]    then        if [ $(pwd) = / ]        then            echo "gar.conf Not found!"        else            cd ..            goto_root          fi    fi}function class_name {    TARGET_PATH=$1    goto_root    SOURCE_PATH=$(pwd)/$SOURCE/    echo ${TARGET_PATH//$SOURCE_PATH/}}function get_title {    NAME=${1%.md}    # 将文件名里用于距离中文和英文的短线替换为空格    L_SPACE_PAT='s/\([^a-zA-Z]\)-/\1 /g'    R_SPACE_PAT='s/-\([^a-zA-Z]\)/ \1/g'    FANCY_NAME="$(echo $NAME | sed -e "$L_SPACE_PAT; $R_SPACE_PAT")"    # 文章题目    PREFIX="^[[:space:]]*\.\.[[:space:]]*title:[[:space:]]*"    TITLE=$(grep -o "${PREFIX}.*$" $1 | sed -e "s/${PREFIX}\(.*\)$/\1/g")    if [ -z "$TITLE" ]    then        TITLE=$FANCY_NAME    fi    echo $TITLE}function markdown_to_html {    HTML=$OUTPUT/$1/${2%.*}.html    pandoc $SOURCE/$1/$2 -s --mathjax \           -c $SCRIPT_PATH/gar.css \           --highlight-style pygments \           --metadata title="$TITLE" -o $HTML}case $1 in    init)        case $2 in            "")                error_msg "You should tell me the name of the project!"                ;;            *)                mkdir $2                cd $2                echo '#!/bin/bash' > $GAR_CONF                echo "BROWSER_FOR_GAR=firefox" >> $GAR_CONF                mkdir $SOURCE $OUTPUT $IMAGES                git init -q                git add .                git commit -a -m "init"                ;;        esac        ;;    new-class)        case $2 in            "")                error_msg "You should tell me the name of the posts class!"                ;;            *)                for i in ${@:2}                do                    MARK=$(pwd)                    mkdir -p $i                    cd $i                    CLASS_NAME=$(class_name $MARK/$i)                                        goto_root                    mkdir -p $IMAGES/$CLASS_NAME                    mkdir -p $OUTPUT/$CLASS_NAME                    cd $MARK                done                ;;        esac        ;;    remove-class)        case $2 in            "")                error_msg "You should tell me the name of the posts class!"                ;;            *)                for i in ${@:2}                do                    MARK=$(pwd)                    rm -rf $i                    CLASS_NAME=$(class_name $MARK/$i)                                        goto_root                    rm -rf $IMAGES/$CLASS_NAME                    rm -rf $OUTPUT/$CLASS_NAME                    cd $MARK                done                ;;        esac        ;;    new-post)        case $2 in            "")                error_msg "You should tell me the name of the post!"                ;;            *)                MARK=$(pwd)                CLASS_NAME=$(class_name $MARK)                for i in ${@:2}                do                    touch $i                    goto_root                    mkdir -p $IMAGES/$CLASS_NAME/${i%.*}                    cd $MARK                done                goto_root                git add .                DOCUMENTS="${@:2}"                git commit -a -m "Added $DOCUMENTS"                ;;        esac        ;;    remove-post)        case $2 in            "")                error_msg "You should tell me the name of the post!"                ;;            *)                MARK=$(pwd)                for i in ${@:2}                do                    if [ ! -e $i ]                    then                        error_msg "The file you want to remove is not found!"                    fi                    rm $i                    CLASS_NAME=$(class_name $MARK)                    goto_root                    rm -rf $IMAGES/$CLASS_NAME/${i%.*}                    HTML_OF_POST=$OUTPUT/$CLASS_NAME/${i%.*}.html                    if [ -e HTML_OF_POST ]                    then                        rm $HTML_OF_POST                    fi                    cd $MARK                done                goto_root                git add .                DOCUMENTS="${@:2}"                git commit -a -m "Remove $DOCUMENTS"                ;;        esac        ;;    rename)        case $2 in            "")                error_msg "You should tell me the name of the post you want to rename!"                ;;            *)                if [ ! -e $2 ]                then                    error_msg "The file you want to rename is not found!"                fi                case $3 in                    "")                        error_msg "You should tell me new name of the post!"                        ;;                    *)                        mv $2 $3                        CLASS_NAME=$(class_name $(pwd))                        goto_root                        mv $IMAGES/$CLASS_NAME/${2%.*} $IMAGES/$CLASS_NAME/${3%.*}                        mv $OUTPUT/$CLASS_NAME/${2%.*} $OUTPUT/$CLASS_NAME/${3%.*}                        git add .                        git commit -a -m "Rename $2 $3"                        ;;                esac                ;;        esac        ;;    convert)        case $2 in            "")                error_msg "You should tell me the name of the post!"                ;;            *)                MARK=$(pwd)                for i in ${@:2}                do                    TITLE=$(get_title $i)                    CLASS_NAME=$(class_name $MARK)                    goto_root                    markdown_to_html $CLASS_NAME $i                    cd $MARK                done                ;;            esac        ;;    preview)        case $2 in            "")                error_msg "You should tell me the name of the post!"                ;;            *)                  TITLE=$(get_title $2)                CLASS_NAME=$(class_name $(pwd))                goto_root                source gar.conf                $BROWSER_FOR_GAR $OUTPUT/$CLASS_NAME/${2%.*}.html                ;;            esac        ;;    tree)        goto_root        GAR_ROOT=$(basename $(pwd))        cd ..        tree $GAR_ROOT        cd $GAR_ROOT        ;;    *)        error_msg "I do not know what you want to do!"        ;;esac

Gar 在应用 pandoc 将 Markdown 文档转化为网页时,须要一个 CSS 文件 gar.css,其内容如下:

html {    font-size: 16px;    line-height: 1.8rem;}body {    margin: 0 auto;    max-width: 50rem;    padding: 50px;    hyphens: auto;    word-wrap: break-word;    font-kerning: normal;}header {    text-align: center;    margin-bottom: 4rem;}h1, h2, h3, h4, h5 {    margin-top: 2rem;    margin-bottom: 2rem;    color: #d35400;}h1.title { font-size: 2.5rem; }h1 { font-size: 1.8rem; }h2 { font-size: 1.65rem; }h3 { font-size: 1.5em; }h4 { font-size: 1.35rem; }h5 { font-size: 1.2rem; }p {    margin: 1.3rem 0;    text-align: justify;}figure {    text-align: center;}figure img {    width: 80%;}figure figcaption {    font-size: 0.9rem;}pre {    padding: 1rem;    font-size: 0.9rem;    line-height: 1.6em;    overflow:auto;    background: #f8f8f8;    border: 1px solid #ccc;    border-radius: 0.25rem;}p code {    color: #d35400;}/* 文章里大节题目的序号与题目名称之间的间距 */span.section-sep { margin-left: 0.5rem; margin-right: 0.5rem; }blockquote {    margin: 0px !important;    border-left: 4px solid #009A61;}blockquote p {    font-size: 1rem;    line-height: 1.8rem;    margin: 0px !important;    text-align: justify;    padding:0.5em;}

上述 gar.css 并无特别之处,齐全可依据本人对 css 的相熟水平并联合须要自行定制,然而要记得将它放在 gar 脚本同一目录下。