I have the example makefile below. I am trying to create a directory structure with multiple substitutions, and it seems to be doing a cartesian product when I want by-index substitution:
genomes = C57B6NJ AKJ
GENOME_DIRS = ${genomes:%=${BASE_DATA_DIR}/genomes/%}
TWO_BITS = ${genomes:%=${GENOME_DIRS}/%.2bit}
all:
#echo ${TWO_BITS}
# OUTPUT
# Thu Apr 30 16:35 ~ $make all
# /genomes/C57B6NJ /genomes/AKJ/C57B6NJ.2bit /genomes/C57B6NJ /genomes/AKJ/AKJ.2bit
But the output I desire is
/genomes/C57B6NJ/C57B6NJ.2bit /genomes/AKJ/AKJ.2bit
How can I get this kind of positional substitution?
Your second substitution is asking make to replace each word in genomes with the entire contents of GENOME_DIRS followed by the original word followed by the string .2bit which seems to be exactly what it is doing.
You appear to want to be adding .2bit to the end of the values in GENOME_DIRS in which case you should just do that.
Either with:
TWO_BITS = $(GENOME_DIRS:%=%.2bit)
or with:
TWO_BITS = $(addsuffix .2bit,$(GENOME_DIRS))
Related
For example, I want $(remove_all_extensions foo.bar.buz.x.y.z) to return foo.
basename doesn't work because it only remove the last extension so $(basename foo.bar.buz.x.y.z) returns foo.bar.buz.x.y.
Is there any built-in function I can use in Makefile to achieve the requirement?
You can use glob-match in gmtt which can cut strings with glob syntax. For your problem the pattern *.* is fitting. If a match with the input string results, glob-match returns the string portion corresponding to the glob character, which in this case is * for the leading portion, then the first . and then the rest with all commas for the remaining *.
include gmtt/gmtt.mk
FILES := foo.bar.buz.x.y.z frob.ni.ca.te biz.bof zulu.oat.s
$(foreach f,$(FILES),$(info $(call glob-match,$(f),*.*)))
Output:
foo . bar.buz.x.y.z
frob . ni.ca.te
biz . bof
zulu . oat.s
so you have to $(firstword ) on the result of that call.
i have a a couple of variables with a number in its names. e.g
SERVER_IP48_SUBNET
..
SERVER_IP60_SUBNET
And an additional variable
SERVER_IP
Im trying to expand/concatenate them in the following way:
ALLIPs=${SERVER_IP}
for i in {48..64}; do
ALLIPs=${ALLIPs},${SERVER_IP${i}_SUBNET}
done
as you can imagine this script fails saying:
Wrong substitution
Does anybody of you know a good solution for this problem?
Thanks so far
Use a nameref with bash version 4.3 +
ALLIPs=${SERVER_IP}
for i in {48..64}; do
declare -n tmp="SERVER_IP${i}_SUBNET"
ALLIPs+=",$tmp"
done
But you should really be using an array in the first place:
server_ip=0.0.0.0
subnet_ip=(
[48]=1.1.1.1
[49]=2.2.2.2
# ...
[64]=16.16.16.16
)
all_ips=( "$server_ip" )
for i in {48..64}; do
all_ips+=( "${subnet_ip[i]}" )
done
(
IFS=,
echo "ALLIPs = ${all_ips[*]}"
)
Get out of the habit of using ALLCAPS variable names, leave those as
reserved by the shell. One day you'll write PATH=something and then
wonder why
your script is broken.
I just noticed, if you just want a to join the IP addresses with commas, and you're using an array, you don't need a loop at all:
all_ips=$(
IFS=,
set -- "$server_ip" "${subnet_ip[#]}"
echo "$*"
)
You can use ${!varprefix#} or ${!varprefix*} to expand to all variables with that common prefix (the difference is the same as $# and $*):
SERVER_IP48_SUBNET=48sub
SERVER_IP49_SUBNET=49sub
SERVER_IP50_SUBNET=50sub
SERVER_IP=1.2.3.4
# set this as empty since !SERVER_IP# also matches SERVER_IP
ALLIPS=""
for var in "${!SERVER_IP#}"; do
ALLIPS=$ALLIPS,${!var}
done
This would probably be more practical if you could invert the names like this, since we can only match prefixes:
SERVER_IP_SUBNET_48=48sub
SERVER_IP_SUBNET_49=49sub
SERVER_IP_SUBNET_50=50sub
SERVER_IP=1.2.3.4
ALLIPS=$SERVER_IP
for var in "${!SERVER_IP_SUBNET_#}"; do
ALLIPS=$ALLIPS,${!var}
done
More info on this feature in the bash manual.
One idea:
SERVER_IP48_SUBNET=48sub
SERVER_IP49_SUBNET=49sub
SERVER_IP50_SUBNET=50sub
SERVER_IP=1.2.3.4
ALLIPs=${SERVER_IP}
for i in {48..50}
do
tmpvar="SERVER_IP${i}_SUBNET" # build the variable name
ALLIPs="${ALLIPs},${!tmpvar}" # indirect variable reference via tmpvar
done
echo "ALLIPs = $ALLIPs}"
This generates:
ALLIPs = 1.2.3.4,48sub,49sub,50sub
Given a variable, how can I create another variable with the leading repetitions of ../ that the first variable starts with, albeit with one less repetition?
For instance, given ../../../foo/bar, how do I end up with ../../?
I've tried a perl regexp like perl -e '$a = "../../../foo/bar"; $a =~ /^\.\.\/((\.\.\/)+)/; print "$1\n"' but can't figure out the magic incantation of quotes, backslashes, dollar signs, and so on needed to run it within $(shell).
Further I suspect there's a simpler way to do it.
This will remove one ../ at the front: $(patsubst ../%,%,$(ORIGINAL_PATH))
To isolate the first streak of ../ one could go as follows:
space := $(strip) $(strip)#
FIRST_STREAK := $(firstword $(subst $(space)../,../,$(subst ../,../$(space),$(ORIGINAL_PATH))))
Let's write this as a function:
space := $(strip) $(strip)#
UP_PATH = $(patsubst ../%,%,$(firstword $(subst $(space)../,../,$(subst ../,../$(space),$1))))
Test it:
ORIGINAL_PATH := ../../../foo/bar/../baz
$(info $(call UP_PATH,$(ORIGINAL_PATH)))
Output:
../../
I am trying to automate creation of folders. Each folder should have a number at beginning of file name (increased by the for-loop [$i]. Also the rest of the folder name should be build up by variables that are constructed as [folder_x] where [x] is supposed to be stepped up also by the loop.
To be more specific. How do you build a string that is combined by the for-loops [$i] and a variable that is called, but should at the end also use the [$i]?
For more details see below:
#!/bin/bash
# Variables to be used
folder_1=1-folderOne
folder_2=2-folderTwo
folder_3=3-folderThree
# This is the folder names that should be created:
# mkdir /tmp2/1-folderOne
# mkdir /tmp2/2-folderTwo
# mkdir /tmp2/3-folderThree
# The for loop should combine the [$i] and above [folder_x],
# where the [x] should also be increased by the loop.
# Below is what i have right now:
# Note! The text "created-by-forloop" is just dummy text,
# and should be replace by the real solution.
for i in 1 2 3
do
if [ ! -d /tmp2/$i-created-by-forloop ]; then
mkdir -p /tmp2/$i-created-by-forloop;
fi
done
Use an array of names instead of distinct variables for each:
numbers=(One Two Three)
for i in "${!numbers[#]}" ; do
mkdir /tmp2/$((i+1))-folder"${numbers[i]}"
done
The loop iterates $i over the indices of the array. We need to add 1 to the index as arrays are zero based, but we want our files to be numbered from 1, not 0.
My makefile has:
debug:
W1 := foo bar cat
I want to extract each word foo, bar and cat and get the output as below:
v1=foo
v2=bar
v3=cat
I tried:
W1 :=foo bar cat
v1 :=$(word 1, $$(W1))
v2 :=$(word 2, $$(W1))
v3 :=$(word 3, $$(W1))
debug:
#echo "${v1}"
#echo "${v2}"
#echo "${v3}"
Does not work the way I wanted. Please help
(Answered in a comment. See Empty InfoWindow when Marker is clicked )
#MadScientist wrote:
Why are you using double-dollar signs? That simply tells make to not expand the value, so you definitely won't get what you want. Use $(word 2,$(W1)) etc.
#Etan Reisner wrote:
Also since the variables are make (and not shell) variables you can use echo '${v1}' (or echo '$(v1)' which would be my preference) in the rule body instead of using double quotes.