1

I am trying to invoke awk inside a bash script and passing some bash variables values to it. Is there any way to get a count of these variables and print all which are passed to it inside awk.

No. of variables passed to awk would be dynamic.

Below example

#!/bin/bash
NAME="Dell"
SERIAL="12345"
echo "Hello" | awk -v a=$NAME -v b=$SERIAL '{ print a,b }'

Referenced, Working example, Irvur's answer in here

#!/bin/bash

function confirm_peers (){
  header_def=("${header_def[@]:-None}")
  read -r -p "Enter nodes from header[${header_def[*]}]: " -a header
  header=("${header[@]:-${header_def[@]}}") ;
  echo -e "\nInput Recorded - ${header[@]} \n"
}

createEncConfig()
{
 /usr/bin/awk -f - <<-'EOE' ${header[*]}  FS=":" $1
  BEGIN {
    count=1
    for (i = 1; i < ARGC - 2; ++i) {
    split(ARGV[i], ar, "=");
    print ar[2]
    }
print "\nCount=" ARGC -3
        }
EOE
}
confirm_peers
# Making array content adaptable to awk input
for x in ${!header[@]} ; do header[x]="a=${header[x]}"; done
createEncConfig $1

Referenced - Easiest so far , Kusalananda's answer

#!/bin/bash

function confirm_peers (){
  header_def=("${header_def[@]:-None}")
  read -r -p "Enter nodes from header[${header_def[*]}]: " -a header
  header=("${header[@]:-${header_def[@]}}") ;
  echo -e "\nInput Recorded - ${header[@]} \n"
}

createEncConfig()
{
/usr/bin/awk -v args="${header[*]}" -f - <<-'EOE' FS=":" $1
BEGIN {
  count=split(args,ar," ")
  for ( x in ar ) {
    print ar[x]
  }
print "\n" count
}
EOE
}
confirm_peers
createEncConfig $1

Output : Just pass any dummy file

$ bash /tmp/a.bsh /tmp/enc1.txt

Enter nodes from header[None]: a b c d

Input Recorded - a b c d

a
b
c
d

Count=4

Thanks to all ..

