Is there a way to call something like clang-format --style=Webkit for an entire cpp project folder, rather than running it separately for each file?
I am using clang-format.py and vim to do this, but I assume there is a way to apply this once.
Unfortunately, there is no way to apply clang-format recursively. *.cpp will only match files in the current directory, not subdirectories. Even **/* doesn't work.
Luckily, there is a solution: grab all the file names with the find command and pipe them in. For example, if you want to format all .h and .cpp files in the directory foo/bar/ recursively, you can do
find foo/bar/ -iname *.h -o -iname *.cpp | xargs clang-format -i
See here for additional discussion.
What about:
clang-format -i -style=WebKit *.cpp *.h
in the project folder. The -i option makes it inplace (by default formatted output is written to stdout).
First create a .clang-format file if it doesn't exist:
clang-format -style=WebKit -dump-config > .clang-format
Choose whichever predefined style you like, or edit the resulting .clang-format file.
clang-format configurator is helpful.
Then run:
find . -regex '.*\.\(cpp\|hpp\|cc\|cxx\)' -exec clang-format -style=file -i {} \;
Other file extensions than cpp, hpp, cc and cxx can be used in the regular expression, just make sure to separate them with \|.
I recently found a bash-script which does exactly what you need:
https://github.com/eklitzke/clang-format-all
This is a bash script that will run clang-format -i on your code.
Features:
Finds the right path to clang-format on Ubuntu/Debian, which encode the LLVM version in the clang-format filename
Fixes files recursively
Detects the most common file extensions used by C/C++ projects
On Windows, I used it successfully in Git Bash and WSL.
For the Windows users: If you have Powershell 3.0 support, you can do:
Get-ChildItem -Path . -Directory -Recurse |
foreach {
cd $_.FullName
&clang-format -i -style=WebKit *.cpp
}
Note1: Use pushd . and popd if you want to have the same current directory before and after the script
Note2: The script operates in the current working directory
Note3: This can probably be written in a single line if that was really important to you
When you use Windows (CMD) but don't want to use the PowerShell cannon to shoot this fly, try this:
for /r %t in (*.cpp *.h) do clang-format -i -style=WebKit "%t"
Don't forget to duplicate the two %s if in a cmd script.
The below script and process:
works in Linux
should work on MacOS
works in Windows inside Git For Windows terminal with clang-format downloaded and installed.
Here's how I do it:
I create a run_clang_format.sh script and place it in the root of my project directory, then I run it from anywhere. Here's what it looks like:
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
Using --style=file means that I must also have a custom .clang-format clang-format specifier file at this same level, which I do.
Now, make your newly-created run_clang_format.sh file executable:
chmod +x run_clang_format.sh
...and run it:
./run_clang_format.sh
Here's a sample run and output for me:
~/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
You can find my run_clang_format.sh file in my eRCaGuy_PPM_Writer repository, and in my eRCaGuy_CodeFormatter repository too. My .clang-format file is there too.
References:
My repository:
eRCaGuy_PPM_Writer repo
run_clang_format.sh file
My notes on how to use clang-format in my "git & Linux cmds, help, tips & tricks - Gabriel.txt" doc in my eRCaGuy_dotfiles repo (search the document for "clang-format").
Official clang-format documentation, setup, instructions, etc! https://clang.llvm.org/docs/ClangFormat.html
Download the clang-format auto-formatter/linter executable for Windows, or other installers/executables here: https://llvm.org/builds/
Clang-Format Style Options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html
[my answer] How can I get the source directory of a Bash script from within the script itself?
Related:
[my answer] Indenting preprocessor directives with clang-format
See also:
[my answer] https://stackoverflow.com/questions/67678531/fixing-a-simple-c-code-without-the-coments/67678570#67678570
Here is a solution that searches recursively and pipes all files to clang-format as a file list in one command. It also excludes the "build" directory (I use CMake), but you can just omit the "grep" step to remove that.
shopt -s globstar extglob failglob && ls **/*.#(h|hpp|hxx|c|cpp|cxx) | grep -v build | tr '\n' ' ' | xargs clang-format -i
You can use this inside a Make file. It uses git ls-files --exclude-standard to get the list of the files, so that means untracked files are automatically skipped. It assumes that you have a .clang-tidy file at your project root.
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
Run with make format
Notice that I escaped $ using $$ for make.
If you use go-task instead of make, you will need this:
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}}
Run with task format
If you want to run the individual scripts, then use these
# 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
I'm using the following command to format all objective-C files under the current folder recursively:
$ find . -name "*.m" -o -name "*.h" | sed 's| |\\ |g' | xargs clang-format -i
I've defined the following alias in my .bash_profile to make things easier:
# 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"
In modern bash you can recursively crawl the file tree
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
Here the source is assumed to be located in ./src and the .clang-format contains the formatting information.
As #sbarzowski touches on in a comment above, in bash you can enable globstar which causes ** to expand recursively.
If you just want it for this one command you can do something like the following to format all .h, .cc and .cpp files.
(shopt -s globstar; clang-format -i **/*.{h,cc,cpp})
Or you can add shopt -s globstar to your .bashrc and have ** goodness all the time in bash.
As a side note, you may want to use --dry-run with clang-format the first time to be sure it's what you want.
I had similar issue with clang-format, we have a huge project with a lot of files to check and to reformat.
Scripts were a ok solutions, but there was too slow.
So, I've wrote an application that can recursively going thru files in folder and executes clang-format on them in fast multithreaded manor.
Application also supports ignore directories and files that you might not wanna touch by format (like thirdparty dirs)
You can checkout it from here: github.com/GloryOfNight/clang-format-all
I hope it would be also useful for other people.
ps: I know that app huge overkill, but its super fast at it job
A bit <O/T>, but when I googled "how to feed a list of files into clang-format" this was the top hit. In my case, I don't want to recurse over an entire directory for a specific file type. Instead, I want to apply clang-format to all the files I edited before I push my feature/bugfix branch. The first step in our pipeline is clang-format, and it almost always fails, so I wanted to run this "manually" on my changes just to take care of that step instead of nearly always dealing with a quickly failing pipeline. You can get a list of all the files you changed with
git diff <commitOrTagToCompareTo> --name-only
And borrowing from Antimony's answer, you can pipe that into xargs and finally clang-format:
git diff <commitOrTagToCompareTo> --name-only | xargs clang-format -i
Running git status will now show which files changed (git diff(tool) will show you the changes), and you can commit and push this up, hopefully moving on to more important parts of the pipeline.
The first step is to find out header and source files, we use:
find . -path ./build -prune -o -iname "*.hpp" -o -iname "*.cpp" -o -iname "*.c" -o -iname "*.h"
The -o is for "or" and -iname is for ignoring case. And in your case specifically, you may add more extensions like -o -iname "*.cc". Here another trick is to escape ./build/ directory, -path ./build -prune suggests do not descend into the given directory "./build".
Type above command you will find it still prints out "./build", then we use sed command to replace "./build" with empty char, something like:
sed 's/.\/build//' <in stream>
At last, we call clang-format to do formatting:
clang-format -i <file>
Combine them, we have:
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
I had similar issue where I needed to check for formatting errors, but I wanted to do it with a single clang-format invocation both on linux and windows.
Here are my one-liners:
Bash:
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)
I use below command in shell file and works fine in the directories that regex matches.
Problem is, it lists all files when there is no match for regex. Anyone knows why it has this behaviour?
Are there anyway to avoid it?
find . -type f -mtime +365 | egrep '.*xxx.yyy*.*'|grep -v "[^.]/" | xargs ls -lrt | tr -s " " | cut -d" " -f6-9
thanks for your time.
Note: I m using this script with splunk forwarder on solaris 8.
If the input of xargs is empty, then it will execute ls -lrt in the current folder.
Try xargs -i "{}" ls -lrt "{}" instead. That forces xargs to put the input arguments into a certain place in the command that it executes. If it doesn't have any input, it can't and will skip running the command at all.
If you have GNU xargs, you can use the switch --no-run-if-empty instead.
If that doesn't work, try to move all the greping into find, so you can use -ls to display the list of files. That will also avoid running the ls command if no file matches.
With regards to this post, how would I exclude one or more files from applying the string replacement? By using the aforementioned post as an example, I would like to be able to replace "apples" with "oranges" in all descendant files of a given directory except, say, ./fpd/font/symbol.php.
My idea was using the -regex switch in the find command but unfortunately it does not have a -v option like the grep command hence I can't negate the regex to not match the files where the replacement must occur.
I use this in my Git repository:
grep -ilr orange . | grep -v ".git" | grep -e "\\.php$" | xargs sed -i s/orange/apple/g {}
It will:
Run find and replace only in files that actually have the word to be replaced;
Not process the .git folder;
Process only .php files.
Needless to say you can include as many grep layers you want to filter the list that is being passed to xargs.
Known issues:
At least in my Windows environment it fails to open files that have spaces in the path or name. Never figured that one out. If anyone has an idea of how to fix this I would like to know.
Haven't tested this but it should work:
find . -path ./fpd/font/symbol.php -prune -o -exec sed -i 's/apple/orange/g' {} \;
You can negate with ! (or -not) combined with -name:
$ find .
.
./a
./a/b.txt
./b
./b/a.txt
$ find . -name \*a\* -print
./a
./b/a.txt
$ find . ! -name \*a\* -print
.
./a/b.txt
./b
$ find . -not -name \*a\* -print
.
./a/b.txt
./b
I am working on a project which requires the understanding of llvm compiler source-code. To browse source code of llvm, I tried to use cscope with following command in the root directory of the source:
cscope -R *
But it doesn't work. As there are mainly .cpp and .h files but some .c files are also there. So now I don't have a clue how to make cscope work? Can someone please help?
You can use following commands to do the required task from the root directory of llvm source tree:
touch tags.lst
find | grep "\.c$" >> tags.lst
find | grep "\.cpp$" >> tags.lst
find | grep "\.h$" >> tags.lst
cscope -i tags.lst
It would create cscope.out file which is used with cscope to browse the code. Hope it helps!
A convenient way to list all C++ files in a project is to use the ack tool: a grep-like command optimized for source code searching (In some distributions, for instance Ubuntu, the tool is called ack-grep). You can run it like this:
ack -f --cpp > cscope.files
The output are paths to all .cpp, .h, .cc .hpp files
Just because this is still the most popular entry. The stdin thingy may have been added in the meantime or not, but it makes it kind of elegant:
find -regex '.*\.\(c\|h\|cpp\|cxx\|hh\|hpp\|hxx\)$' | cscope -i- -b -q
I have following in my .bashrc which make things easier. Run cscope_build() to generate data base and cscope to start cscope tool.
# Use vim to edit files
export CSCOPE_EDITOR=`which vim`
# Generate cscope database
function cscope_build() {
# Generate a list of all source files starting from the current directory
# The -o means logical or
find . -name "*.c" -o -name "*.cc" -o -name "*.cpp" -o -name "*.h" -o -name "*.hh" -o -name "*.hpp" > cscope.files
# -q build fast but larger database
# -R search symbols recursively
# -b build the database only, don't fire cscope
# -i file that contains list of file paths to be processed
# This will generate a few cscope.* files
cscope -q -R -b -i cscope.files
# Temporary files, remove them
# rm -f cscope.files cscope.in.out cscope.po.out
echo "The cscope database is generated"
}
# -d don't build database, use kscope_generate explicitly
alias cscope="cscope -d"
To cover our large code base I have a script that looks a bit like this to build cscope indexes. The reason I change to / is so that I have full file paths to the source files which makes things work a little smoother.
cd /
find -L /home/adrianc/code -name "*.c" -o -name "*.cc" -o -name "*.h" > /home/adrianc/code/cscope.files
cd /home/adrianc/code
/usr/local/bin/cscope -b -icscope.files -q -u
Also it may be worth checking out
http://cscope.sourceforge.net/cscope_vim_tutorial.html
I am trying to do a find where I can specify wildcards in the directory structure then do a grep for www.domain.com in all the files within the data directory.
ie
find /a/b/c/*/WA/*/temp/*/*/data -type f -exec grep -l "www.domain.com" {} /dev/null \;
This works fine where there is only one possible level between c/*/WA.
How would I go about doing the same thing above where there could be multiple levels between C/*/WA?
So it could be at
/a/b/c/*/*/WA/*/temp/*/*/data
or
/a/b/c/*/*/*/WA/*/temp/*/*/data
There is no defined number of directories between /c/ and /WA/; there could be multiple levels and at each level there could be the /WA/*/temp/*/*/data.
Any ideas on how to do a find such as that?
How about using a for loop to find the WA directories, then go from there:
for DIR in $(find /a/b/c -type d -name WA -print); do
find $DIR/*/temp/*/*/data -type f \
-exec grep -l "www.domain.com" {} /dev/null \;
done
You may be able to get all that in a single command, but I think clarity is more important in the long run.
Assuming no spaces in the paths, then I'd think in terms of:
find /a/b/c -name data -type f |
grep -E '/WA/[^/]+/temp/[^/]+/[^/]+/data' |
xargs grep -l "www.domain.com" /dev/null
This uses find to find the files (rather than making the shell do most of the work), then uses the grep -E (equivalent to egrep) to select the names with the correct pattern in the path, and then uses xargs and grep (again) to find the target pattern.