Adding up variable within loop - bash

I got a number of files and I need to determine how many of those will fit on a 4Tb drive by just knowing first filename. Name pattern is 001j00_rf_geo_????$seqn with sequential 3-digit number at the end. Say I start with 001j00_rf_geo_????100.
block=4000000000000
shopt -s dotglob
seqn="100"
size=`stat -c%s 001j00_rf_geo_????$seqn`
for (( i=$size ;i < $block ; seqn++ ))
do
((size+=$(stat -c%s 001j00_rf_geo_????$seqn)))
done
echo $size
I am pretty sure the summing up part in for loop is wrong. I just could get my head around how to get a total size of files having the the loop part in code.

Look at your for loop, you are not using 'i' at all -- it is unneeded. If you want to use a C-style for loop, you can simply omit the initializer:
for ((; size < block; seqn++))
do
or use a while loop instead
while ((size < block))
do
...
((seqn++))
done
Of course you can just move your initialization to the for loop as well and get rid of the one above
for ((seqn = 100; size < block; seqn++))
do
Give either a try and let me know if you have further questions.

Related

How to avoid line insert to the file if the line is already present in the file?

How should the check be made so that there are no line duplicates in the file
open ( FILE, ">newfile");
for( $a = 1; $a < 20; $a = $a + 1 ) {
my $random_number = 1+ int rand(10);;
# check to avoid inserting the line if the line is already present in the file
print FILE "Random number is $random_number \n";
}
close(FILE);
!$seen{$_}++ is a common idiom for identifying duplicates.
my %seen;
for (1..19) {
my $random_number = 1+ int rand(10);
say "Random number is $random_number" if !$seen{$random_number}++;
}
But that doesn't guarantee that you will get all numbers from 1 to 10 in random order. If that's what you are trying to achieve, the following is a far better solution:
use List::Util qw( shuffle );
say "Random number is $_" for shuffle 1..10;
It seems like what you are asking is how to randomize the order of the numbers 1 to 20. I.e. no duplicates, random order. That can be easily done with a Schwartzian transform. For example:
perl -le'print for map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, rand()] } 1..20'
6
7
16
14
5
20
3
13
19
17
4
8
15
10
9
11
18
1
2
12
In this case, reading from the end and backwards, we create a list of numbers 1 .. 20, we feed that into a map statement which turns each number into an array-ref, containing the number, and a random number. Then we feed that list of array refs to a sort, where we sort numerically on the second argument in the array ref: the random number (hence creating a random order). Then we transform the array ref back into a simple number with another map statement. Finally we print the list using a for loop.
So in your case, the code would look something like:
print "Random number is: $_\n" for # print each number
map { $_>[0] } # restore to a number
sort { $a->[1] <=> $b->[1] } # sort the list on the random number
map { [ $_, rand() ] } # create array ref with random number as index
1 .. 20; # create list of numbers to randomize order of
Then you can use the program like below to redirect output to a file:
$ perl numbers.pl > newfile.txt
Enter each line into a hash as well, what makes it easy and efficient to later check for it
use warnings;
use strict;
use feature 'say';
my $filename = shift or die "Usage: $0 filename\n";
open my $fh, '>', $filename or die "Can't open $filename: $!";
my %existing_lines;
for my $i (1..19)
{
my $random_number = 1 + int rand(10);
# Check to avoid inserting the line if it is already in the file
if (not exists $existing_lines{$random_number}) {
say $fh "Random number is $random_number";
$existing_lines{$random_number} = 1;
}
}
close $fh;
This assumes that the intent in the question is to not repeat that number (symbolizing content to be stored without repetition).
But if it is indeed the whole line (sentence) to be avoided, where that random number is used merely to make each line different, then use the whole line for the key
for my $i (1..19)
{
my $random_number = 1 + int rand(10);
my $line = "Random number is $random_number";
# Check to avoid inserting the line if it is already in the file
if (not exists $existing_lines{$line}) {
say $fh $line;
$existing_lines{$line} = 1;
}
}
Notes and literature
Lexical filehandles (my $fh) are much better than globs (FILE), and the three-argument open is better. See the quide perlopentut and reference open
Always check the open call (or die... above). It can and does fail -- quietly. In that check always print the error for which it failed, $!
The C-style for loop is very rarely needed while the usual foreach (with synonym for) is much nicer to use; see it in perlsyn. The .. is the range operator
Always declare variables with my, and enforce that with strict pragma; always use warnings
If the filehandle refers to pipe-open (not the case here) always check its close
See perlintro for a general overview and for hashes; for more about Perl's data types see perldata. Keep in mind for later the notion of complex data structures, perldsc
return false will do the trick.
Because you cannot generate 20 distinct numbers in the range [1, 10].

Problem when renaming a files in a foor loop

