다음과 같은 파일 트리가 있습니다.
$ 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/}
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
Makefile 변수를 사용하려면 다음을 수행하세요 OUT_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 이름을 전달한 다음 인라인 스크립트에서 선택합니다.
Makefile에서 인용이 어떻게 작동하는지 100% 확신할 수 없습니다. 위 코드에서 줄 바꿈을 이스케이프 처리하거나 별도의 작은 스크립트를 만들어 이를 수행한 다음 Makefile에서 호출할 수 있습니다.
답변2
구문적 유사성에도 불구하고 makefile과 쉘 스크립트는 분필과 치즈만큼 다릅니다.
쉘 스크립팅의 정신으로 메이크파일을 작성하고 있습니다. 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)