cp one file to multiple, sequentially named files - shell

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.

Related

Bash Loop Help (want to echo aa, bb, cc)

Loop logic always confuses me, this is probably a simple solution. My current loop:
for i in a b; do for j in a b; do echo $i$j; done; done
This loop prints the following output:
aa
ab
ba
bb
I would like for it to only print:
aa
bb
I just want it to match up the first two letters, then the second two letters and so on. Eventually I want to expand this over files in two different directories. So I want to print the first file name in dir1, then the first in dir2. Then the 2nd in dir1 and the 2nd in dir2. Just trying to simplify that and understand the logic first.
I would suggest using arrays to solve your problem:
dir1_files=( dir1/* )
dir2_files=( dir2/* )
for (( i = 0; i < ${#dir1_files[#]}; ++i )); do
echo "${dir1_files[i]} ${dir2_files[i]}"
done
This assumes that the number of files in each directory is the same.
Just check that they're the same?
for i in a b; do
for j in a b; do
if [[ "$i" = "$j" ]]; then
echo $i$j
fi
done
done
Or:
for i in a b; do for j in a b; do [[ "$i" = "$j" ]] && echo $i$j; done; done

Convert Twos complement results on one line rather than line by line, how?

I am using the code below to take values and convert them to two's complementary. I have tried twos FF & twos FE. How can I put it in a string so the results come back -1, -2 rather than line by line?
twos() { x=$((16#$1)); [ "$x" -gt 128 ] && ((x=x-256)); echo "$x"; }
Now:
$ twos FF
-1
$ twos FE
-2
$ twos 01
1
Updating my previous answer to add commas, let's create the function:
$ twosx() (c=; for x in "$#"; do x=$((16#$x)); [ "$x" -gt 127 ] && ((x=x-256)); printf "%s%s" "$c" "$x"; c=", "; done; echo ""; )
Examples of its use:
$ twosx 00
0
$ twosx 00 01 7F 80 FE FF
0, 1, 127, -128, -2, -1
As #choroba has commented, there is an answer providing (almost) the code you need already.
I am still answering this because the question actually consists of two problems, that is calculating two's complement and doing it multiple times. If you separate the two problems instead of duplicating code, you can also do other calculations multiple times in the same way:
# execute a command for each argument provided,
# concatenating output with ", "
# $1 ....... command
# $2 - $n .. command arguments
multi() {
cmd="$1"
shift
for arg in $#
do
"$cmd" "$arg"
done | xargs | sed 's/ /, /g'
}
Usage:
multi twos FF FE AB 01
-1, -2, -85, 1

Generate dummy files in bash

I'd like to generate dummy files in bash. The content doesn't matter, if it was random it would be nice, but all the same byte is also acceptable.
My first attempt was the following command:
rm dummy.zip;
touch dummy.zip;
x=0;
while [ $x -lt 100000 ];
do echo a >> dummy.zip;
x=`expr $x + 1`;
done;
The problem was its poor performance. I'm using GitBash on Windows, so it might be much faster under Linux but the script is obviously not optimal.
Could you suggest me a quicker and nice way to generate dummy (binary) files of given size?
You can try head command:
$ head -c 100000 /dev/urandom >dummy
You may use dd for this purpose:
dd if=/dev/urandom bs=1024 count=5 of=dummy
if:= in file
of:= out file
bs:= block size
Note, that
x=`expr $x + 1`;
isn't the most efficient way to calculation in bash. Do arithmetic integer calculation in double round parenthesis:
x=((x+1))
But for an incremented counter in a loop, there was the for-loop invented:
x=0;
while [ $x -lt 100000 ];
do echo a >> dummy.zip;
x=`expr $x + 1`;
done;
in contrast to:
for ((x=0; x<100000; ++x))
do
echo a
done >> dummy.zip
Here are 3 things to note:
unlike the [ -case, you don't need the spacing inside the parens.
you may use prefix (or postfix) increment here: ++x
the redirection to the file is pulled out of the loop. Instead of 1000000 opening- and closing steps, the file is only opened once.
But there is still a more simple form of the for-loop:
for x in {0..100000}
do
echo a
done >> dummy.zip
This will generate a text file 100,000 bytes large:
yes 123456789 | head -10000 > dummy.file
If your file system is ext4, btrfs, xfs or ocfs2, and if you don't care about the content you can use fallocate. It's the fastest method if you need big files.
fallocate -l 100KB dummy_100KB_file
See "Quickly create a large file on a Linux system?" for more details.
$ openssl rand -out random.tmp 1000000
Possibly
dd if=/dev/zero of=/dummy10MBfile bs=1M count=10
echo "To print the word in sequence from the file"
c=1
for w in cat file
do
echo "$c . $w"
c = expr $c +1
done
Easy way:
make file test and put one line "test"
Then execute:
cat test >> test
ctrl+c after a minute will result in plenty of gigabytes :)

bash: executing an output of a script as a script

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

How to loop in bash script?

i have following lines in a bash script under Linux:
...
mkdir max15
mkdir max14
mkdir max13
mkdir max12
mkdir max11
mkdir max10
...
how is the syntax for putting them in a loop, so that i don't have to write the numbers (15,14..) ?
with bash, no need to use external commands like seq to generate numbers.
for i in {15..10}
do
mkdir "max${i}"
done
or simply
mkdir max{01..15} #from 1 to 15
mkdir max{10..15} #from 10 to 15
say if your numbers are generated dynamically, you can use C style for loop
start=10
end=15
for((i=$start;i<=$end;i++))
do
mkdir "max${i}"
done
No loop needed for this task:
mkdir max{15..10} max0{9..0}
... but if you need a loop construct, you can use one of:
for i in $(seq [ <start> [ <step> ]] <stop>) ; do
# you can use $i here
done
or
for i in {<start>..<stop>} ; do
# you can use $i here
done
or
for (( i=<start> ; i < stop ; i++ )) ; do
# you can use $i here
done
or
seq [ <start> [ <step> ]] <stop> | while read $i ; do
# you can use $i here
done
Note that this last one will not keep the value of $i outside of the loop, due to the | that starts a sub-shell
for a in `seq 10 15`; do mkdir max${a}; done
seq will generate numbers from 10 to 15.
EDIT: I was used to this structure since many years. However, when I observed the other answers, it is true, that the {START..STOP} is much better. Now I have to get used to create directories this much nicer way: mkdir max{10..15}.
Use a for-loop
for i in {1..15} ; do
mkdir max$i
done

Resources