I have a short bash script to get source code's dependency files.
#!/bin/sh
rule=$(cpp -P -w -undef -nostdinc -C -M file.cc)
rule=${rule##*:}
#echo $rule
echo ${rule//\\}
Unfortunately, it outputs ./findDep.sh: 5: ./findDep.sh: Bad substitution.
But if I uncomment echo $rule, the script will execute without any problem:
lib.h macro.inc fundamental.h lib/fs.h lib/net.h \ lib/net/fetch.h
lib.h macro.inc fundamental.h lib/fs.h lib/net.h lib/net/fetch.h
Any one know why?
Thanks in advance.
You should change #!/bin/sh to #!/bin/bash or #!/usr/bin/env bash.
I can't reproduce your problem here with Bash 4.2.29.
However, did you know that read will join lines with \ newline continuations by default?
read rule < <(cpp -P -w -undef -nostdinc -C -M file.cc)
echo "${rule##*:}"
Or, in a more sh-compatible way (I think),
cpp -P -w -undef -nostdinc -C -M file.cc | {
read rule
echo "${rule##*:}"
}
Related
I am writing a Makefile, which will list all headers included by a.cpp, b.cpp and c.h files. However, I got the error of unexpected EOF. Similar questions are always caused by the line terminator, like they used CRLF instead of LF for an EOL. However, my Text editor was set to using LF and I recheck this by delete all EOL and re-added. Unfortunately, the error still remains. Here are the codes:
#!/bin/bash
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
I got this error message:
for file in "Bigram.cpp client.cpp Bigram.h"
/bin/sh: -c: line 1: syntax error: unexpected end of file"
Thanks in advance for any help.
First note you have to escape $ that you want the shell to see, otherwise make will expand them before calling the shell. However, your main problem is that every logical line in a make recipe is a separate shell command. So, this rule:
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
will cause make to invoke the shell commands:
/bin/sh -c 'for file in a.cpp b.cpp b.h'
/bin/sh -c 'do'
/bin/sh -c 'echo "ile includes headers: "'
/bin/sh -c 'grep -E '^#include' ile | cut -f2'
/bin/sh -c 'done'
You need to use backslashes to "continue" a logical line across newlines if you want them all sent to the same shell, and you have to add semicolons to make that work since the newlines no longer serve as command delimiters:
list-header:
for file in a.cpp b.cpp b.h; \
do \
echo "$$file includes headers: "; \
grep -E '^#include' $$file | cut -f2; \
done
I am writing a Makefile, which will list all headers included by a.cpp, b.cpp and c.h files. However, I got the error of unexpected EOF. Similar questions are always caused by the line terminator, like they used CRLF instead of LF for an EOL. However, my Text editor was set to using LF and I recheck this by delete all EOL and re-added. Unfortunately, the error still remains. Here are the codes:
#!/bin/bash
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
I got this error message:
for file in "Bigram.cpp client.cpp Bigram.h"
/bin/sh: -c: line 1: syntax error: unexpected end of file"
Thanks in advance for any help.
First note you have to escape $ that you want the shell to see, otherwise make will expand them before calling the shell. However, your main problem is that every logical line in a make recipe is a separate shell command. So, this rule:
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
will cause make to invoke the shell commands:
/bin/sh -c 'for file in a.cpp b.cpp b.h'
/bin/sh -c 'do'
/bin/sh -c 'echo "ile includes headers: "'
/bin/sh -c 'grep -E '^#include' ile | cut -f2'
/bin/sh -c 'done'
You need to use backslashes to "continue" a logical line across newlines if you want them all sent to the same shell, and you have to add semicolons to make that work since the newlines no longer serve as command delimiters:
list-header:
for file in a.cpp b.cpp b.h; \
do \
echo "$$file includes headers: "; \
grep -E '^#include' $$file | cut -f2; \
done
I have this configuration file in my CI where I'm specifying a header file and some CMAKE flags on one line.
The configuration file looks like this (filelist):
./settings6.h -DMY_COMPILE_FLAGS="-m32 -fstrict-aliasing"
./settings7.h -DMY_FEATURE_1=ON
./settings8.h -DMY_FLAG=ON -DMY_FEATURE_2=ON -DMY_INCLUDE_DIR=/usr/include/
Now, I'm using a bash script to process this configuration file:
#!/bin/bash
SCRIPTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
while read i; do
HEADERFILE=$(echo $i | cut -d ' ' -f 1)
CMAKEFLAGS=$(echo $i | cut -s -d ' ' -f 2-)
if [[ "$HEADERFILE" == "" ]]; then
continue
fi
CFLAGS="-Werror" cmake "my_build_dir" "$CMAKEFLAGS" -G "Ninja" -DMY_EXTRA_INCLUDE="$SCRIPTDIR/$HEADERFILE" -B"build_env_dir" > /dev/null
ninja -C "build_env_dir"
done <<ENDOFINPUT
$(grep -v '^#' $SCRIPTDIR/filelist)
ENDOFINPUT
When I have the bash script as above, the line with settings6.h gets processed properly, i.e. the MY_COMPILE_FLAGS are set to -m32 -fstrict-aliasing.
However, settings8.h is failing because the value of MY_FLAG is seen by CMAKE as ON -DMY_FEATURE_2=ON -DMY_INCLUDE_DIR=/usr/include/, so MY_FEATURE_2 and MY_INCLUDE_DIR are not processed correctly.
After googling around a bit, I thought, well, surely a quoting issue, probably I have to remove the quotes around $CMAKEFLAGS like this:
CFLAGS="-Werror" cmake "my_build_dir" $CMAKEFLAGS -G "Ninja" -DMY_EXTRA_INCLUDE="$SCRIPTDIR/$HEADERFILE" -B"build_env_dir" > /dev/null
In fact, this lets settings8.h work as expected (all three options are processed), but now, settings6.h is suddenly failing since CMAKE complains:
CMake Error: The source directory "/src/-fstrict-aliasing"" does not exist
Can someone guide me please how I read the settings correctly from my filelist so that settings6.h and settings8.h both succeed?
Here's a Makefile which refactors this into a sequence of recipes.
Cases := $(patsubst %.h,%,$(wildcard ./settings*.h))
all_done := $(patsubst %,.%.done,$(Cases))
.PHONY: all
all: $(all_done)
cases.mk: filelist.txt
sed 's%^\./%case_%;s% % := %' $< >$#
include cases.mk
.%_done: ./%.h
CFLAGS="-Werror" cmake "my_build_dir" $(case_$*) -G "Ninja" \
-DMY_EXTRA_INCLUDE="$<" -B"build_env_dir" > /dev/null
ninja -C "build_env_dir"
I am writing a Makefile, which will list all headers included by a.cpp, b.cpp and c.h files. However, I got the error of unexpected EOF. Similar questions are always caused by the line terminator, like they used CRLF instead of LF for an EOL. However, my Text editor was set to using LF and I recheck this by delete all EOL and re-added. Unfortunately, the error still remains. Here are the codes:
#!/bin/bash
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
I got this error message:
for file in "Bigram.cpp client.cpp Bigram.h"
/bin/sh: -c: line 1: syntax error: unexpected end of file"
Thanks in advance for any help.
First note you have to escape $ that you want the shell to see, otherwise make will expand them before calling the shell. However, your main problem is that every logical line in a make recipe is a separate shell command. So, this rule:
list-header:
for file in a.cpp b.cpp b.h
do
echo "$file includes headers: "
grep -E '^#include' $file | cut -f2
done
will cause make to invoke the shell commands:
/bin/sh -c 'for file in a.cpp b.cpp b.h'
/bin/sh -c 'do'
/bin/sh -c 'echo "ile includes headers: "'
/bin/sh -c 'grep -E '^#include' ile | cut -f2'
/bin/sh -c 'done'
You need to use backslashes to "continue" a logical line across newlines if you want them all sent to the same shell, and you have to add semicolons to make that work since the newlines no longer serve as command delimiters:
list-header:
for file in a.cpp b.cpp b.h; \
do \
echo "$$file includes headers: "; \
grep -E '^#include' $$file | cut -f2; \
done
I want to process each source code file after it has been preprocessed:
myprocess `gcc -E file1.c`
myprocess `gcc -E file2.c`
...
myprocess `gcc -E fileN.c`
This gets tedious so how do I make this a single command?
That is, something along the line:
myprocess SOMETHINGMAGIC(gcc -E file*.c)
Thanks in advance!
You mean like
for i in file*.c ; do
myprocess `gcc -E $i`
done
If this is part of an ongoing processes (as opposed to a one time thing), use make, it is good at automating work pipelines.
In particular use suffix rules with traditional make or gmake style implicit rules.
Here is an outline for a suffix rule implementation:
.c.cpre:
$(CC) -E $(CPPFLAGS) -o $# $<
.cpre.cmy:
$(MY_PROCESS) $<
# Or whatever syntax you support..
#
# You could
# $(RM) $<
# here, but I don't recommend it
.cmy.o:
$(CC) -c $(CFLAGS) $(CPPFLAGS) -o $# $<
# $(RM) $<
No magic nedded, just
my_process $(gcc -E *.c)
Note that I used the $(command) form because backticks are deprecated.
As an aside: are you sure you want to do that? You are putting the whole output of gcc -E as command line parameters of my_process. Not sure this is what you want. Maybe you want to use a simple pipe
gcc -E file.c | my_process
so that the output of gcc becomes the input of my_process.
In the latter case something like
for c_file in *.c ; do
gcc -E $c_file | myprocess > ${c_file}.processed
done
would do the trick.
Perhaps this is what you're looking for?:
for i in file*.c ; do
echo "Header for myprocess: $i"
gcc -E $i 2>&1
done | myprocess
2>&1 assumes you want to grab stderr, too
echo ... gives myprocess a starting point for each compilation
How about:
myprocess {$(gcc -E file1.c),$(gcc -E file2.c),$(gcc -E file3.c)}
Or if you pipe it:
{$(gcc -E file1.c),$(gcc -E file2.c),$(gcc -E file3.c)} | myprocess
It's been a while since I've used bash, so, please, point out my mistakes!
ls file*.c | xargs --replace myprocess $(gcc -E {})