awk - print last field $NF of array [duplicate] - arrays

This question already has answers here:
How can I get the length of an array in awk?
(9 answers)
Closed 6 years ago.
is any chance how to print - lets say example:
A/b/c/d/e/f A B C D E F G|H|I|J|
I would like to split fields to array and print:
awk -v OFS="\t" '{split($1,a,"/"); split($3,b,"|"); print a[LAST_FIELD??],$1,$2,$3,b[1],b[2]}' input
the result should be:
f A B C g H I
I am not sure how to define "LAST_FIELD" in my array. Thank you for any help.

split returns the number of elements, so you can use that:
n = split($1,a,"/")
then:
print a[n]

You can use this awk:
awk -v OFS="\t" '{lena=split($1,a,"/"); split($NF,b,"|");
print a[lena],$2,$3,$4,b[1],b[2],b[3]}' file
f A B C G H I

Related

Converting bash array into string with - in between [duplicate]

This question already has answers here:
How can I join elements of an array in Bash?
(34 answers)
Closed 4 months ago.
How do I convert an array into a string with a dash '-' in between in bash. For eg, I have this array
arr=(a b c d e)
I want to convert it into a string "a-b-c-d".
I figured out this "a-b-c-d-e," but there is an unwanted dash at the end. Is there an efficient way of doing this?
Thanks
This is where the "${arr[*]}" expansion form is useful (note the double quotes and the * index). This joins the array elements using the first character of the IFS variable
arr=(a b c d e)
joined=$(IFS='-'; echo "${arr[*]}")
declare -p joined
# => declare -- joined="a-b-c-d-e"
But if you want all-but-the-last elements, you'll combine this with the ${var:offset:length} expansion
joined=$(IFS='-'; echo "${arr[*]:0: ${#arr[#]} - 1}")
declare -p joined
# => declare -- joined="a-b-c-d"
This one's a little tricker. The offset and length parts of that expansion are arithmetic expressions. I'm calculating the length as "the number of elements in the array minus one".
Note how I'm defining IFS inside the command substitution parentheses: this is overriding the variable in the subshell of the command substitution, so it won't affect the IFS variable in your current shell.
Using awk if you want to remove the last entry
$ awk -vOFS=- '{NF--;$1=$1}1' <<<${arr[#]}
a-b-c-d
or the complete array
$ awk -vOFS=- '{$1=$1}1' <<<${arr[#]}
a-b-c-d-e

Create an array in bash from given start and end point [duplicate]

This question already has answers here:
How do I iterate over a range of numbers defined by variables in Bash?
(20 answers)
Closed 4 years ago.
I have this code that works:
$ array=( {100..300..100} )
$ for k in ${array[#]} ; do echo $k ; done
100
200
300
I want to parametrize the start and end points (and also the increment, because why not?)
I tried this:
$ low=100
$ high=300
$ incr=100
$ array=( {$low..$high..$incr} )
But it didn't work:
$ for k in ${array[#]} ; do echo $k ; done
{100..300..100}
What am I doing wrong?
From bash manual:
A sequence expression takes the form {x..y[..incr]}, where x and y are either integers or single characters, and incr, an optional increment, is an integer.
So, parameter and variable expansion are not performed on x, y and incr. You should use seq:
arr=( $(seq $low $incr $high) )

Bash: How do I combine two arrays into a third array? [duplicate]

This question already has answers here:
Merging two Bash arrays into Cartesian key:value pairs
(5 answers)
Closed 6 years ago.
I have two arrays: array1 and array2:
array1=( a b c )
array2=( 1 2 3 )
How do I make a third array, array3:
array3=( a b c 1 2 3 )
This question is different from Combine arrays in the beginning of a for-loop (Bash) because this deals exclusively with combining arrays, not whether such a statement is legal within a for loop. Thus, it is more general.
From The Advanced Bash Scripting Guide example 27-10, with a correction:
declare -a array1=( zero1 one1 two1 )
declare -a array2=( [0]=zero2 [2]=two2 [3]=three2 )
dest=( "${array1[#]}" "${array2[#]}" )
Thus, for my case, it's:
array3=( "${array1[#]}" "${array2[#]}" )

Access a bash array in awk loop

I have a bash array like
myarray = (1 2 3 4 5 ... n)
Also I am reading a file with an input of only one line for example:
1 2 3 4 5 ... n
I am reading it line by line into an array and printing it with:
awk 'BEGIN{FS=OFS="\t"}
NR>=1{for (i=1;i<=NF;i++) a[i]+=$i}
END{for (i=1;i<NF;i++) print OFS a[i]}' myfile.txt
myarray has the same size as a. Now myarray starts with the index 0 and a with index 1. My main problem though is how I can pass the bash array to my awk expression so that I can use it inside the print loop with the corresponding elements. So what I tried was this:
awk -v array="${myarray[*]}"
'BEGIN{FS=OFS="\t"}
NR>=1{for (i=1;i<=NF;i++) a[i]+=$i}
END{for (i=1;i<NF;i++) print OFS a[i] OFS array[i-1]}' myfile.txt
This doens't work though. I don't get any output for myarray. My desired output in this example would be:
1 1
2 2
3 3
4 4
5 5
...
n n
To my understanding, you just need to feed awk with the bash array in a correct way. That is, by using split():
awk -v bash_array="${myarray[*]}"
'BEGIN{split(bash_array,array); FS=OFS="\t"}
NR>=1{for (i=1;i<=NF;i++) a[i]+=$i}
END{for (i=1;i<NF;i++) print a[i], array[i]}' file
Since the array array[] is now in awk, you don't have to care about the indices, so you can call them normally, without worrying about the ones in bash starting from 0.
Note also that print a,b is the same (and cleaner) as print a OFS b, since you already defined OFS in the BEGIN block.

Associative array in bash to store all lines start with X

I have a file with lines which I am taking input by $1:
X B C D E
X G H I J
X L M N
Y G
Z B
Y L
In each line starts with X, the key is the 2nd element and the values are the rest elements.
I am reading the file line by lines creating associate array for each.
while read LINE
do
INPUT=$(echo $LINE |awk '{print $1}')
if [[ "$INPUT" = X ]]
then
key_name=$(echo $LINE | awk '{print $2}')
declare -A dependencies
value_names=($(echo $LINE|awk '{$1=$2=""; print $0}'))
dependencies[key_name]=value_names
echo -e "\nvalues of $key_name are ${key_name[*]}\n"
sleep 1
fi
done < $1
So I am losing the value for each line reading.
But I need to store all the lines with X in the associate arays,
because I need to search for the key later for the later lines, lets say: a line start with Y, and it has G, so here I need to find the valuess from the associated arrays
with key G.
Can anyone suggest some idea how to store all lines start with X in a single associative array by reading line line the file? Or any better approach?
Here from the sample input given, the output will be in 3 lines:
H I J
C D E
M N
Here X,Y,X are recognizing the lines, what to do with the next characters. If X store the rest in KEY-PAIR or if Y or Z extract the values from associative arrays.
Using GNU awk for gensub():
$ gawk '{ if (/^X/) a[$2] = gensub(/(\S+\s+){2}/,"",""); else print a[$2] }' file
H I J
C D E
M N
The above implicitly loops through every line in the input file and when it finds a line that starts with X (/^X/) it removes the first 2 non-space-then-space pairs (gensub(/(\S+\s+){2}/,"","")) and stores the result in associative array a indexed by the original 2nd field (a[$2] = ...), so for example for input line X B C D E it saves a["B"] = "C D E". If the line did not start with X (else) then it prints the array indexed by the 2nd field in the current line, so for input line Z B it will execute print a["B"] and so output C D E.
With an old version of gawk (run gawk --version and check for version before 4.0) you might need:
$ gawk --re-interval '{ if (/^X/) a[$2] = gensub(/([^[:space:]]+[[:space:]]+){2}/,"",""); else print a[$2] }' file
but if so youre missing a lot of very useful functionality so get a new gawk!
The declaration should go outside the loop. The variable interpolations need a dollar sign in front. The rest is just refactoring.
declare -A dependencies
awk '$1=="X"{$1=""; print }' "$1" |
{ while read -r key value;
do
dependencies["$key"]="$value"
echo -e "\nvalues of $key_name are ${key_name[*]}\n"
#sleep 1
done
:
# do stuff with "${dependencies[#]}"
}

Resources