如何在一个cpp项目文件夹中调用clang-format?

155

是否有一种方法可以像这样调用clang-format --style=Webkit来处理整个cpp项目文件夹,而不是单独为每个文件运行它?

我正在使用clang-format.pyvim来实现这一点,但我认为有一种方法可以一次性应用。


另请参阅GitHub问题"请求:clang-format递归遍历目录 #62108" - undefined
18个回答

157

很遗憾,目前没有办法递归地应用clang-format。在*.cpp只会匹配当前目录下的文件,而不是子目录。即使使用**/*也无效。

幸运的是,有一个解决方案:使用find命令获取所有文件名并进行管道传输。例如,如果您想要递归地格式化目录foo/bar/中的所有.h.cpp文件,则可以执行以下操作:

find foo/bar/ -iname *.h -o -iname *.cpp | xargs clang-format -i

请参阅此处,了解更多讨论。


9
嗯,在(相当现代的)bash中似乎**/*.cpp可以工作。您可能需要在使用之前执行shopt -s globstar - sbarzowski
1
如果您正在使用CMake,此帖子将向您展示如何使用CMake的GLOB_RECURSE查找所有.cpp文件并将它们传递给clang-format - phoenix
6
使用find ... -print0xargs -0 ...的组合可以确保正确处理所有类型的文件名。 - Alexander
17
如果当前目录中存在任何.cpp.h文件,则该命令会失败。通过运行find foo/bar/ -iname '*.h' -o -iname '*.cpp' | xargs clang-format -i来解决这个问题。 - nyanpasu64
@nyanpasu64 它在 zsh 中也会失败。我实际上不明白为什么它在 bash 中不会失败。 :-) 这些引号应该真的存在,所以为什么在 bash 中没有它们也能工作呢? - Anne van Rossum
显示剩余2条评论

82

怎么样:

clang-format -i -style=WebKit *.cpp *.h

在项目文件夹中。-i选项使其原地更改(默认情况下,格式化输出写入标准输出)。


7
如果您正在使用配置文件并希望格式化多种文件类型: clang-format-3.6 -i -style=file *.cpp *.h *.hpp - mBardos
83
很不幸,它不能递归进入子目录。 - Antimony
4
是的,*.cpp将由shell扩展。clang只需要一个文件列表。更高级的选项(如递归globbing)取决于您的shell的功能。请参见http://unix.stackexchange.com/questions/49913/recursive-glob以了解如何使用`**`构造。 - sbarzowski
@AaronFranke 使用 *.cpp 生成文件列表应该是可行的。clang-tidy 命令允许传递多个文件。` 用法: clang-tidy [选项] <source0> [... <sourceN>]` 在实践中,这可能不是最好的方式。Clang-tidy执行更深入的分析,因此需要每个文件的编译器标志等。当我使用clang-tidy时,通常会有一个“编译数据库” - 一个JSON文件,其中包含每个文件的命令和一些脚本来处理它。那是几年前的事了,也许现在有更好的方法。 - sbarzowski
如果我的C++文件在子文件夹中怎么办? - Aaron Franke
显示剩余4条评论

52

如果不存在,首先创建一个.clang-format文件:

clang-format -style=WebKit -dump-config > .clang-format

选择任何想要的预定义样式,或者编辑生成的.clang-format文件。

clang-format配置工具是有帮助的。

然后运行:

find . -regex '.*\.\(cpp\|hpp\|cc\|cxx\)' -exec clang-format -style=file -i {} \;

正则表达式中可以使用除了 cpp, hpp, cc, 和 cxx 之外的其他文件扩展名,只需用 \| 将它们分隔开即可。


对于 -style=file,是否有一种指定自定义文件路径的方法?我尝试了 -style=~/.clang-format,但它没有起作用。 - Aaron Franke
除了更改当前目录,我不知道还有什么其他方法。 - Alexander
我想到的不太正式的解决方案是写一个函数,该函数首先运行 cp ~/.clang-format . 然后再运行你回答中的 find 命令。 - Aaron Franke
有趣的命令,但在我的macOS上无法运行。但是这个版本(使用-E标志并且不转义特殊符号)可以: find -E . -regex '.*\.(cpp|hpp|cc|cxx)' -exec clang-format -style=file -i {} \; - Anton Breusov
感谢提供信息,Anton Breusov。在Arch Linux上,-E无法使用,但是-regextype egrep可以。-regextype egrep在macOS上也适用吗? - Alexander

15

我最近发现了一段bash脚本,可以做到你需要的功能:

https://github.com/eklitzke/clang-format-all

这是一个bash脚本,它将在您的代码上运行clang-format -i

特点:

  • 在Ubuntu / Debian上找到正确的clang-format路径,其中编码LLVM版本在clang-format文件名中
  • 递归修复文件
  • 检测C / C ++项目中使用最广泛的文件扩展名

在Windows上,我在Git Bash和WSL中成功使用了它。


10

对于Windows用户:如果您拥有Powershell 3.0支持,则可以执行以下操作:

Get-ChildItem -Path . -Directory -Recurse |
    foreach {
        cd $_.FullName
        &clang-format -i -style=WebKit *.cpp
    }

注意1:如果您想在脚本之前和之后保留相同的当前目录,请使用pushd .popd

注意2:该脚本会在当前工作目录中运行

注意3:如果真的很重要,那么这可以写成一行


9

当你在使用Windows(CMD)时,不想使用PowerShell大杀四方,可以尝试以下方法:

for /r %t in (*.cpp *.h) do clang-format -i -style=WebKit "%t"

如果在命令行脚本中,请不要忘记复制这两个%


