How to cut and assign the string to a dynamic array inside the for loop - bash

This is what i have done to perform this function but I am not getting what i want.
#!/bin/sh
DIRECTIONPART1=4-7-9
for (( i=1; i<=3; i++ ))
do
x=`echo $DIRECTIONPART1| awk -F'-' '{print $i}'`
myarray[$i]=$x
done
for (( c=1; c<=3; c++ ))
do
echo ${myarray[$c]}
done
Problem we realised at this step
x=`echo $DIRECTIONPART1| awk -F'-' '{print $i}'`
Please help me in getting the result
This is what i get :
4-7-9
4-7-9
4-7-9
But I want this:
4
7
9

you are right with line of problem. The problem is that you cant use $i as variable in print. I have tried little workaround which worked for me:
x=`echo $DIRECTIONPART1| awk -F '-' -v var=$i '{print $var }'`
in all it looks like:
#!/bin/sh
DIRECTIONPART1=4-7-9
for (( i=1; i<=3; i++ ))
do
x=`echo $DIRECTIONPART1| awk -F '-' -v var=$i '{print $var }'`
myarray[$i]=$x
done
for (( c=1; c<=3; c++ ))
do
echo ${myarray[$c]}
done
with expected output:
# sh test.sh
4
7
9
#

The simplest portable way to get the desired output is to use $IFS (in a subshell):
#!/bin/sh
DIRECTIONPART1=4-7-9
(IFS=- && echo $DIRECTIONPART1)
The shell array would not work portably, as POSIX, ksh, and bash do not
agree on arrays. POSIX doesn't have any; ksh and bash use different syntax.
If you really want an array, I would suggest to do the entire thing in awk:
#!/bin/sh
DIRECTIONPART1=4-7-9
awk -v v=${DIRECTIONPART1} 'BEGIN {
n=split(v,a,"-")
for (i=1;i<=n;i++) {
print a[i]
}
}'
This will produce one line for each value in the string:
4
7
9
And if you want bash arrays, drop the #!/bin/sh, and do something like this:
#!/bin/bash
DIRECTIONPART1=4-7-9
A=( $(IFS=- && echo $DIRECTIONPART1) )
for ((i=0;i<=${#A[#]};i++))
do
echo ${A[i]}
done

Calling awk multiple times, or even once, is not the right thing to do. Use the bash built-in read to populate the array.
# Note that the quotes here are only necessary to
# work around a bug that was fixed in bash 4.3. It
# doesn't hurt to use them in any version, though.
$ IFS=- read -a myarray <<< "$DIRECTIONPART_1"
$ printf '%s\n' "${myarray[#]}"
4
7
9

[akshay#localhost tmp]$ bash test.sh
#!/usr/bin/env bash
DIRECTIONPART1=4-7-9
# Create array
IFS='-' read -a array <<< "$DIRECTIONPART1"
#To access an individual element:
echo "${array[0]}"
#To iterate over the elements:
for element in "${array[#]}"
do
echo "$element"
done
#To get both the index and the value:
for index in "${!array[#]}"
do
echo "$index ${array[index]}"
done
Output
[akshay#localhost tmp]$ bash test.sh
4
4
7
9
0 4
1 7
2 9
OR
[akshay#localhost tmp]$ cat test1.sh
#!/usr/bin/env bash
DIRECTIONPART1=4-7-9
array=(${DIRECTIONPART1//-/ })
for index in "${!array[#]}"
do
echo "$index ${array[index]}"
done
Output
[akshay#localhost tmp]$ bash test1.sh
0 4
1 7
2 9

Related

Awk split use array later in bash

If I have this awk command...
echo $line | awk '{split($0,array,"|")}'
...how can I use array later in the bash program? If I try to print out information from the array later it's just empty.
You can't access awk variables outside of awk, but you can do the same using bash arrays directly
$ IFS='|' read -r -a array <<< "a|b|c"; echo ${array[1]}
b
You can use awk array contents outside of awk if you print it:
IFS=$'\n' read -d '' -r -a line < <(echo 'o|p|s' | awk '{split($0,array,"|"); for (i in array) print array[i]}')
declare -p line
# OUTPUT: declare -a line='([0]="o" [1]="p" [2]="s")'
An other solution in bash if I presume that fields haven't got space then only 1 assignment.
ln="apple|lemon|orange|strawberry" # eg
v=(${ln//|/ }) # substitute | with " " and vectorising with ()
# Now we are ready
# checking it:
for((i=0;i<${#v[#]};i++));do echo ${v[$i]}; done
apple
lemon
orange
strawberry
#or:
for i in ${v[#]}; do echo $i; done
apple
lemon
orange
strawberry
If we have some space but no underline "_" we need 3 steps.
ln="api ple|l em on|ora nge|s traw berry"
echo "$ln"
api ple|l em on|ora nge|s traw berry
ln=${ln// /_} # 1st
echo "$ln"
api_ple|l_em__on|ora__nge|s_traw___berry
v=(${ln//|/ }) # 2nd
for i in ${v[#]}; do echo $i; done
api_ple
l_em__on
ora__nge
s_traw___berry
for((i=0;i<${#v[#]};i++));do v[$i]="${v[i]//_/ }"; done # 3rd
for((i=0;i<${#v[#]};i++));do echo "${v[$i]}"; done
api ple
l em on
ora nge
s traw berry

How to get the 'variable' line from file? [duplicate]

This question already has answers here:
Bash tool to get nth line from a file
(22 answers)
Closed 7 years ago.
This is my script. It print every row in the file with the number of row.
Next i want to read which row user choosed and save it to some variable.
I=1
for ROW in $(cat file.txt)
do
echo "$I $ROW"
I=`expr $I + 1`
done
read var
awk 'FNR = $var {print}' file.txt
Then i want to to print / save the chosen row into the file.
How can I do this ?
when i echo $var it shows me properly the number. But when i'm trying to use this variable in awk, it print every line.
How to read the 'var' line from file?
And moreover, how to save this line in other variable?
Example file.txt
1 line1
2 line2
3 line3
4 line4
when i tap 3 i want to read third line from file.
Try this:
cat -n file.txt; read var; line="$(sed -n ${var}p file)"; echo "$line"
With more focus on Dryingsoussage's version:
#!/bin/bash
file="file.txt"
declare -i counter=0 # set integer attribute
var=0
while read -r line; do
counter=counter+1
printf "%d %s\n" "$counter" "$line"
done < "$file"
# check for number and greater-than 0 and less-than-or-equal $counter
until [[ $var =~ ^[0-9]+$ ]] && [[ $var -gt 0 ]] && [[ $var -le $counter ]]; do
read -p "Enter line number:" var
done
awk -v var="$var" 'FNR==var {print}' "$file"
You cannot use $varname inside ' ' they will not be resolved.
look at this other post it should help you:
How to use shell variables in an awk script
cat -n file.txt
read var
row="$(awk -v tgt="$var" 'NR==tgt{print;exit}' file.txt)"
First: You cannot use $var in a single quotes, as echo '$var' would be plain $var, no its value.
Second: You used = (assignment) operator instead of == (equality) operator.
Third: You don't have to write { print } if you want the line to be printed. You can write nothing instead.
Fourth: As was explained in the deleted comment below - do not allow bash expanding the variables in the awk script code, as it can lead to code injection.
So conclusion is:
awk -v var="$var" 'FNR == var' file.txt
should do what you want.

Using Array With Awk [duplicate]

This question already has answers here:
How do I use shell variables in an awk script?
(7 answers)
Closed 7 years ago.
I am using an array of values, and I want to look for those values using awk and output to file. In the awk line if I replace the first "$i" with the numbers themselves, the script works, but when I try to use the variable "$i" the script no longer works.
declare -a arr=("5073770" "7577539")
for i in "${arr[#]}"
do
echo "$i"
awk -F'[;\t]' '$2 ~ "$i"{sub(/DP=/,"",$15); print $15}' $INPUT >> "$i"
done
The file I'm looking at contains many lines like the following:
chr12 3356475 . C A 76.508 . AB=0;ABP=0;AC=2;AF=1;AN=2;AO=3;CIGAR=1X;DP=3;DPB=3;DPRA=0;EPP=9.52472;EPPR=0;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=0;NS=1;NUMALT=1;ODDS=8.76405;PAIRED=0;PAIREDR=0;PAO=0;PQA=0;PQR=0;PRO=0;QA=111;QR=0;RO=0;RPP=9.52472;RPPR=0;RUN=1;SAF=3;SAP=9.52472;SAR=0;SRF=0;SRP=0;SRR=0;TYPE=snp GT:DP:RO:QR:AO:QA:GL 1/1:3:0:0:3:111:-10,-0.90309,0
Pass the value $i to awk using -v:
awk -F'[;\t]' -v var="$i" '$2 ~ var{sub(/DP=/,"",$15); print $15}' $INPUT >> "$i"
awk will have no idea what the value of the shell's $i is unless you explicitly pass it into awk as a variable
awk -F'[;\t]' -v "VAR=${i}" '$2 ~ VAR {....
I expect the result you see is because 'i' is undefined and treated as zero
which makes your test '$2 ~ $0 {...
You can avoid awk and do this in BASH itself:
arr=("5073770" "7577539" "3356475")
for i in "${arr[#]}"; do
while IFS='['$'\t'';]' read -ra arr; do
[[ ${arr[1]} == *$i* ]] && { s="${arr[14]}"; echo "${s#DP=}"; }
done < "$INPUT"
done

Print bash arguments in reverse order

I have to write a script, which will take all arguments and print them in reverse.
I've made a solution, but find it very bad. Do you have a smarter idea?
#!/bin/sh
> tekst.txt
for i in $*
do
echo $i | cat - tekst.txt > temp && mv temp tekst.txt
done
cat tekst.txt
Could do this
for (( i=$#;i>0;i-- ));do
echo "${!i}"
done
This uses the below
c style for loop
Parameter indirect expansion
(${!i}towards the bottom of the page)
And $# which is the number of arguments to the script
you can use this one liner:
echo $# | tr ' ' '\n' | tac | tr '\n' ' '
bash:
#!/bin/bash
for i in "$#"; do
echo "$i"
done | tac
call this script like:
./reverse 1 2 3 4
it will print:
4
3
2
1
Portably and POSIXly, without arrays and working with spaces and newlines:
Reverse the positional parameters:
flag=''; c=1; for a in "$#"; do set -- "$a" ${flag-"$#"}; unset flag; done
Print them:
printf '<%s>' "$#"; echo
Reversing a simple string, by spaces
Simply:
#!/bin/sh
o=
for i;do
o="$i $o"
done
echo "$o"
will work as
./rev.sh 1 2 3 4
4 3 2 1
Or
./rev.sh world! Hello
Hello world!
If you need to output one line by argument
Just replace echo by printf "%s\n":
#!/bin/sh
o=
for i;do
o="$i $o"
done
printf "%s\n" $o
Reversing an array of strings
If your argument could contain spaces, you could use bash arrays:
#!/bin/bash
declare -a o=()
for i;do
o=("$i" "${o[#]}")
done
printf "%s\n" "${o[#]}"
Sample:
./rev.sh "Hello world" print will this
this
will
print
Hello world
As a function (If you're ok to play with eval.
But eval is evil!!
rev() { eval "set --" $(seq -f '"${%g}"' $# -1 1);printf '%s\n' "$#";}
Then
rev Hello\ world print will this
this
will
print
Hello world

shell: addition from lines in an string

Let's say I have a variable like this:
list='1
1
1.4
1
1
1'
Now I want to add the value from each line to the value from the line before. Like this:
result='1
2
3.4
4.4
5.4'
It must work with floating numbers too, so I guess awk is the best tool for it.
I was stating with a while loop:
while read line; do
add=$( awk 'BEGIN{ print "'"$x"'" + "'"$line"'" }' )
done <<< "$list"
But this doesn't work because I don't know how to save the value from the line before.
This would be my bash snippet:
prev=0
for val in $list; do
val=$(echo "$val + $prev"|bc)
prev=$val
result="${result}${val}\n"
done
echo -e "$result"
I don't know whether using zsh (which has floating point support) is an option, but if it is, you can simply do:
s=0;for i in $list; do s=$(($s+$i)); echo $s; done
Using awk you can do:
awk 'NR==1{print; s=$1; next} {s+=$1; print s}' file
1
2
3.4
4.4
5.4
6.4
Here is a way of doing it using bc:
#!/bin/bash
list='1
1
1.4
1
1
1'
results=( )
total="0.0"
index=0
for line in $list; do
total=$(bc -l <<< "$line + $total")
echo $total
results[$index]=$total
let index++
done
echo ${results[#]}
Also:
total="0.0"; while read v; do total=$(bc -l <<< "$v + $total"); echo $total; done <<< "$list"

Resources