I'm trying to write a bash-script in Linux which traverses the current directory and, in every subdirectory, it launches the existing makefile. It should work for each subdirectory, regardless of depth.
Some restrictions:
I cannot use Python;
I don't know in advance how many subdirectories and their names;
I don't know in advance the name of current directory;
the make command for each directory should only be launched if there is makefile in such folder.
Any ideas on how to do it?
Using -exec and GNU make
find -type f \( -name 'GNUmakefile' -o -name 'makefile' -o -name 'Makefile' \) \
-exec bash -c 'cd "$(dirname "{}")" && make' \;
Given that this is make-related. I'd try to use a makefile at the top-level instead of a script. Something like this:
MAKEFILES:=$(shell find . -mindepth 2 -name Makefile -type f)
DIRS:=$(foreach m,$(MAKEFILES),$(realpath $(dir $(m))))
.PHONY: all
all: $(DIRS)
.PHONY: $(DIRS)
$(DIRS):
$(MAKE) -C $#
I'd accept what #MLSC says about using for with find, and that kind of applies here too .. the problem with that is when you have a space in the directory name. However, in many cases that's not going to happen, and IMHO there are benefits in using a makefile instead of a script. (There might be a solution using make that can cope with spaces in the directory name, but I can't think of it off the top of my head.)
You can use this script https://gist.github.com/legeyda/8b2cf2c213476c6fe6e25619fe22efd0.
Example usage is:
foreach */ 'test -f Makefile && make'
This should work if dont care about the execution order or if parent directory also has a Makefile.
#!/bin/bash
for f in $(find . -name Makefile); do
pushd $(dirname $f)
make
popd
done
Related
How can I find every Makefile file in the current path and subdirs and run a make clean command in every occurance.
What I have till now (does not work) is something like:
find . -type f -name 'Makefile' 2>/dev/null | sed 's#/Makefile##' | xargs -I% cd % && make clean && cd -
Another option would be to use find with -execdir but this gives me the issue with $PATH : The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find ....
But I do not want to change the $PATH variable.
An answer using the tools I used would be helpful so that I can understand what I do wrong,
but any working answer is acceptable.
Of course find is an option.. My approach with that would be more like:
find . -name Makefile -exec bash -c 'make -C "${1%/*}" clean' -- {} \;
But since you're using bash anyway, if you're in bash 4, you might also use globstar.
shopt -s globstar
for f in **/Makefile; do make -C "${f%/*}" clean; done
If you want to use the execution feature of find you can still do this:
find "${PWD}" -name Makefile -exec sh -c 'cd "${0%Makefile}" && make clean' {} \;
I would use the following approach:
find "$(pwd)" -name Makefile | while read -r line; do cd "$(dirname "$line")" && make clean; done
Please note the find $(pwd) which gives the full path as output of find.
I am trying to convert an entire directory from html into markdown. The directory tree is quite tall, so there are files nested two and three levels down.
In answering this question, John MacFarlane suggested using the following Makefile:
TXTDIR=sources
HTMLS=$(wildcard *.html)
MDS=$(patsubst %.html,$(TXTDIR)/%.markdown, $(HTMLS))
.PHONY : all
all : $(MDS)
$(TXTDIR) :
mkdir $(TXTDIR)
$(TXTDIR)/%.markdown : %.html $(TXTDIR)
pandoc -f html -t markdown -s $< -o $#
Now, this doesn't seem to go inside subdirectories. Is there any easy way to modify this so that it will process the entire tree?
I don't need this to be in make. All I'm looking for is a way of getting a mirror of the initial directory where each html file is replaced by the output of running pandoc on that file.
(I suspect something along these lines should help, but I'm far from confident that I won't break things if I try to go at it on my own. I'm illiterate when it comes to GNU make).)
Since you mentioned you don't mind not using make, you can try bash.
I modified the code from this answer, use in the parent directory:
find ./ -iname "*.md" -type f -exec sh -c 'pandoc "${0}" -o "${0%.md}.pdf"' {} \;
It worked when I tested it, so it should work for you.
As per the request Any ideas how to specify the output folder? (Using html as the original file and md as the output):
find ./ -iname "*.html" -type f -exec sh -c 'pandoc "${0}" -o "./output/$(basename ${0%.html}.md)"' {} \;
I have tested this and it works for me.
Edit: As per a comment, the {} \; when used with find and the -exec option is used as a, more or less, placeholder for where the filename should be. As in it expands the filenames found to be placed in the command. The \; ends the -exec. See here for more explanation.
This is how I did it!
files=($(find ${INPUT_FOLDER} -type f -name '*.md'))
for item in ${files[*]}
do
printf " %s\n" $item
install -d ${DIR}/build/$item
pandoc $item -f markdown -t html -o ${DIR}/build/$item.html;
rm -Rf ${DIR}/build/$item
done
I've created a python script for converting all files under a folder tree which have a given suffix. It's called Pandoc-Folder. It might be useful, so I've put it on github: https://github.com/andrewrproper/pandoc-folder
You can create a settings folder and file (YAML format), and then run it like this:
python pandoc-folder.py ./path/to/book/.pandoc-folder/settings-file.yml
there is an example-book folder and matching .bat and .sh scripts for how to convert the markdown from the example-book folder into a single output file.
I hope this might be useful to someone.
John MacFarlane's answer is almost right. However, one needs to create the subfolder for pandoc, in case it doesn't exist. This is how I'd do it:
TXTDIR=sources
HTMLS=$(wildcard *.html)
MDS=$(patsubst %.html,$(TXTDIR)/%.markdown, $(HTMLS))
.PHONY : all
all : $(MDS)
$(TXTDIR)/%.markdown : %.html $(TXTDIR)
mkdir -p $(dir $#)
pandoc -f html -t markdown -s $< -o $#
This is a solution using ipython:
from pathlib import Path
files = [path for path in Path('.').rglob('*.html')]
for f in files:
!pandoc -s {str(path)} -o {path.name.replace(".html",".md")}
Note that you must execute the command inside the directory where you keep the HTML files, and your file will be saved in the same directory. In case just change the output path.
I want to compile each and every file in a directory as well as its sudirectories and directories of that subdirectories. So the basic idea is to compile all the files in that project. For that i have to write a shell script.
My code
cd pathtomaindirectory
gcc *.c
In this i have to run the that command on all the subdirectory files with .c extensions. So how do i use a for loop to it. I'm new to shell script
Thank You
With bash, enable recursive glob wildcards:
shopt -s globstar nullglob
gcc **.c
You could do something like
cd path/to/main/directory
find . -name '*.c' -exec gcc -c {} \;
gcc -o app *.o
I need to take an argument which is a directory of the current directory and search its folders and compile any C files in those folders. I'm just beginning shell scripting in Bash and am a little over my head.
So far things I've tried included using find to search for the files and then pipe it to xargs to compile but kept getting an error saying that testing.c wasn't a directory.
find ~/directory -name *.c | xargs gcc -o testing testing.c
I've also tried ls -R to search folders for .c files but don't know how to then take the paths as arguments to then move to and compile?
find ~/directory -type f -name "*.c" -print0 |
while IFS= read -r -d '' pathname; do
gcc -o "${pathname%.c}" "$pathname"
done
find directory -type f -name "*.c" -exec sh -c \
'cd $(dirname $1);make $(basename $1 .c)' sh {} \;
As #shx2 suggested, using make (or some other build system) would arguably be the best approach. You don't want to go compiling files in some source tree without a proper build system.
I'm working on a C kernel and I want to make it easier to compile all of the sources by using a bash script file. I need to know how to do a foreach loop and only get the files with a .c extension, and then get the filename of each file I find so I can make gcc compile each one.
Use find to walk through your tree
and then read the list it generates using while read:
find . -name \*.c | while read file
do
echo process $file
done
If the action that you want to do with file is not so complex
and can be expressed using ore or two commands, you can avoid while
and make all things with the find itself. For that you will use -exec:
find . -name \*.c -exec command {} \;
Here you write your command instead of command.
You can also use -execdir:
find . -name \*.c -execdir command {} \;
In this case command will be executed in the directory of found file (for each file that was found).
If you're using GNU make, you can do this using only make's built-in functions, which has the advantage of making it independent of the shell (but the disadvantage of being slower than find for large trees):
# Usage: $(call find-recursive,DIRECTORY,PATTERN)
find-recursive = \
$(foreach f,$(wildcard $(1)/*),\
$(if $(wildcard $(f)/.),\
$(call find-recursive,$(f),$(2)),\
$(filter $(2),$(f))))
all:
#echo $(call find-recursive,.,%.c)