6
以下是脚本和流程:
  1. Linux上运行
  2. 应该在MacOS上运行
  3. Windows中,在Git For Windows终端中使用clang-format 下载并安装
这是我的操作步骤:
我创建一个run_clang_format.sh脚本,并将其放在项目目录的根目录中,然后可以从任何地方运行它。下面是它的样子:

run_clang_format.sh

#!/bin/bash

THIS_PATH="$(realpath "$0")"
THIS_DIR="$(dirname "$THIS_PATH")"

# Find all files in THIS_DIR which end in .ino, .cpp, etc., as specified
# in the regular expression just below
FILE_LIST="$(find "$THIS_DIR" | grep -E ".*(\.ino|\.cpp|\.c|\.h|\.hpp|\.hh)$")"

echo -e "Files found to format = \n\"\"\"\n$FILE_LIST\n\"\"\""

# Format each file.
# - NB: do NOT put quotes around `$FILE_LIST` below or else the `clang-format` command will 
#   mistakenly see the entire blob of newline-separated file names as a SINGLE file name instead 
#   of as a new-line separated list of *many* file names!
clang-format --verbose -i --style=file $FILE_LIST

使用--style=file的意思是我必须在同一级别上拥有一个自定义的.clang-format clang-format指定文件,而我确实有这个文件。
现在,将您新创建的run_clang_format.sh文件设置为可执行:
chmod +x run_clang_format.sh

...然后运行它:
./run_clang_format.sh

这是一个关于我的示例运行和输出的样本。
~/GS/dev/eRCaGuy_PPM_Writer$ ./run_clang-format.sh 
Files found to format = 
"""
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo/PPM_Writer_demo.ino
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo2/PPM_Writer_demo2.ino
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.h
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.cpp
/home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/timers/eRCaGuy_TimerCounterTimers.h
"""
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo/PPM_Writer_demo.ino
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/examples/PPM_Writer_demo2/PPM_Writer_demo2.ino
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.h
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/eRCaGuy_PPM_Writer.cpp
Formatting /home/gabriel/GS/dev/eRCaGuy_PPM_Writer/src/timers/eRCaGuy_TimerCounterTimers.h

您可以在我的run_clang_format.sh文件中,找到我的eRCaGuy_PPM_Writer存储库和我的eRCaGuy_CodeFormatter存储库。我的.clang-format文件也在那里

参考资料:

我的存储库: 1. 更新:请参阅此处:https://github.com/ElectricRCAircraftGuy/eRCaGuy_CodeFormatter 2. eRCaGuy_PPM_Writer 存储库 3. run_clang_format.sh 文件 关于如何在我的 "git & Linux cmds, help, tips & tricks - Gabriel.txt" 文档中使用 clang-format 的笔记,请在我的 eRCaGuy_dotfiles 存储库中搜索 "clang-format"。
官方的 clang-format 文档、设置和说明等!https://clang.llvm.org/docs/ClangFormat.html 在这里下载 Windows 版的 clang-format 自动格式化程序/代码检查器可执行文件,或其他安装程序/可执行文件:https://llvm.org/builds/ Clang-Format 样式选项:https://clang.llvm.org/docs/ClangFormatStyleOptions.html [我的回答] 如何从 Bash 脚本内部获取脚本所在目录的路径?

相关:

  1. [我的回答] 使用clang-format缩进预处理指令

另请参阅:

  1. [我的回答] https://stackoverflow.com/questions/67678531/fixing-a-simple-c-code-without-the-coments/67678570#67678570

3
在现代bash中,您可以递归遍历文件树。
for file_name in ./src/**/*.{cpp,h,hpp}; do
    if [ -f "$file_name" ]; then
        printf '%s\n' "$file_name"
        clang-format -i $file_name
    fi
done

这里假设源代码位于./src,而.clang-format则包含格式化信息。

我认为在bash中默认情况下没有启用globstar,因此您需要设置它。 - Eric Backus
1
这是最佳解决方案。清晰易懂,运行良好! - Yongquan

2
你可以在Makefile中使用此命令。它使用git ls-files --exclude-standard获取文件列表,因此未跟踪的文件会自动被跳过。它假定您在项目根目录下有一个.clang-tidy文件。
format:
ifeq ($(OS), Windows_NT)
    pwsh -c '$$files=(git ls-files --exclude-standard); foreach ($$file in $$files) { if ((get-item $$file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $$file } }'
else
    git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$$' | xargs clang-format -i -style=file
endif

使用make format命令来运行。

注意,在make中,我使用$$转义$符号。

如果你使用go-task而不是make,则需要执行以下操作:

  format:
    - |
      {{if eq OS "windows"}} 
        powershell -c '$files=(git ls-files --exclude-standard); foreach ($file in $files) { if ((get-item $file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $file } }' 
      {{else}} 
        git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$' | xargs clang-format -i -style=file 
      {{end}}

使用task format运行

如果您想运行单个脚本,则使用以下命令

# powershell
$files=(git ls-files --exclude-standard); foreach ($file in $files) { if ((get-item $file).Extension -in ".cpp", ".hpp", ".c", ".cc", ".cxx", ".hxx", ".ixx") { clang-format -i -style=file $file } }

# bash
git ls-files --exclude-standard | grep -E '\.(cpp|hpp|c|cc|cxx|hxx|ixx)$' | xargs clang-format -i -style=file

2
这里有一个解决方案,可以递归搜索并将所有文件作为文件列表一次性传输到clang-format中。它还排除了“build”目录(我使用CMake),但您可以省略“grep”步骤以删除该目录。
shopt -s globstar extglob failglob && ls **/*.@(h|hpp|hxx|c|cpp|cxx) | grep -v build | tr '\n' ' ' | xargs clang-format -i

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接