I'm using make to automate the creation of a file which depends on several other files, each of which depends on an intermediate file being created, and ultimately on a file on the hard drive.
all
/ \
a1 a2
| |
b1 b2
| |
file1 file2
all depends on a1 and a2, which depend on the intermediate files b1 and b2, which are in turn made from file1 and file2 on the hard drive. Here's my Makefile.
all: $(patsubst %, a%, 1 2)
echo "making all"
a%: b%
echo "making $# from $^"
b%: file%
echo "making $# from $^"
When I do make -n, I see the following output (after touching file1 and file2).
echo "making b1 from file1"
echo "making a1 from b1"
echo "making b2 from file2"
echo "making a2 from b2"
echo "making all"
rm b1 b2
My problem is that b1 and b2 are extremely large, and I can't keep them both on my hard drive at the same time (in my real situation, it's not just two but dozens of these files). Is there a way to tell make to dispose of b1 as soon as a1 is finished, before starting to make a2? In other words, I would like to see the following output.
echo "making b1 from file1"
echo "making a1 from b1"
rm b1
echo "making b2 from file2"
echo "making a2 from b2"
rm b2
echo "making all"
Related
this might be obvious, but I can't get it done: say there is a file called a1.xml and I want to create 7 additional copies named with sequential numbers (a2.xml, a3.xml). I tried to break the problem in different steps but still stuck. any thoughts?
thanks
You can do:
for f in a{2..7}.txt; do
cp a1.xml "$f"
done
Easiest:
for f in a2 a3 a4 a5 a6 a7
do cp a1.xml $f.xml
done
Also works (given command seq) — and better if you need 500 copies:
for n in $(seq 2 7)
do cp a1.xml a$n.xml
done
Or use arithmetic:
i=2
while [ $i -le 7 ]
do
cp a1.xml a$i.xml
((i++))
done
Etc.
I have a file that contains information in two columns:
box1 a1
box2 a2
I'm trying to read this file line by line into read and have each line items be put into a variable.
On the first pass, $a would contain box1 and $b would contain a1.
On the second pass, $a would contain box2 and $b would contain a2, etc.
An example of the code that I am using to try to achieve is this:
for i in text.txt; do
while read line; do
echo $line | read a b;
done < text.txt;
echo $a $b;
done
This gives me the following results:
box1 a1 box2 a2
When I expected the following results:
box1 a1
box2 a1
How can I fix this?
Piping into a read command causes the variables to be set in a subshell, which makes them inaccessible (indeed, they are gone) to the rest of your code. In this case, though, you don't even need the for loop or the second read command:
while read -r a b; do
echo "$a" "$b"
done < text.txt
To store the variables into the current shell:
~$ {
read -r a
read -r b
read -r c
} < <(printf '%s\n' one two three)
$ echo "a=$a b=$b c=$c"
a=one b=two c=three
Another approach
echo -e "one\ntwo\nthree" | {
read -r a;
read -r b;
read -r c;
echo "output of ($a, $b, $c)"
}
I want to use the same complex block of recipes for an implicit and a normal rule.
Also, I want make to echo the next command AFTER thre previous command executed.
Make does not allow mixing implicit and normal rules.
Desired output:
$ make foo bar.abc
echo a
a
echo b
b
echo a
a
echo b
b
This won't work:
%.abc foo:
echo a
echo b
This will work:
CMD = echo a && echo b
foo:
$(CMD)
%.abc:
$(CMD)
but the output is not what I want:
$ make foo bar.abc
echo a && echo b
a
b
echo a && echo b
a
b
You can use define to assign multi-line values to variables:
define CMD
echo a
echo b
endef
I have a bash script called foo with variable number of arguments, with the first one being a required one, i.e.:
foo a1 b2 b3 b4 ...
I understand that in bash $1 will get me argument a1, but is there a way to get all the rest of the arguments? $# or $* seem to get me all the arguments including a1.
Slice the $# array.
echo "${#:2}"
You can use shift command for that. That will remove $1 and you can access the rest of arguments starting with $1.
#!/bin/sh
echo $*
shift
echo $*
shift will shift all the parameters, running the previous example would give:
$ test_shift.sh a b c d e
a b c d e
b c d e
./foo.sh 1 2 3 4
#!/bin/bash
echo $1;
echo $2;
echo $3;
echo $4;
Will output:
1
2
3
4
I'm writing a simple script to generate all combinations of a and b of a given length (say 10). I want to be able to do this on a command line (I know this is fairly easy if I just put everything in a bash script file and execute it). However, I was wondering if it's possible to do without any extra files. Here's what I have so far:
n=10;
for i in `seq 1 1 $n`; do
echo "for a$i in {a..b}; do ";
done;
echo -n "echo ";
for i in `seq 1 1 $n`; do
echo -n '$'"a$i"; done;
echo;
for i in `seq 1 1 $n`; do
echo "done;";
done
(I formatted the code for readability, but it's actually all on one line run from a prompt)
This gives me the following output:
for a1 in {a..b}; do
for a2 in {a..b}; do
for a3 in {a..b}; do
for a4 in {a..b}; do
for a5 in {a..b}; do
for a6 in {a..b}; do
for a7 in {a..b}; do
for a8 in {a..b}; do
for a9 in {a..b}; do
for a10 in {a..b}; do
echo $a1$a2$a3$a4$a5$a6$a7$a8$a9$a10
done;
done;
done;
done;
done;
done;
done;
done;
done;
done;
which is just fine. If I copy that and paste it back on the command line, it works like a charm and gives me the result.
The question is how do I do this with just the initial script, without copy-pasting and without redirecting anything to files.
I've tried sticking $( ) around the script, but that gives me "No command 'for' found'", since it's not really a command but a bash builtin. I've tried putting eval somewhere before this, but I just keep getting more errors. I'm a bit stuck, so any help would be greatly appreciated.
(Btw, just to reiterate, I'm doing this more or less to just learn bash more -- that's why I don't want to redirect the output to a file and then execute that file. I know how to do that part, but I don't know how to just do it from command line)
You need to use an eval, $() gives you a string.
eval $( echo echo foo )
Another option is to stick into a subshell and pipe it to a bash:
(echo echo foo) | /bin/bash
You can do for i in $(seq $n) instead of seq 1 1 $n.
You can do for ((i=1; i<=$n; i++)) and avoid calling an external utility.
You can do this (slightly hacky with only one loop):
$ a=A; b=B; n=4; s=''; for ((i=1;i<=n;i++)); do s+="{$a..$b}"; done; eval echo "''" $s"$'\n'"
or this (highly hacky without any loops):
$ a=A; b=B; n=4; eval echo "''" $(printf "{$a..$b}%.0s" $(eval echo "{1..$n}"))"$'\n'"
Either one will get you this:
AAAA
AAAB
AABA
AABB
ABAA
ABAB
ABBA
ABBB
BAAA
BAAB
BABA
BABB
BBAA
BBAB
BBBA
BBBB