Function with awk to print single or multiple columns Function with awk to print single or multiple columns unix unix

Function with awk to print single or multiple columns


$ cat tst.shfunction a {    awk -v args="$*" '        BEGIN { n=split(args,f) }        { for (i=1;i<=n;i++) printf "%s%s", $(f[i]), (i<n?OFS:ORS) }    '}echo "a b c d e f" | a 1 3 5echo "---"echo "a b c d e f" | a 1 3 4 6$ ./tst.sh  a c e---a c d f


You could get arbitrary complicated with this sort of thing (what if you wanted to be able to say a 2-5 7 11-, as with cut?) but here's one that will work with a list of numbers:

a() { (IFS=,; awk '{print '"${*/#/$}"'}'); }

That requires a bit of explanation.

a() { ... }

defines a shell function, which differs from an alias in various ways, one of which being that you can give it parameters.

Inside the shell function, I want to change the value of IFS; to avoid having to remember the old value and change it back, I surround the command I actually want to execute with (...), which causes it to execute in a subshell. When the subshell finishes, all environmental changes finish with it, so it effectively makes the change to IFS local.

IFS is the set of characters used for word splitting, but it also defines the character used to separate elements in the expansion of "$*" (that is, the list of function or script arguments) when it is surrounded by quotes. So setting it to , means the $* expansion will be a comma-separated list.

The awk program I want to create is actually something like {print $1,$4,$7}, so aside from putting commas between the list, I need to add a $ before each number. I do that with the bash parameter expansion substitute syntax: ${parameter/pattern/replacement}. By specifying * as the parameter, I get $* with the substitution applied to each argument. (Note that the expansion is quoted. If it weren't, it wouldn't work.)

In the replacement expression, the pattern is empty because the # character at the beginning of the pattern indicates that the match must be at the beginning of the string. Since the actual pattern is empty, the first match always be at the beginning of the string and the replacement ($) will therefore be inserted at the beginning of each argument. The # is needed because // is syntactically different: it means "change all occurrences of the pattern", instead of just the first one.

Unlike many languages, in bash search-and-replace expressions are not terminated with a /, but rather with the matching }. If you type ${p/foo/bar/}, it will replace the first instance of foo with bar/.