I have a Makefile that includes another makefile, say CommonMakefile. There is a variable that I want to prefix with a value (cf. appending using +=) keeping rest of the value intact.
V = value1 # set in CommonMakefile that is included
V = value0 $(V) # I want to prefix value0 to V
# rule defined in CommonMakefile that is included
whatsV:
echo $V # first char is a <TAB> here
How can I do that?
Expecting:
$ gmake whatsV
value0 value1
Actual:
$ gmake whatsV
Makefile:3: *** Recursive variable `V' references itself (eventually). Stop.
PS: I can-not/do-not-want to change CommonMakefile.
This won't work:
V = value0 $(V)
because here V is a recursively expanded variable, and it can't be defined in terms of itself. But change it to a simply expanded variable:
V := value0 $(V) # <-- Note the colon
and it will work as intended.
Related
I have a filename as a string, say filname="a/b/c/d.png".
Is there a general method to extract the parent directory at a given level using ONLY shell parameter expansion?
I.e. I would like to extract "level 1" and return c or "level 2" and return b.
Explicitly, I DO NOT want to get the entire parent path (i.e. a/b/c/, which is the result of ${filename%/*}).
Using just shell parameter expansion, assuming bash, you can first transform the path into an array (splitting on /) and then ask for specific array indexes:
filename=a/b/c/d.png
IFS=/
filename_array=( $filename )
unset IFS
echo "0 = ${filename_array[0]}"
echo "1 = ${filename_array[1]}"
echo "2 = ${filename_array[2]}"
echo "3 = ${filename_array[3]}"
Running the above produces:
0 = a
1 = b
2 = c
3 = d.png
These indexes are the reverse of what you want, but a little
arithmetic should fix that.
Using zsh, the :h modifier trims the final component off a path in variable expansion.
The (s:...:) parameter expansion flag can be used to split the contents of a variable. Combine those with normal array indexing where a negative index goes from the end of the array, and...
$ filename=a/b/c/d.png
$ print $filename:h
a/b/c
$ level=1
$ print ${${(s:/:)filename:h}[-level]}
c
$ level=2
$ print ${${(s:/:)filename:h}[-level]}
b
You could also use array subscript flags instead to avoid the nested expansion:
$ level=1
$ print ${filename[(ws:/:)-level-1]}
c
$ level=2
$ print ${filename[(ws:/:)-level-1]}
b
w makes the index of a scalar split on words instead of by character, and s:...: has the same meaning, to say what to split on. Have to subtract one from the level to skip over the trailing d.png, since it's not stripped off already like the first way.
The :h (head) and :t (tail) expansion modifiers in zsh accept digits to specify a level; they can be combined to get a subset of the path:
> filname="a/b/c/d.png"
> print ${filname:t2}
c/d.png
> print ${filname:t2:h1}
c
> print ${filname:t3:h1}
b
If the level is in a variable, then the F modifier can be used to repeat the h modifier a specific number of times:
> for i in 1 2 3; printf '%s: %s\n' $i ${filname:F(i)h:t}
1: c
2: b
3: a
If using printf (a shell builtin) is allowed then this will do the trick in bash:
filename='a/b/c/d.png'
level=2
printf -v spaces '%*s' $level
pattern=${spaces//?/'/*'}
component=${filename%$pattern}
component=${component##*/}
echo $component
prints out
b
You can assign different values to the variable level.
I need to declare a makefile variable with a leading white space, and I used the following code, but it doesn't work
SPACE :=
SPACE +=
VIU_DIAG_SW_VERSION :=$(SPACE)AJ
Try:
NULL :=
VIU_DIAG_SW_VERSION := $(NULL) AJ
Demo:
$ cat Makefile
NULL :=
VIU_DIAG_SW_VERSION := $(NULL) AJ
.PHONY: all
all:
#echo "X$(VIU_DIAG_SW_VERSION)X"
$ make
X AJX
And if you want a variable containing just one space:
SPACE := $(NULL) $(NULL)
I need to store multiple command outputs that comes from a for loop into a single variable.
The variable should store the output separated by space.
Output that I am expecting:
for i in a b c d e
do
xyz=$i
done
echo $xyz should return a b c d e
Concatenate strings
#!/usr/bin/env sh
# initialize xyz to empty
xyz=
for i in a b c d e
do
# concatenate xyz space and i into xyz
xyz="$xyz $i"
done
# remove the extra leading space from xyz
xyz="${xyz# }"
echo "$xyz should return a b c d e"
Or with growing the arguments array:
#!/usr/bin/env sh
# clear arguments array
set --
for i in a b c d e
do
# add i to arguments array
set -- "$#" "$i"
done
# expand arguments into xyz
xyz="$*"
echo "$xyz should return a b c d e"
I'd suggest to use an array instead of var
for i in a b c d e
do
arr+=("$i")
done
echo "${arr[#]}"
Here is one way to solve the problem:
for i in a b c d e
do
xyz="$xyz$sep$i"
sep=" "
done
echo "$xyz"
Here is the output:
a b c d e
Here is how it works. At first, both variables xyz and sep are unset, so expanding them (that is, $xyz and $sep) leads to empty strings. The sep variable represents a separator which is empty initially.
After the first iteration, the xyz variable is set to a and the sep variable is set to (a space). Now for the second and subsequent iterations, the separator is a space, so now xyz="$xyz$sep$i" appends a space and the new value of $i to the existing value of $xyz. For example, in the second iteration, xyz="$xyz$sep$i" expands to xyz="a b".
Of course, another alternative that avoids altering the separator value between iterations is as follows:
for i in a b c d e
do
xyz="$xyz $i"
done
xyz="${xyz# }"
echo "$xyz"
Once again the output is:
a b c d e
In this alternative solution, the separator is always a space. But that means that in the first iteration of xyz="$xyz$sep$i" adds a leading space to $xyz, that is, after the first iteration, the value of the xyz variable is a (note the leading space). The final value of the variable thus becomes a b c d e (note the leading space again). We get rid of this leading space with the ${xyz# } syntax which removes the smallest prefix pattern.
See POSIX.1-2008: Shell Command Language: 2.6.2 Parameter Expansion for more details on parameter expansion and prefix pattern removal.
From the bash software manual:
${parameter/pattern/string}
The pattern is expanded to produce a
pattern just as in filename expansion. Parameter is expanded and the
longest match of pattern against its value is replaced with string.
... If pattern begins with ‘%’, it must match
at the end of the expanded value of parameter.
And so I've tried:
local new_name=${file/%old/new}
Where string is an absolute file path (/abc/defg/hij and old and new are variable strings.
However this seems to be trying to match the literal %sb1.
What is the syntax for this?
Expected Output:
Given
old=sb1
new=sb2
Then
/foo/sb1/foo/bar/sb1 should become /foo/sb1/foo/bar/sb2
/foo/foosb1other/foo/bar/foosb1bar should become /foo/foosb1other/foo/bar/foosb2bar
Using only shell-builtin parameter expansion:
src=sb1; dest=sb2
old=/foo/foosb1other/foo/bar/foosb1bar
if [[ $old = *"$src"* ]]; then
prefix=${old%"$src"*} # Extract content before the last instance
suffix=${old#"$prefix"} # Extract content *after* our prefix
new=${prefix}${suffix/"$src"/"$dest"} # Append unmodified prefix w/ suffix w/ replacement
else
new=$old
fi
declare -p new >&2
...properly emits:
declare -- new="/foo/foosb1other/foo/bar/foosb2bar"
I have a Makefile with a set of booleans which must be used to control the flags for an external application. The problem is that the flag must be passed as a comma-separated string.
Something like this (non-working pseudo code):
WITH_LIST = ""
WITHOUT_LIST = ""
ifeq ($(BOOL_A),y)
# Append A to list "WITH_LIST"
else
# Append A to list "WITHOUT_LIST"
endif
ifeq ($(BOOL_B),y)
# Append B to list "WITH_LIST"
else
# Append B to list "WITHOUT_LIST"
endif
ifeq ($(BOOL_C),y)
# Append C to list "WITH_LIST"
else
# Append C to list "WITHOUT_LIST"
endif
Now assuming BOOL_A == y, BOOL_B == n and BOOL_C == y, I need to run the following command:
./app --with=A,C --with-out=B
How can I generate these string using Gnu Make?
First you create the two white-space separated lists, either using your method, or thiton's.
Then you use the little trick from the end of section 6.2 of the GNU make manual to create a variable holding a single space, and one holding a comma. You can then use these in $(subst ...) to change the two lists to comma-separated.
PARTS := A B C
BOOL_A := y
BOOL_B := n
BOOL_C := y
WITH_LIST := $(foreach part, $(PARTS), $(if $(filter y, $(BOOL_$(part))), $(part)))
WITHOUT_LIST := $(filter-out $(WITH_LIST), $(PARTS))
null :=
space := $(null) #
comma := ,
WITH_LIST := $(subst $(space),$(comma),$(strip $(WITH_LIST)))
WITHOUT_LIST := $(subst $(space),$(comma),$(strip $(WITHOUT_LIST)))
all:
./app --with=$(WITH_LIST) --with-out=$(WITHOUT_LIST)
A construct like
OPTIONS+=$(if $(filter y,$(BOOL_A)),--with=A,--with-out=A)
should work.
Edit: Sorry, overlooked the necessary collation.
PARTS=A B C
YESSES=$(foreach i,$(PARTS),$(if $(filter y,$(BOOL_$(i))),$(i)))
all:
echo with=$(shell echo $(YESSES) | tr ' ' ',')
The idea is to check for each possible part X whether it's set to yes and insert it into a list if it is yes. This list is whitespace-separated and hard to comma-separate with make, but easy to do this in shell.
Or just use sed: ugly (and untested) but straightforward
WITH_LIST = $(shell echo A$(BOOL_A) B$(BOOL_B) C$(BOOL_C) | sed -e 's/[ABC][^yABC]*//g' -e 's/y//g' -e 's/ /,/g')
WITHOUT_LIST = $(shell echo A$(BOOL_A) B$(BOOL_B) C$(BOOL_C) | sed -e 's/[ABC]y[^ABC]*//g' -e 's/[^ABC ]//g' -e 's/ /,/g')