Showing posts with label awk. Show all posts
Showing posts with label awk. Show all posts

Sunday, June 11, 2017

Posted by beni in , , , , , , , , , , , , , | June 11, 2017

A little exercise about a recent forum question input field handling in awk and Perl


Just recently the following question was posted to the UNIX scripting group in Linkedin:
remove all duplicate entries in a colon separated list of strings
e.g. a: b:c: b: d:a:a: b:e::f should be transformed to a: b:c: d:e::f

Some of the fields contain spaces which should be preserved in the output of course, there is an empty field too which (to me and other authors) indicates that the fields are not necessarily ordered. Here I wont discuss the suggested solutions, I also did not answer to the original posting because I read it one month too late.

awk
But when reading the question my brain already got working and I could not help to try for myself. The obvious tool of choice for exercises like this is awk because awk has inbuilt mechanisms for viewing lines as a sequence of fields with configurable field separators.

A solution could be

BEGIN {
  FS=":" ;    # field separator
  ORS=":"     # output record separator
}
{ for(i=1;i<=NF;i++) {  # for all input fields
    if( f[$i] ) {       # check if array entry for field already exists
     continue;          # if yes: go to next field
    } else {
     print $i;          # if no: print the field content
     f[$i] = 1;          # and record it in array f
    }  }
}

which leads to this output:
a: b:c: d:e::f:

The script can be shortened by omitting superfluous braces and else to

BEGIN { FS=":" ; ORS=":" } 
{ for(i=1;i<=NF;i++) { if(f[$i]) continue; f[$i]=1; print $i; } } 

The script uses a very simple straightforward logic: loop through all input fields, if a field is new then print it, if not skip it. This is achieved by storing each field in an associated array f when it first occurs.
Using the field separator FS for splitting the input line and the output record separator ORS when printing (you need to know that print automatically adds ORS) makes this an easy task.

There is one issue though: this solution adds an extra colon at the very end (compared to the requested output), this could be an issue or not depending on the context of this request so one might prefer this code:

BEGIN { FS=":" } 
{ printf $1; f[$1]=1; 
  for(i=2;i<=NF;i++) { if(f[$i]) continue; f[$i]=1; printf FS $i } }

which uses a slightly different logic: the first field is printed straight away (and recorded), the loop checks the remaining fields 2..NF and prints the field separator as a prefix to the field content. This code also works for the extreme case where there is just one field and no colon.

Perl
I then wondered if this couldnt be done equivalently or even shorter in Perl but my best solution is a little bit lengthier because I have to use split to get the individual fields.

$FS=":";
@s = split($FS,<>);
for($i=0;$i<=$#s;$i++) {$e=$s[$i]; next if(exists($f{$e})); $f{$e}=1; print $e,$FS }


I could have used command line options "-a -F:" to avoid the split but I need FS to be defined anyway for the output (I dont know if the split pattern defined by -F can be accessed in Perl).
I use split to chop up the input line and put it into an array s. Then the same logic applies as in awk. Instead of an associative array Im using a hash table f in Perl. The variable e is only used to avoid repeated occurances of $s[$i]. In the end tits a matter of personal preference which solution you take.


It should be noted that I tested with

echo -n "...." | awk ... or perl -e ...

which feeds a string without newline to the pipe which helped to avoid chomp in Perl for removing the newline in the last field.

Monday, April 3, 2017

Posted by beni in , , , , , , | April 03, 2017

5 GNU AWK Practical Examples Part 1



This is the first part of GNU awk command line examples the series in UbuntuBuzz for complete beginners. I planned to continue my previously GNU sed examples the series here, for GNU awk. In this first article, I provide the simplest and basical things about printing in awk. GNU awk is available built-in in almost every GNU/Linux system, including Ubuntu. I hope this article and the rest of the series help everyone a lot.
Subscribe to UbuntuBuzz Telegram Channel https://telegram.me/ubuntubuzz to get article updates directly.


About GNU awk


awk is a programming language name and also a program name, originated from UNIX system (dated back around 30 years ago), which its name is an abbreviation from Aho-Weinberger-Kernighan (read https://en.wikipedia.org/wiki/AWK). As a computer program, awk works to process and manipulate text, especially to filter and edit it in a simple but advanced way, using its own AWK language. GNU awk is a program compatible completely with AWK language and the UNIX awk program, originated from GNU operating system, by The GNU Project. The official page of GNU awk is https://www.gnu.org/software/gawk.


Text Sample


This is the text used as example in this article.  I saved this text in a file named students.txt. I will demonstrate how to use awk towards this text file.

awk { print } students.txt
NAME    ID      OS      MARK
Parker  01      Darwin  19
Zorro   02      BSD     56
Kent    03      GNU     11
Wayne   04      UNIX    9
Robin   05      Plan9   17
Logan   06      VMS     98


1. Basic Printing


Command:
$ awk { print } students.txt

Output:
master@master:/tmp> awk { print $3," ",$4 } students.txt
OS       MARK
Darwin   19
BSD      56
GNU      11
UNIX     9
Plan9    17
VMS      98
master@master:/tmp> 



2. Print Particular Column


Command:
$ awk { print $1 } students.txt


Output:
master@master:/tmp> awk { print $1 } students.txt
NAME
Parker
Zorro
Kent
Wayne
Robin
Logan
master@master:/tmp>


3. Print More Than One Columns


Command:
$ awk { print $3," ",$4 } students.txt


Output:
master@master:/tmp> awk { print $3," ",$4 } students.txt
OS       MARK
Darwin   19
BSD      56
GNU      11
UNIX     9
Plan9    17
VMS      98
master@master:/tmp>



4. Print Columns Reordered


Command:
$ awk { print $4," ",$3," ",$2," ",$1 }


Output:
master@master:/tmp> awk { print $4," ",$3," ",$2," ",$1 } students.txt
MARK     OS      ID      NAME
19       OS/2    01      Parker
56       BSD     02      Zorro
11       GNU     03      Kent
9        UNIX    04      Wayne
17       Plan9   05      Robin
98       VMS     06      Logan
master@master:/tmp>


5. Print A Line


Command:
$ awk NR==1 students.txt
$ awk NR==4 students.txt


Output:

master@master:/tmp> awk NR==1 students.txt
NAME    ID      OS      MARK
master@master:/tmp> awk NR==4 students.txt
Kent    03      GNU     11
master@master:/tmp>


About GNU awk


awk is a computer language name and also a program name, originated from UNIX system (dated back about 30 years ago), consisted of Aho-Weinberger-Kernighan. As a computer program, awk works to manipulate text, especially to filter and edit it in a simple but advanced way, using its own awk language. The program awk from UNIX is proprietary, but its specification is free, and GNU awk is a program from GNU operating system created by The GNU Project as free software to replace UNIX awk completely. GNU awk contains no UNIX awk source code, because its created from scratch.

Search