我有一个文件树,如下所示:
$ tree src
src
├── bible
│ ├── index.md
│ └── README.md
├── index.md
└── other.md
我想通过pandoc(1)
以下方式将此文件树中的每个 Markdown 文件渲染为 HTML:保留结构。
这个新的文件树应该以 为根out
,如下所示:
$ tree out
out
├── bible
│ ├── index.html
│ └── README.html
├── index.html
└── other.html
理想情况下,我想通过make(1)
.这是我到目前为止所拥有的:
SRC_DIR=src
OUT_DIR=out
.PHONY: all
all: $(SRC_DIR)/*.md
find . -name "*.md" -exec pandoc '{}' -o '{}'.html \;
find -name "*.html" -exec bash -c 'mv {} $(OUT_DIR)/`dirname {}`/`basename {} .md.html`.html' \;
# mv $(SRC_DIR)/*.html $(OUT_DIR)
firefox $(OUT_DIR)/index.html
.PHONY: clean
clean:
rm $(OUT_DIR)/*.html
这失败了:
$ make
find . -name "*.md" -exec pandoc '{}' -o '{}'.html \;
find . -name "*.html" -exec bash -c 'mv {} `basename {} .md.html`.html' \;
mv src/*.html out
mv: cannot stat 'src/*.html': No such file or directory
make: *** [Makefile:8: all] Error 1
答案1
对于中或以下某处的单个文件的路径.md
名:$pathname
src
name=$(basename "$pathname" .md)
destdir=out/$( dirname "${pathname#src/}" )
mkdir -p "$destdir" && pandoc -o "$destdir/$name.html" "$pathname"
这里,basename "$pathname" .md
将是不带文件.md
名后缀且不带任何目录路径的文件名(例如README
)src/bible/README.md
,将是不带初始目录名${pathname#src/}
的文件路径名,并将设置为目标目录路径名(交换为,无最终文件名组件,例如) 。最后,我们让write to (如果目标目录创建成功)。src/
$destdir
src/
out/
out/bible
src/bible/README.md
pandoc
$destdir/$name.html
您可以对.md
目录结构中的所有文件运行此命令:
find src -type f -name '*.md' -exec sh -c '
for pathname do
name=$(basename "$pathname" .md)
destdir=out/$( dirname "${pathname#src/}" )
mkdir -p "$destdir" && pandoc -o "$destdir/$name.html" "$pathname"
done' {} +
这是循环中的同一组命令。我们让find
循环使用在下面找到的路径名src
(另请参见了解“find”的 -exec 选项)。
测试:
$ tree -F
.
`-- src/
|-- bible/
| |-- README.md
| `-- index.md
|-- index.md
`-- other.md
2 directories, 4 files
(这里正在运行命令)
$ tree -F
.
|-- out/
| |-- bible/
| | `-- README.html
| |-- index.html
| `-- other.html
`-- src/
|-- bible/
| |-- README.md
| `-- index.md
|-- index.md
`-- other.md
4 directories, 7 files
如果您想使用您的SRC_DIR
MakefileOUT_DIR
变量:
find $(SRC_DIR) -type f -name '*.md' -exec sh -c '
srcdir=${1%/}; outdir=$2; shift 2
for pathname do
name=$(basename "$pathname" .md)
destdir=$outdir/$( dirname "${pathname#$srcdir/}" )
mkdir -p "$destdir" && pandoc -o "$destdir/$name.html" "$pathname"
done' $(SRC_DIR) $(OUT_DIR) {} +
也就是说,在sh -c
脚本的命令行上传递 src 和 out 名称,然后在内联脚本中将它们挑选出来。
我不是 100% 确定 Makefile 中的引用是如何工作的,您可能想要转义上面代码中的换行符,或者创建一个单独的小脚本来执行此操作,然后从 Makefile 中调用它。
答案2
尽管 makefile 和 shell 脚本在语法上有相似之处,但它们的区别就像粉笔和奶酪一样不同。
您正在按照 shell 脚本的精神编写 makefile。在 make 中,我们必须考虑关系和依赖关系。 shell 仅在最后阶段作为满足依赖关系的规则才出现。用 make 的话说,这些被称为菜谱。
######
SHELL := /bin/sh
SRC_DIR := src
OUT_DIR := out
### recursive glob
**/* = $(foreach i,$(strip \
$(wildcard $(1:=/*))),$(strip \
$(call $0,$i,$2) \
$(filter $(subst *,%,$2),$i)))
MD_FILES = $(call **/*,$(SRC_DIR),*.md)
HTML_FILES = $(subst $(SRC_DIR)/,$(OUT_DIR)/,$(MD_FILES:.md=.html))
.PHONY: all
all: $(HTML_FILES)
.PHONY: make_dir
make_dir: $(OUT_DIR)/
$(OUT_DIR)/%.html: $(SRC_DIR)/%.md |make_dir
pandoc $< -o $@
$(OUT_DIR)/: $(SRC_DIR)/
rsync -avz -f"+ */" -f"- *" $< $@
.PHONY: clean
clean:
rm -f -- $(HTML_FILES)