0

I am trying to argument processing on awk. It works fine if I have no body in it:

PK@rhel8:~/tmp-> cat testARG.awk 
#!/usr/bin/awk -f
BEGIN{
argc = ARGC ;
CmdName = ARGV[0] ;
FirstArg = ARGV[1]      
printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
}
#{
#   printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
#   print $0
#}
PK@rhel8:~/tmp-> ./testARG.awk 1 2 3 4
Argument count = 5; command name = awk; first argument = 1
PK@rhel8:~/tmp-> 

However when I uncomment the body it doesn't like it at all:

PK@rhel8:~/tmp-> cat testARG.awk 
#!/usr/bin/awk -f
BEGIN{
argc = ARGC ;
CmdName = ARGV[0] ;
FirstArg = ARGV[1]      
printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
}
{
    printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
    print $0
}
PK@rhel8:~/tmp-> ./testARG.awk 1 2 3 4
Argument count = 5; command name = awk; first argument = 1
awk: ./testARG.awk:6: fatal: cannot open file `1' for reading (No such file or directory)
PK@rhel8:~/tmp-> 

Is there some different way I have to use awk to allow it to see the arguments as arguments and not files?

Philip Kearns
  • 377
  • 4
  • 14
  • 1
    What you call *body* waits for input and takes your first command line argument as file descriptor. Keep everything in `BEGIN` without input files. – Andre Wildberg Feb 10 '23 at 16:36
  • Don't use a shebang to call awk. Just call awk. See https://stackoverflow.com/a/61002754/1745001 – Ed Morton Feb 10 '23 at 16:45
  • Same outcome - see my answer below. – Philip Kearns Feb 10 '23 at 16:46
  • Using a shebang to call awk just make everything more complicated, coming up with a way to work around that extra complication for one specific scenario isn't the best approach IMO when it's trivial to just call awk instead. – Ed Morton Feb 10 '23 at 16:51
  • But it's the same outcome, it still thinks the arguments are files. – Philip Kearns Feb 10 '23 at 16:54
  • @PhilipKearns what awk thinks is entirely up to the code you write. Your solution to your problem is to add a `-` to the list of args you're calling your Unix command with. You're going that because you're using a shebang. markp-fuso's solution is to add awk-specific `-v` flags to the list of args you're calling your Unix command with. They're going that because you're using a shebang. If you simply don't use a shebang then you don't need to change/complicate/tightly couple the interface to your command - just write the code inside your command to call awk passing the shell args as awk vars. – Ed Morton Feb 10 '23 at 17:23

3 Answers3

2

The fix is to add a -:

PK@rhel8:~/tmp-> ./testARG.awk - 1 2 3 4
Argument count = 6; command name = awk; first argument = -
howdy
Argument count = 6; command name = awk; first argument = -
howdy
^C
PK@rhel8:~/tmp->

Of course I have to move the arguments down by one to skip the -. However it still gets confused if you use ^D instead of ^C:

PK@rhel8:~/tmp-> ./testARG.awk - 1 2 3 4 
Argument count = 6; command name = awk; first argument = -
howdy
Argument count = 6; command name = awk; first argument = -
howdy
howdy there
Argument count = 6; command name = awk; first argument = -
howdy there
awk: ./testARG.awk:10: fatal: cannot open file `1' for reading (No such file or directory)
PK@rhel8:~/tmp-> 

Not sure how the coders intended it to be used. So it would appear the solution suggested @markp-fuso is better choice.

Philip Kearns
  • 377
  • 4
  • 14
2

You may want to get used to a more standard way of passing non-file args to awk. A common method is to define awk variable assignments on the command line. The general format:

awk -v awk_var1="OS val 1" -v awk_var2="OS val 2" '... awk script ...' [ optional list of filenames]

# or

./script.awk -v awk_var1="OS val 1" -v awk_var2="OS val 2" [ optional list of filenames]

If you won't be processing any files then all processing within the awk script will need to take place within the BEGIN{} block.

Since the current script is looking to count input args and print the 'first', I take it to mean the number of input args could be variable. One common approach would be to provide the args in a single delimited string, eg:

$ cat testARG.awk 
#!/usr/bin/awk -f
BEGIN { n=split(inlist,vars,";")           # split awk input variable "inlist" on a ";" delimiter and put results in the vars[] array; split() returns the number of entries in the array, we save this count in awk variable "n"
        CmdName = ARGV[0]
        printf "Argument count = %d; command name = %s; first argument = %s\n", n, CmdName, vars[1]
      }

$ ./testARG.awk -v inlist="1;2;3;4"        # define awk variable "inlist" as a ";"-delimited list of values
Argument count = 4; command name = awk; first argument = 1
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
  • 1
    Good point: I've used the method many times before in a different context, until you said it I hadn't thought of using it here. Thanks. – Philip Kearns Feb 10 '23 at 16:57
1

It's not entirely clear what you want to do since you're passing numbers to your Unix command and apparently don't want any of them treated as file names, but then your uncommented code relies on having an input file to process.

I'm assuming since you have a part of your script operating line by line printing $0 that you'll pass a file name for awk to work on as the final argument to your command but you want the command arguments before that last one to not be treated as files.

Given that, here's how to write a Unix command to see initial arguments to it as values to be passed to your awk script and not as files without changing the interface to your script and without tightly coupling it to being implemented in awk:

$ cat testArg
#!/usr/bin/env bash

args="${*:1:$#-1}"
shift $(( $# - 1 ))

awk -v args="$args" '
    BEGIN{
        argc = split(args,vals)
        CmdName = ARGV[0] ;
        FirstArg = vals[1]
        printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
    }
    {
        printf("Argument count = %d; command name = %s; first argument = %s\n",argc,CmdName,FirstArg) ;
        print $0
    }
' "${@:--}"

$ seq 2 > file

$ ./testArg 1 2 3 file
Argument count = 3; command name = awk; first argument = 1
Argument count = 3; command name = awk; first argument = 1
1
Argument count = 3; command name = awk; first argument = 1
2

Note that with this approach you don't need do modify your desired command line in any way, including having anything awk-specific on the command-line when you call your script, and if you decide to replace awk with perl or a compiled C program or anything else inside your script you don't need to change the script's API nor any of the places it's called from.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185