运行Makefile时报/bin/sh: "(" unexpected语法错误的解决方法
问题诱因
- Makefile 头部加
#!/bin/bash没有任何作用:make执行规则内的命令时,不会解析文件头的shebang声明,默认固定调用/bin/sh作为命令执行环境,所有命令都交给sh解析运行。 - 报错的直接原因:命令中使用的
<(command)是进程替换语法,属于Bash、Zsh等Shell的扩展特性,不兼容POSIX标准的sh,sh解析到括号位置时就会抛出语法错误。 - 额外隐藏问题:你写的目标文件编译规则本身存在逻辑错误,就算解决Shell问题也会编译失败:
g++ $@ -o $<不仅把输入、输出参数写反,还缺少编译目标文件必需的-c参数,会错误地把.o文件当作源文件尝试链接成可执行文件,完全不符合预期。 - 补充注意:GNU Make默认的
wildcard函数不支持**递归通配符,你写的$(wildcard **/*.cpp)大概率无法匹配多层嵌套目录下的cpp源文件。
修复方案
- 指定make使用Bash执行命令,在Makefile最顶部添加内置变量配置,替换默认的sh:
该变量是make的内置配置项,优先级最高,设置后所有规则下的命令都会交给Bash执行,原生支持进程替换语法。SHELL := /bin/bash - 修正.o目标文件的编译规则,补全编译选项、调整参数顺序:
其中%.o: %.cpp tests/%.cpp $(headers) g++ -c $< -o $@-c参数告诉g只编译生成目标文件、不执行链接操作;$<代表规则的第一个依赖项(待编译的cpp源文件),$@代表当前规则要生成的目标.o文件,参数顺序符合g的调用要求。 - (可选优化)如果需要可靠递归查找全目录下的cpp文件,把source变量的定义改为find命令实现,避免通配符不生效导致源文件遗漏:
source := $(shell find . -name "*.cpp")
修复完成后的完整可运行Makefile参考:
SHELL := /bin/bash source := $(shell find . -name "*.cpp") headers := $(wildcard include/*.h) objects := ${source:.cpp=.o} run: $(objects) g++ -o run $(objects) diff -u <(cat $(source)) <(clang-format $(source)) %.o: %.cpp tests/%.cpp $(headers) g++ -c $< -o $@
如果不想依赖Bash特性提升跨环境兼容性,也可以把diff行的进程替换写法改为POSIX sh兼容的临时文件实现,不需要切换Shell也能运行:
run: $(objects) g++ -o run $(objects) clang-format $(source) > .format_tmp diff -u $(source) .format_tmp rm -f .format_tmp
内容的提问来源于stack exchange,提问作者Potatmen
相关产品推荐
相关产品推荐

