You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Makefile未终止字符串错误:兼容含空格目录的解决办法

解决Makefile中含空格目录的未终止字符串错误

我在使用下面的Makefile时,反复遇到/bin/sh: 1: Syntax error: Unterminated quoted string错误:

BASE=/mnt/c/Users/kilgore/scripts
SCRIPTS=$(BASE)/scripts
CSV=$(BASE)/csv

DIR=
QUOTED_DIR := $(patsubst %, "%", $(DIR))
REPORTS=./$(firstword $(subst ., ,$(subst /, ,$(QUOTED_DIR))))/reports

SH_INV=$(SCRIPTS)/inventory.sh
PY_INV=$(SCRIPTS)/filecheck.py
CSV_NAME=$(shell echo "$(DIR)" | sed -r "s/[[:punct:][:space:]]//g")

.PHONY: peek csv scan

peek:
        find "$(DIR)"* -maxdepth 0 -type d -exec sh -c 'echo -n "{}: "; find "{}" -type f -print | wc -l' \;

scan:
        bash $(SH_INV) "$(DIR)"

dir:
        @echo "$(DIR)"
        @echo $(REPORTS)

INVENTORY=$(CSV)/$(CSV_NAME)_inventory.csv
REPORT=$(CSV_NAME)_inventory.xlsx

$(INVENTORY):
        bash $(SH_INV) "$(DIR)" >$@

$(REPORT): $(INVENTORY)
        unoconv -d spreadsheet -f xlsx -i FilterOptions=9,34 -o "$(REPORTS)/$(REPORT)" $<

report: $(REPORT)


report2:
        python3 $(PY_INV) -f "$(REPORTS)/$(REPORT)"

移除引号能消除错误,但我需要处理带空格的目录。请问如何在支持含空格目录的同时解决该未终止字符串错误?


错误根源

  1. 变量定义的引号问题:QUOTED_DIR使用了HTML转义的"而非Makefile原生双引号,且当DIR为空时会生成空引号,后续字符串替换时会把引号混入路径,导致shell解析时出现未终止字符串。
  2. 路径处理逻辑错误:REPORTS变量对带引号的QUOTED_DIR做切割,会把引号当作路径的一部分,最终生成的路径包含引号,传递给shell后引发语法错误。
  3. 命令引号嵌套冲突:peek目标中find -exec sh -c的引号嵌套逻辑有问题,直接使用{}会和外层引号冲突。

解决方案及修改后的Makefile

BASE=/mnt/c/Users/kilgore/scripts
SCRIPTS=$(BASE)/scripts
CSV=$(BASE)/csv

# 默认空目录,使用时通过 make DIR="带空格的目录" 指定
DIR=
# 直接基于DIR提取第一级目录名,处理空DIR的情况
REPORTS=$(if $(DIR),./$(firstword $(subst /, ,$(DIR)))/reports,)

SH_INV=$(SCRIPTS)/inventory.sh
PY_INV=$(SCRIPTS)/filecheck.py
# 用printf传递DIR避免空格解析问题
CSV_NAME=$(shell printf '%s' "$(DIR)" | sed -r 's/[[:punct:][:space:]]//g')

.PHONY: peek csv scan dir report report2

peek:
	# 用位置参数传递路径,避免引号嵌套冲突
	find "$(DIR)"* -maxdepth 0 -type d -exec sh -c 'echo -n "$1: "; find "$1" -type f -print | wc -l' sh {} \;

scan:
	bash "$(SH_INV)" "$(DIR)"

dir:
	@echo "$(DIR)"
	@echo "$(REPORTS)"

INVENTORY=$(CSV)/$(CSV_NAME)_inventory.csv
REPORT=$(CSV_NAME)_inventory.xlsx

$(INVENTORY):
	bash "$(SH_INV)" "$(DIR)" >$@

$(REPORT): $(INVENTORY)
	# 确保报告目录存在
	mkdir -p "$(REPORTS)"
	unoconv -d spreadsheet -f xlsx -i FilterOptions=9,34 -o "$(REPORTS)/$(REPORT)" "$<"

report: $(REPORT)

report2:
	python3 "$(PY_INV)" -f "$(REPORTS)/$(REPORT)"

修改说明

  1. 变量优化:

    • 移除冗余的QUOTED_DIR,直接在需要引用路径的地方给DIR加双引号"$(DIR)"。
    • REPORTS用if函数处理空DIR场景,避免生成无效路径;直接基于原始DIR切割目录名,不会混入引号。
    • CSV_NAME改用printf传递DIR,避免echo处理空格时的解析问题。
  2. 命令修复:

    • peek目标的find -exec sh -c改用位置参数$1传递路径,彻底避免引号嵌套冲突。
    • 所有涉及路径的命令都给变量加上双引号,确保带空格的目录被shell正确解析。
    • 添加mkdir -p "$(REPORTS)"确保报告目录存在,避免unoconv执行失败。

内容的提问来源于stack exchange,提问作者Kilgore Trout

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 10:45:33