I want to rename different files:
for ((i = 2; i < 10; i++)); mv /Users/neurolab/Desktop/Stephan/Oncology/dummy/sub-$i/ses-1/anat/Sub* /Users/neurolab/Desktop/Stephan/Oncology/dummy/sub-$i/ses-1/anat/sub-$i_ses-1_T1w.nii.gz;
But by using $i_ it does not recognize the "$i_ses" part and skips "$i_ses". There is no problem if I use a hyphen such as "$i-ses".
How can I avoid this problem?
Best

How to add 5 to every number of an array with Bash?

I am trying to create an array of user inputs and then add to each element in the array:
read number
for i in 1 2 3
read array[$i]
done
let position=0
for i in "${array[#]}"
do
let array[position]+=($i+$number)
let "position++"
done
for (( i=0; $i<3; i=$1+1 ))
do
echo ${array[$1]}
So, the user will enter "5" for number and then three more numbers for the array(90, 80, 70). The results should be array(95, 85, 75), but the output I'm getting is array(95, 175, 155).
A saner way to write this would be:
read -r number
read -r -a array
for idx in "${!array[#]}"; do
(( array[$idx] += number ))
done
printf '%s\n' "${array[#]}"
Instead of assuming that the indexes start at 0 (which they didn't, originally, because you were explicitly assigning to positions 1, 2 and 3), using "${!array[#]}" finds the actual indexes, and thus works correctly even with sparse arrays or ones not indexed starting at position 0.
Instead of duplicating $i (aka the values from your array) on the right-hand side of +=, it only adds the number itself.
Instead of iterating over indexes (again) to print the values, it just asks the array to dump all its values in index order with "${array[#]}".
See this in operation at https://ideone.com/WTLJSu
There is a behavioral difference insofar as it expects all the array values to be passed on a single line of input. If you don't want that, see the version at https://ideone.com/3OQtt3 instead.

'for' loop with dynamic array size

I have an array that gets elements added to it when it calls the function findVar. The problem seems to be on the for loop that does not update the number of elements once started running.
When I do echo at the end of the for loop I get the correct number of elements and the last element but it seems not to be checking on the for conditions once started.
for var in "${tempV[#]}"
do
num_words=${#tempV[#]}
let i=i+1
if ! [ $i -gt $num_words ]
then
findVar $objBKP $var
fi
done
Your attempt is looping over every original element in the array and then ensuring that you haven't looped more times than that and calling your function.
That doesn't work because the original expansion of tempV happens once and so the added entries are never seen. But that also doesn't make sense since, by definition, if you are looping over the elements of the array you can't loop more times then there are elements in the array.
What you want to be doing (assuming a non-sparse, integer-indexed array that is only appended to) is looping numerically and checking that you haven't exceeded the array size as the loop condition.
Something like this (untested):
i=0
while [ "$i" -lt "${#tempV[#]}" ]; do
var=${tempV[i]}
findVar "$objBKP" "$var"
i=$((i + 1))
done
You're not using $i for anything other than an iteration counter. It's completely unnecessary in your posted example. Instead, just iterate over the contents of the variable expansion. For example:
for var in "${tempV[#]}"; do
findVar "$objBKP" "$var"
done

Calculating IDs for model runs

I'm running some array jobs on a PBS system (although hopefully no knowledge of PBS systems is needed to answer my question!). I've got 24 runs, but I want to split them up into 5 sub-jobs each, so I need to run my script 120 times.
After giving the PBS option of -t 1-120, I can get the current job-array ID using $PBS_ARRAYID. However, I want to create some output files. It would be best if these output files used the ID that it would have had if there were only 24 runs, together with a sub-run identifier (e.g. output-1a.txt, output-1b.txt ... output-1e.txt, output-2a.txt).
What I therefore need is a way of calculating a way to get the ID (in the range 1-24) together with the sub-run identifier (presumably in a set of if-statements), which can be used in a shell-script. Unfortunately, neither my maths nor my Unix knowledge is quite good enough to figure this out. I assume that I'll need something to do with the quotient/remainder based on the current $PBS_ARRAYID relative to either 120 or 24, but that's as far as I've got...
You just need a little modular division. A quick simulation of this in Ruby would be:
p = Array.new;
(1..120).each {|i| p[i] = "Run #{1+(i/5)}-#{((i%5)+96).chr}" }
What this says is simply that the run should start at 1 and increment after each new section of five, and that the trailing sub-run should be the ascii character represented by 96 plus the position of the sub-run (eg, 97 == 'a').
Here it is in Bash:
#!/bin/bash
chr() {
local tmp
[ ${1} -lt 256 ] || return 1
printf -v tmp '%03o' "$1"
printf \\"$tmp"
}
for ((i = 0; i < ${#ARP[*]}; i++))
do
charcode=$((($i % 5)+97))
charachter=$(chr "$charcode")
echo "Filename: output-$((($i/5)+1))$charachter"
done
I just used ARP as the name of the array, but you can obviously substitute that. Good luck!

Resources