Shell script: for-loop within while loop - shell

I have 32 files (named by the same pattern, the only difference is the $sample number as written below) that I want to divide into 4 folders. I am trying to use the following script to do this job, but the script is not working, can someone help me with the following shell script please? - Thanks
#!/bin/bash
max=8 #8 files in each sub folder
numberFolder=4
sample=0
while ($numberFolder > 1) #skip the current folder, as 8 files will remain
do
for (i=1; i<9; i++)
do
$sample= $i * $numberFolder # this distinguish one sample file from another
echo "tophat_"$sample"_ACTTGA_L003_R1_001" //just an echo test, if works, will replace it with "cp".
done
$numberFolder--
end

You need to use math contexts -- (( )) -- correctly.
#!/bin/bash
max=8
numberFolder=4
sample=0
while (( numberFolder > 1 )); do # math operations need to be in a math context
for ((i=1; i<9; i++)); do # two (( )), not ( ).
(( sample = i * numberFolder ))
echo "tophat_${sample}_ACTTGA_L003_R1_001" # don't unquote before the expansion
done
(( numberFolder-- )) # math operations need to be inside a math context
done

Related

Bash loop trough files in directory manually

I have N files in my directory, I want to be able to loop through them with batches, for example for 2/3/4 files I want to execute one command. I saw something like this, but can't find it, unfortunately. I will try to explain what I want using pseudo code, for example:
i=0
while i<N:
a=getFile(i)
b=getFile(i+1)
dosomething a
dosomething b
i+=2
Is there any way to do it in Bash? Right now I'm using regexp, but it gets one file at a time (I'm using *.ext because all files have one extension, so you can just loop through all files in the directory in your answer, if it's easier):
for j in *.ext; do
...
done
This would be the same with most programming languages: loop and store items somewhere until you have enough of them:
declare -i count=0
declare -a files=()
for f in *.ext; do
files[count]="$f"
(( count += 1 ))
if (( count == 2 )); then
dosomething "${files[0]}"
dosomething "${files[1]}"
count=0
fi
done
If you want to process your files in batches of more than 2 at a time we can also design something a bit more generic (adapt the value of batch):
declare -i batch=3
declare -i count=0
declare -a files=()
for f in *.ext; do
files[count]="$f"
(( count += 1 ))
if (( count == batch )); then
for (( i=0; i<batch; i++ )); do
dosomething "${files[i]}"
done
count=0
fi
done
You can collect your arguments into an array, and then slice that any way you like.
array=( *.ext )
for ((i=0; i<=${#array[#]}; i+=2)); do
dosomething "${array[i]}" "${array[i+1]}"
done

For loop over sequence of large numbers in Bash [duplicate]

This question already has answers here:
Bash command line and input limit
(4 answers)
Closed 4 years ago.
In a Bash script I am using a simple for loop, that looks like:
for i in $(seq 1 1 500); do
echo $i
done
This for loop works fine. However, when I would like to use a sequence of larger numbers (e.g. 10^8 to 10^12), the loop won't seem to start.
for i in $(seq 100000000 1 1000000000000); do
echo $i
done
I cannot imagine, that these numbers are too large to handle. So my question: am I mistaken? Or might there be another problem?
The problem is that $(seq ...) is expanded into a list of words before the loop is executed. So your initial command is something like:
for i in 100000000 100000001 100000002 # all the way up to 1000000000000!
The result is much too long, which is what causes the error.
One possible solution would be to use a different style of loop:
for (( i = 100000000; i <= 1000000000000; i++ )) do
echo "$i"
done
This "C-style" construct uses a termination condition, rather than iterating over a literal list of words.
Portable style, for POSIX shells:
i=100000000
while [ $i -le 1000000000000 ]; do
echo "$i"
i=$(( i + 1 ))
done

Echo a variable with an index at the end in bash script

I am trying to allocate a bunch of temp files in a loop and export them from within the loop. I then want to loop thru again and echo the values.
for (( i=1; i<=5; i++ ))
do
dd if=/dev/zero of=/tmp/mh1_$i.out bs=1024 count=1024 status=none
declare TEMP_FILE_${i}="/tmp/mh1_${i}.out"
export "TEMP_FILE_${i}"
done
If I do a echo $TEMP_FILE_1 it correctly prints /tmp/mh1_1.out
But when I try this in a loop it prints 1, 2, 3, 4 and 5.
for (( i=1; i<=5; i++ ))
do
echo $TEMP_FILE_${i} --> This prints the i value instead of /tmp/mh1_x.out
done
How do I escape the index $i in the echo above to see the real file name ?
I suggest using the mktemp utility with arrays:
# create tmp files
tmp_files=()
for ((i=0;i<5;++i)); do
tmp_files+=("$(mktemp --tmpdir mh1_XXXXXX.out)") || exit 1
done
# process all tmp files
for file in "${tmp_files[#]}"; do
echo "$file"
# do something with the file ...
done
# list all tmp files
printf '%s\n' "${tmp_files[#]}"
# list 2nd tmp file
echo "${tmp_files[1]}"
In order to make your code work, you need to use variable indirection and change your second for loop to:
for ((i=1;i<=5;++i)); do
var=temp_file_$i
echo "${!var}"
done
Don't use uppercase variables as they could clash with environmental or internal shell variables. Also, note that export is needed only when you want to pass the variables to child processes in the environment.
Why not use bash arrays?
export temp_file
loop
temp_file[$i]="/tmp/mh1_${i}.out"
...
endloop
Then loop over the array

Iterate through folders with names like folder0100, folder0101 to folder1100 in shell script

I wrote a small shell script to iterate through folder with names having numbers in them. The script is as below.
#!/bin/bash
for (( i = 100; i < 1000; i++))
do
cp test0$i/test.out executables/test0$i.out
done
here he script traverses through test0100 .. to test0999.
I want to enhance this script to traverse from test0000 to test1100 folders. I am not able to do that.
I am new to shell scripting. Any help is appreciated.
Using seq:
for i in $(seq -w 0 1100); do
cp test$i/test.out executables/test$i.out
done
with the -w flag seq pads generated numbers with leading zeros such that all numbers have equal length.
How about this -
#!/bin/bash
for (( i = 0; i < 1100; i++))
do
cp test$(printf "%04d" $i)/test.out executables/test$(printf "%04d" $i).out
done
With a recent bash
#!/bin/bash
for i in {0000..1100}; do
do
cp test$i/test.out executables/test$i.out
done
Note that brace expansion occurs before variable expansion (see the manual) so if you want to do
start=0000
stop=1100
for i in {$start..$stop}
that won't work. In that case, use seq

Increment with bash

I'm stuck trying to increment a variable in an .xml file. The tag may be in a file 100 times or just twice. I am trying to add a value that will increment the amount several times. I have included some sample code I am working on, but when I run the script it will only place a one and not increment further. Advice would be great on what I'm doing wrong.
for xmlfile in $(find $DIRECTORY -type f -name \*.xml); do
TFILE="/tmp/$directoryname.$$"
FROM='><process>'
TO=' value\=""><process>'
i=0
while [ $i -lt 10 ]; do
i=`expr $i + 1`
FROM='value\=""'
TO='value\="'$i'"'
done
sed "s/$FROM/$TO/g" "$xmlfile" > $TFILE && mv $TFILE "$xmlfile"
done
The while loop was something I just placed to test the code. It will insert the <process> but it will not insert the increment.
My end goal:
<process>value="1"</process>
<process>value="2"</process>
<process>value="3"</process>
<process>value="4"</process>
And so on as long as <process> is present in the file it needs to increment.
I just tested your code and it seems to correctly increment i.
You could try changing your increment syntax from:
i=`expr $i + 1`
To
i=$((i+1))
For a proper increment in bash, use a for loop (C style) :
n=10
for ((i=1; i<=n; i++)) {
printf '<process>value="%d"</process>\n' $i
}
OUTPUT
<process>value="1"</process>
<process>value="2"</process>
<process>value="3"</process>
<process>value="4"</process>
<process>value="5"</process>
<process>value="6"</process>
<process>value="7"</process>
<process>value="8"</process>
<process>value="9"</process>
<process>value="10"</process>
NOTE
expr is a program used in ancient shell code to do math. In Posix shells like bash, use $(( expression )). In bash and ksh93, you can also use (( expression )) or let expression if you don't need to use the result in an expansion.
EDIT
If I misunderstood your needs and you have a file with blank values like this :
<process>value=""</process>
try this :
$ perl -i -pe '$c++; s/<process>value=""/<process>value"$c"/g' file.xml
<process>value"1"</process>
<process>value"2"</process>
<process>value"3"</process>
<process>value"4"</process>
<process>value"5"</process>
<process>value"6"</process>
<process>value"7"</process>
-i switch edit the file for real, so take care.
This is the simplest way to increment a variable in bash:
i=0
((i++))
This also works.
Declaring the variable as an integer.
declare -i i=0
Then later you can increment like so:
i+=1
Use awk:
awk '{gsub( "value=\"\"", "value=" i++ ); print }' i=1 input-file
This will replace the string value="" with value="1", value="2", etc. You can easily change the start value and the increment ( eg ..."value=" i ); i+=5; print )

Resources