If statement for two different arrays in bash - arrays

I have two arrays, which are arrAlpha[] and arrPT[]. Array arrAlpha contains the Alphabets and array arrPT[] contains some of the plain letters.following is the code that i wrote in bash shell script to compare elements of both arrays and to store the position elements of arrPT[] in arrAlpha[] to array arrT[]. But when i run i feel like something is wrong in if statement to print out the elements in arrT[]. can anyone help me please?
#!/bin/bash
arrAlpha=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
arrPT=(E K N R S W )
lenPT=${#arrPT}
declare -A arrT
q=0
for((i=0; i<lenPT; i++)) do
for((j=i; j<26; j++)) do
if [ ${arrPT[$i]} = ${arrAlpha[$j]} ]; then
arrT[$q]=$j % 26;
((++q));
fi
done
done
echo ${arrAlpha[#]}
echo ${arrPT[#]}
echo ${arrT[#]}
the expected output is to change elements arrPT to number 0 to 25.
arrAlpha=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
arrPT =(E K N R S W)
arrT =(4 10 13 17 18 22)

Here's a fixed version of your script - there are some style changes that I prefer
arrAlpha=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
arrPT=(E K N R S W )
# array length needs index *
lenPT=${#arrPT[*]}
# seems arrT can be simple indexed array
declare -a arrT
q=0
for((i=0; i<lenPT; ++i)); do
for((j=0; j<26; ++j)); do
if [[ ${arrPT[i]} == ${arrAlpha[j]} ]]; then
# arithmetic inside $(())
arrT[q++]=$((j % 26))
fi
done
done
echo ${arrAlpha[#]}
echo ${arrPT[#]}
echo ${arrT[#]}

The main bug is with this line:
lenPT=${#arrPT}
which should be written like:
lenPT=${#arrPT[#]}
But, in fact, there is no need (even if it is not wrong to use it) for such variable, change this line:
for((i=0; i<lenPT; i++)) do
to:
for((i=0; i<${#arrPT[#]}; i++)) do
Some other issues:
There is no need to mod 26 the value of $j, it will never reach 26.
The array arrT seems to be an indexed array: -a no need for -A.
There is no need for variable q if each result will be in the same position as the index i
Please quote your expansions.
Use printf (more reliable) instead of echo.
The script with all the above done is:
#!/bin/bash
arrAlpha=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
arrPT=(E K N R S W )
declare -a arrT
for((i=0; i<${#arrPT[#]}; i++)) do
for((j=i; j<${#arrAlpha[#]}; j++)) do
[[ ${arrPT[i]} == "${arrAlpha[j]}" ]] && let arrT[i]=j
done
done
printf '%s ' "${arrAlpha[#]}" ; echo
printf '%3s ' "${arrPT[#]}" ; echo
printf '%3s ' "${arrT[#]}" ; echo
Of course, the second loop could be removed by cutting that string at the character:
#!/bin/bash
Alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZ
arrPT=(E K N R S W )
declare -a arrT
for((i=0; i<${#arrPT[#]}; i++)) do
arrT[i]=${Alpha%"${arrPT[i]}"*} # cut the string at the character.
arrT[i]=${#arrT[i]} # Use the len of the cut string.
done
printf '%s ' "$Alpha" ; echo
printf '%3s ' "${arrPT[#]}" ; echo
printf '%3s ' "${arrT[#]}" ; echo

Related

Batch insert heading/newline to ASCII file if value of column changes

I have a file similar to this:
A B C
D E C
F G C
A B X
F G X
A B Q
D E Q
Thats what I am looking for
> C
A B C
D E C
F G C
> X
A B X
F G X
> Q
A B Q
D E Q
So far I have a kind of complicated work-around.
Using AWK to add a empty line.
awk -v i=3 "NR>0 && $i!=p { print "A" }{ p=$i } 1" file.txt
I dont manage to add a ">" directly with awk since its a newline value. Instead of the "A", awk is outputting a empty line. Not really sure why..
Using then
sed -e "s/^$/>/" file.txt
I manage to insert a ">" to the empty line but the heading behind is still missing.
sed is for doing s/old/new, that is all. What you are attempting to do is not just s/old/new so you shouldn't be considering using sed, just use awk:
$ awk '$3!=p{print ">", $3; p=$3} 1' file
> C
A B C
D E C
F G C
> X
A B X
F G X
> Q
A B Q
D E Q
awk solution. Assuming that your input file is sorted:
awk '!a[$NF]++{ print ">",$NF }1' file
The output:
> C
A B C
D E C
F G C
> X
A B X
F G X
> Q
A B Q
D E Q
Could you please try following also and let me know if this helps you.
awk 'NR==1{print ">",$3 RS $0;prev=$3;next} prev!=$3{print ">",$3};1; {prev=$3}' Input_file
Output will be as follows.
> C
A B C
D E C
F G C
> X
A B X
F G X
> Q
A B Q
D E Q

Use Perl to only print only if the value of column A appears with every different value of Column B

So in Perl how can I go through a sample file like so:
1 D Z
1 E F
1 G L
2 D I
2 E L
3 D P
3 G L
So here I want to be able to print out only the values that have a value in the first column that appears with every different value of the second column.
The output would look like this:
1 D Z
1 E F
1 G L
cat test
1 D Z
1 E F
1 G L
2 D I
2 E L
3 D P
3 G L
perl -a -lne 'unless ( $h{ $F[1] } ) { print }; $h{ $F[1] } = 1; ' test
1 D Z
1 E F
1 G L
Okay this isn't as easy as it seems. I've read the file into memory so that I can take three passes over it
Count the number of different values in column 2
Record each combination of values in column 1 and column 2
Print those lines in the file whose first column has as many occurrences as there are different values of column 2
This could be improved with more information about the input file, but it works fine as it is and I see no reason to optimise it
use strict;
use warnings 'all';
use List::MoreUtils qw/ uniq /;
my #lines = <>;
my #col2 = uniq map { (split)[1] } #lines;
my %data;
for ( #lines ) {
my ($c1, $c2) = split;
$data{$c1}{$c2} = 1;
}
for ( #lines ) {
my ($c1) = split;
print if keys %{ $data{$c1} } == #col2;
}
output
1 D Z
1 E F
1 G L

line 39: [: ==: unary operator expected

I am trying to generate a password with certain requirements.
When I enter the while loop to generate a random character from the array it is fine until I add a count for my index "$i"
With the following code:
#!/bin/bash
#SET ARRAY VALUES
all=( 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z )
echo
#SET COUNT VALUES TO 0
numc=0
lowc=0
upc=0
i=0
while true;
do
#GENERATE PASSWORD
phrase[$i]=${all[$RANDOM%62]}
#CHECK IF PASSWORD MEETS REQUIREMENTS
for ((n=0; n<10; n++))
do
if [ ${phrase[$i]} == ${all[$n]} ]
then
echo num ${all[$n]}
let "numc++"
let "i++"
fi
done
I get the error "line 39: [: ==: unary operator expected"
but if I remove the let "i++" line then there is no error. But I need to increase my index in order to exit the loop and check the minimum length of the password
If ${phrase[$i]} (by the way you don't need $ in [$i] context ${phrase[i]} works too) is ever the empty string that if test will become [ == value-of-all-n ] which isn't a valid test.
Either quote the variables (which is almost always the right thing to do) or prevent that from ever being the empty string. (Was that i++ supposed to happen outside the inner loop?)

How would I convert an argument into a string in bash

When I run the script I enter a single argument. I want to store the argument into a variable and access it as a string. So if I enter $ ./script foo I should be able to access f, o, and o. So echo $pass[0] should display f
but what I am finding is that $pass is storing the argument as one piece
so echo $pass[0] displays foo
How do I access the different positions in the string?
#!/bin/bash
all=( 0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z )
pass=$1
max=${#pass}
for (( i=0; i<max; i++ ))
do
for (( n=0; n<10; n++ ))
do
if [ "${pass[$i]}" == ${all[$n]} ]
then
echo true
else
echo false i:$i n:$n pass:${pass[$i]} all:${all[$n]}
fi
done
done
To spell out Etan's comment in the context of this question:
set -- "my password"
chars=()
for ((i=0; i<${#1}; i++)); do chars+=("${1:i:1}"); done
declare -p chars
outputs
declare -a chars='([0]="m" [1]="y" [2]=" " [3]="p" [4]="a" [5]="s" [6]="s" [7]="w" [8]="o" [9]="r" [10]="d")'

Array Bash printing elements in a loop of multiple arrays

I have multiple arrays ( I limit it to 3 ) & first time using arrays
The length of the arrays are the same. They correspond to the same records
So array a, b and c values are listed below:
array a = 1 2 3 4 5
array b = a b c d e
array c = v w x y z
I need to print then content so the output is like this on each line
1 a v
2 b w
3 c x
4 d y
5 e z
Can you help?
Thanks
Here's a more bash-ful version (if you will):
#!/usr/bin/env bash
# initialize arrays
a=(1 2 3 4 5)
b=(a b c d e)
c=(v w x y z)
# count elements (assuming all arrays are the same size)
numElems=${#a[#]}
# loop over all elements
for (( i = 0; i < numElems; i++ )); do
# -e ensures that escape sequences such as \t are recognized
echo -e "${a[i]}\t${b[i]}\t${c[i]}"
done
This is how I worked it out, hopefully there is a better way. There are 3 arrays sample listed above, Each array has a list of values in it. Since they are of equal length. This is what can be done . The $'\t' puts a tab in between.
s=${#a[#]}
counter=0
echo $counter
while [[ $counter -lt $s ]];
do
echo ${a[$counter]} $'\t' ${b[$counter]} $'\t' ${c[$counter]}
counter=$(( $counter + 1 ))
done

Resources