如何在 cpp 项目文件夹上调用 clang-format?

有没有一种方法可以对整个 cpp 项目文件夹调用类似 clang-format --style=Webkit的函数,而不是对每个文件单独运行它?

我正在使用 clang-format.pyvim来做这件事,但是我假设有一种方法可以将其应用一次。

118182 次浏览

那么:

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

I 选项将其置入(默认情况下,格式化输出写入标准输出)。

不幸的是,没有办法递归地应用 clang-format。*.cpp将只匹配工作目录中的文件,而不匹配子目录中的文件。就算是 **/*也没用。

幸运的是,有一个解决方案: 使用 find命令获取所有文件名,并通过管道将它们输入。例如,如果希望递归地格式化目录 foo/bar/中的所有 .h.cpp文件,可以这样做

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

有关更多讨论,请参见 给你

我最近发现了一个 bash 脚本,它可以完全满足您的需要:

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

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

特点:

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

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

首先创建一个不存在的 .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 {} \;

cpphppcccxx以外的其他文件扩展名也可以用在正则表达式中,只要确保用 \|将它们分开即可。

对于 Windows 用户: 如果你有 Powershell 3.0支持,你可以:

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

注1: 如果你想在脚本之前和之后使用相同的工作目录,可以使用 pushd .popd

注2: 脚本在当前工作目录下运行

注3: 如果这对你很重要的话,可以写成一行

我使用以下命令格式化当前文件夹 递归下的所有目标 C 文件:

$ find . -name "*.m" -o -name "*.h" | sed 's| |\\ |g' | xargs clang-format -i

我在我的 .bash_profile中定义了以下别名,以使事情变得更简单:

# Format objC files (*.h and *.m) under the current folder, recursively
alias clang-format-all="find . -name \"*.m\" -o -name \"*.h\" | sed 's| |\\ |g' | xargs clang-format -i"

下面是一个递归搜索的解决方案,它在一个命令中以文件列表的形式将所有文件传送到 clang 格式。它还排除了“ build”目录(我使用 CMake) ,但是您可以省略“ grep”步骤来删除它。

shopt -s globstar extglob failglob && ls **/*.@(h|hpp|hxx|c|cpp|cxx) | grep -v build | tr '\n' ' ' | xargs clang-format -i

在现代 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包含格式化信息。

当你使用 Windows (CMD)但不想使用 PowerShell 大炮射击这只苍蝇时,试试这个:

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

如果在 cmd 脚本中,不要忘记复制这两个 %

下面的脚本和过程:

  1. Linux工作
  2. 应该在 MacOS上工作
  3. clang-format 下载并安装一起在 窗户内的 Windows 的 Git终端工作。

我是这么做的:

我创建了一个 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

您可以在我的 ERCaGuy _ PPM _ Writer存储库中找到我的 run_clang_format.sh文件,也可以在我的 ERCaGuy _ CodeFormatter存储库中找到我的 .clang-format文件也在那里文件。

参考文献:

  1. 我的资料库:
    1. ERCaGuy _ PPM _ Writer repo
    2. run_clang_format.sh文件
  2. 关于如何在我的 “ git & Linux cmds,help,tips & trick-Gabriel.txt”文档中使用 clang-format的笔记,在我的 ERCaGuy _ dotfiles回购文档中(在文档中搜索“ clang-format”)。
  3. 官方 clang-format文档,设置,说明等
  4. 在这里下载适用于 Windows 的 clang-format auto-formatter/linter 可执行文件或其他安装程序/可执行文件: https://llvm.org/builds/
  5. Clang-Format Style 选项: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
  6. [我的回答] 如何从脚本本身获取 Bash 脚本的源目录?

相关阅读:

  1. [我的回答] 使用 clang 格式缩进预处理器指令

参见:

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

正如@sbarzowski 在上面的注释中提到的,在 bash 中可以启用导致 **递归扩展的 Globstar

如果您只是想为这一个命令使用它,那么您可以执行以下操作来格式化所有 .h.cc.cpp文件。

(shopt -s globstar; clang-format -i **/*.{h,cc,cpp})

或者你可以在你的 .bashrc中加入 shopt -s globstar,并且在 bash 中始终保持 **的优点。

另外,您可能想在第一次使用带有 clang 格式的 --dry-run来确保它是您想要的。

您可以在 Make 文件中使用它。它使用 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

注意,我使用 $$转义了 $

如果你使用 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

我在 clang-format 方面也遇到过类似的问题,我们有一个庞大的项目,需要检查和重新格式化大量的文件。 脚本是一个不错的解决方案,但是太慢了。

所以,我写了一个应用程序,可以递归地通过文件夹中的文件,并执行 clang-format 对他们在快速多线程庄园。 应用程序还支持忽略目录和文件,您可能不希望触及的格式(如第三方目录)

你可以从这里查看: github.com/gloryofnight/clang-format-all

我希望它对其他人也有用。

注: 我知道这个应用程序有点过火,但是它的速度非常快

有点“ O/T”,但是当我在谷歌上搜索“如何将一个文件列表提供给 clang-format”时,这是最受欢迎的。在我的例子中,我不希望递归遍历特定文件类型的整个目录。相反,我想应用 clang-format的所有文件,我编辑 之前我推我的功能/bug 修复分支。我们管道中的第一步是 clang-format,它几乎总是失败,所以我想在我的更改上“手动”运行这个步骤,只是为了照顾这个步骤,而不是几乎总是处理快速失败的管道。您可以获得所有更改过的文件的列表

git diff <commitOrTagToCompareTo> --name-only

借用 锑的答案,你可以把它导入 xargs,最后是 clang-format:

git diff <commitOrTagToCompareTo> --name-only | xargs clang-format -i

现在,运行 git status将显示哪些文件发生了更改(git diff(tool)将显示更改) ,您可以提交和推送这些文件,希望能够转到管道中更重要的部分。

第一步是找出头文件和源文件,我们使用:

find . -path ./build -prune -o -iname "*.hpp" -o -iname "*.cpp" -o -iname "*.c" -o -iname "*.h"

表示“或”,我叫表示忽略大小写。特别是在您的情况下,您可以添加更多类似于 - 我的名字是 * . cc的扩展。这里还有一个诀窍就是逃跑。/build/目录,- 路径/构建-修剪建议不要下降到给定的目录”。/建造」。
输入以上命令,你会发现它仍然打印出来”。/build”,然后使用 sed 命令替换。/build”,类似于:

sed 's/.\/build//' <in stream>

最后,我们调用 clang-format 来进行格式化:

clang-format -i <file>

把它们结合起来,我们有:

find . -path ./build -prune -o -iname "*.hpp" -o -iname "*.cpp" -o -iname "*.cc" -o -iname "*.cxx" -o -iname "*.c" -o -iname "*.h"|sed 's/.\/build//'|xargs clang-format -i

我也遇到过类似的问题,需要检查格式化错误,但是我想在 Linux 和 Windows 上使用一个 clang-format 调用来检查格式化错误。

以下是我的俏皮话:

巴斯:

find $PWD/src -type f \( -name "*.h" -o -name "*.cpp" \) -exec clang-format -style=file --dry-run --Werror {} +

Powershell:

clang-format -style=file --dry-run --Werror $(Get-ChildItem -Path $PWD/src -Recurse | Where Name -Match '\.(?:h|cpp)$' | Select-Object -ExpandProperty FullName)