Bharat
  • 814
  • 2
    I don't understand why you don't know how many variables you're passing to awk. You're specifically passing two here. Can you show an example where you have an unknown number of variables you're passing to awk? – Jeff Schaller Oct 13 '18 at 12:59
  • yes, here I am passing two but it was just for example, now it is like this and seems working, header is an array holding input from user , this way I am passing dynamic variables. /usr/bin/awk -f - <<-'EOE' $(echo "${header[*]}") FS=":" $1 .............etc – Bharat Oct 13 '18 at 14:24
  • then just do echo ${#header[@]} everywhere you want, to know how many arguments are there – αғsнιη Oct 13 '18 at 14:27
  • I had to read the count inside awk , once I invoke awk inside bash , it doesn't see anything from bash, – Bharat Oct 13 '18 at 14:33
  • read within awk, so awk '{... ; print args; ....}' args=${#header[@]}. or if you need values awk '{... ;split(args, intoArray [, separator]) ; print intoArray[1]; ....}' args="$(echo {header[@]})", better to use printf – αғsнιη Oct 13 '18 at 14:42
  • @sddgob I can't see args inside BEGIN this way – Bharat Oct 13 '18 at 15:42
  • depending on awk implementations shell variables are not available within BEGIN block and you will need convert thrm into awk variables with -v Var, while I don't see any point of using it within BEGIN block. you also can do it by awk -F'SEP' '{ print NF }' <<<"${header[*]}" in short that I moved into an answer – αғsнιη Oct 13 '18 at 16:04
  • couldn't you just pass an additional variable, ala -v nargs=${#header[@]} ? – Jeff Schaller Oct 14 '18 at 00:41

5 Answers5

2

One option I can think of is to reposition those variables so they feature in ARGV. ARGC will indicate how many of them there are. Beware this makes the variables unavailable in BEGIN block. You also need to account for any file names that you may be passing as parameters to awk by subtracting the number of such files from ARGC

echo "Hello" | awk  '{ print a, b, ARGC - 1}' a=$NAME b=$SERIAL
DELL 12345 2
iruvar
  • 16,725
  • ARGC should help, wondering why awk doesn't consider them as an input file – Bharat Oct 13 '18 at 01:24
  • @Bharat, good question. awk treats anything with a = in it as a variable assignment. The only way to get awk to treat it a=$NAME as a file appears to be using -- to signal end of options. For example echo "Hello" | awk -- '{ print FILENAME, a, b, ARGC - 1}' a=$NAME – iruvar Oct 13 '18 at 01:43
  • One more thing, ARGV stores complete assignment i.e a=$NAME in it , any easy way to figure out the variable from it, as here I have hard code a and b but values should be dynamic, echo "Hello" | awk -- '{ print FILENAME, a, b,ARGV[1], ARGC - 1}' a=$NAME – Bharat Oct 13 '18 at 01:59
  • 1
    @Bharat, split(ARGV[1], a, "="), print a[0],a[1] – iruvar Oct 13 '18 at 02:05
  • @iruvar -- will not prevent awk from treating subsequent arguments of the a=b form as variable assigments. You can check with echo | awk -- '{print KEY}' KEY=value. See also my answer here. Your example is misleading because FILENAME will be - if it's the stdin -- try it with a=17 instead ;-) –  Oct 13 '18 at 14:30
  • @mosvy, you're right. – iruvar Oct 13 '18 at 22:37
  • @iruvar but the idea in your answer is right: awk 'BEGIN{for(i=1; i<ARGC; i++) vn+=(ARGV[i]~/^[a-zA-Z_][a-zA-Z0-9_]*=/); print vn}' foo=bar zep=q too=r => 3 (that pattern should catch assignment as they're described in /OPERANDS/argument/assignment here) –  Oct 14 '18 at 10:08
1

From your comment, it seems that you have an array in bash that you'd like to pass into an awk program on the command line.

Here's an easy way of doing that:

array=("my 1st item" "my 2nd item" "the last item")

(
    IFS=:

    awk -v var="${array[*]}" '
        BEGIN {
            array_len = split(var, array, ":")
            for (i = 1; i <= array_len; ++i)
                printf("Item #%d is \"%s\"\n", i, array[i])
        }'
)

This would output

Item #1 is "my 1st item"
Item #2 is "my 2nd item"
Item #3 is "the last item"

This takes the array and turns it into a single string of :-delimited items. If any of the elements of the array contains :, then pick another character for delimiting the items in the string. The expansion of ${array[*]} will join the items in the array on the first character of $IFS, which is why we set this variable before performing that expansion. This string is passed to awk as the variable var.

In the BEGIN block in the awk program, we then create the array array and also keep track of the array's length in array_len through splitting var on :. The loop is just for showing that we managed to split the string successfully.

We set IFS and run our awk program in a subshell so that the rest of the script can run with an unmodified IFS variable.

Kusalananda
  • 333,661
  • makes it easier, wondering putting it like this, wont show it in BEGIN, awk 'BEGIN{print args}{}' args="${header[*]}" /tmp/enc1.txt whereas this works well awk -v args="${header[*]}" 'BEGIN{print args}{}' /tmp/enc1.txt – Bharat Oct 13 '18 at 15:41
  • @Bharat That first code won't work because awk will not have looked at the command line arguments (which are usually the names of files to parse) until after the BEGIN block. – Kusalananda Oct 13 '18 at 15:57
  • thanks, looks like ARGC and ARGV has that capability only – Bharat Oct 13 '18 at 15:59
0

Now, as awk doesn't provide means to get at its own command line and the options therein, this is a bit far fetched, and it relies on linux internals like the proc file system, but just for the fun of it (YMMV):

awk -v"VAR1=sd" -va=b -vJ=onny '
BEGIN   {("tr \47\\000\\n\47 \47\t\47 </proc/$PPID/cmdline") | getline CMDLN
         n = split (CMDLN, ELM, "\t")
         for (i=1; i<=n; i++)   if (sub ("^-v", "", ELM[i]))    {split (ELM[i], T, "=")
                                                                 print "Option", T[1], "=", T[2]
                                                                 Options++
                                                                } 
        }
'
Option VAR1 = sd
Option a = b
Option J = onny

The "command" being read by getline will be executed in a subshell, so we get at the cmdline of the parent PID, and tr the <NUL> and <LF> chars to <TAB>s. Then, the CMDLN is split into its elements, in each of which a leading "-v" is matched (and eliminated) to find variable definitions. Those are printed, and the options count incremented. Give it a try.

RudiC
  • 8,969
0

You can pass your array into awk directly with here-string.

awk -F'SEP' '{ print NF }' <<<"${header[*]}"

Where the SEP is the character which your array elements are delimited with.

Tset:

$ array=( 1, 2, 3, 7 )
$ awk -F',' '{ print NF }' <<<"${array[*]}"
4

And $1 will be your first element's value, $2 second and etc.

αғsнιη
  • 41,407
0

Since you have a bash array, and bash knows how many elements are in the array, simply tell awk directly:

$ array=("my 1st item" "my 2nd item" "the last item")
$ awk -v var="${array[*]}" -v nargs=${#array[@]} '
  BEGIN {
    print nargs;
  }
'
3
Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255