4

In recent version of awk there is a inplace option that can be used to do in place operations similar to sed's -i option. However I can't make it work with print statement. Let's see my example. The content of the file test is:

11 aa
22 bb
root@localhost:~# cat test
11 aa
22 bb

If I don't use -i inplace, I can get my desired result in the console.

root@localhost:~# awk 'BEGIN{print "begin"} $1=="11"{print "111" $2; next} 1; END{print "end"}' test
begin
111aa
22 bb
end

When I add -i inplace, this is what I got.

root@localhost:~# awk -i inplace 'BEGIN{print "begin"} $1=="11"{print "111" $2; next} 1; END{print "end"}' test
begin
end
root@localhost:~# cat test
111aa
22 bb
root@localhost:~#

How to improve my code and get what I want?

Update


My awk version is 4.1.1.

root@localhost:~# awk --version
GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2-p3, GNU MP 6.0.0)
Copyright (C) 1989, 1991-2014 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
root@localhost:~#

Update2


root@localhost:~# awk --version
GNU Awk 4.1.2, API: 1.1
Copyright (C) 1989, 1991-2015 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.
root@localhost:~# cat file
11 aa
22 bb
root@localhost:~# awk 'BEGIN{print "begin"} $1=="11"{print "111" $2; next} 1; END{print "end"}' file
begin
111aa
22 bb
end
root@localhost:~# awk -i inplace 'BEGIN{print "begin"} $1=="11"{print "111" $2; next} 1; END{print "end"}' file
begin
end
root@localhost:~# cat file
111aa
22 bb
root@localhost:~#
9
  • Yes, the version is 4.1.1 Commented May 11, 2015 at 17:05
  • I can reproduce your described effect with GNU awk 4.1.1, with BEGIN / END as well as with BEGINFILE / ENDFILE. Andrew Schorr informed in Usenet that there's a fix of that with GNU awk version 4.1.2. Commented May 12, 2015 at 0:28
  • @Janis: It seems that BEGINFILE worked but ENDFILE didn't. Can you please give us that link. Commented May 12, 2015 at 4:14
  • @Janis, I've tested on gawk 4.1.2 and it still not work. See my updates. Commented May 12, 2015 at 4:18
  • 2
    @OgrishMan: You should try BEGINFILE and ENDFILE. Commented May 12, 2015 at 4:27

2 Answers 2

3

I think you need to use BEGINFILE instead of BEGIN and ENDFILE instead of END for the begin and end to print into the file.

At least that works on cygwin.

Rob@Rob-PC /cygdrive/c/tmp
$ awk --version
GNU Awk 4.1.1, API: 1.1 (GNU MPFR 3.1.2, GNU MP 6.0.0)
Copyright (C) 1989, 1991-2014 Free Software Foundation.


Rob@Rob-PC /cygdrive/c/tmp
$ echo "11 aa
22 bb" > test

Rob@Rob-PC /cygdrive/c/tmp
$ awk -i inplace 'BEGINFILE{print "begin"} $1=="11"{print "111" $2; next} 1; ENDFILE{print "end" >> "test"}' test

Rob@Rob-PC /cygdrive/c/tmp
$ cat test
begin
111aa
22 bb
end
4
  • I've tried that. Didn't work. Commented May 11, 2015 at 17:22
  • Yeah the begin works but not the end :( Commented May 11, 2015 at 17:30
  • You missed the last end. Commented May 11, 2015 at 17:32
  • Did you try my update :) with the >> in the ENDFILE to append to the file :) Commented May 11, 2015 at 17:32
0

The BEGIN and END loops are run before the file is processed and after it has been processed in full. The -i inplace only makes sense while a file is being processed. I can't be sure without checking the source code but it therefore seems reasonable that the BEGIN and END won't work here.

As has already been suggested, that's what BEGINFILE and ENDFILE are for. If those don't work in your setup, you will have to use workarounds. While matching the first line is easy (if(NR==1){print "begin"}), matching the last line is a bit more tricky. Some tricks you can use are:

  1. Add the begin and end lines using another tool:

    perl -i -007ne 'print "begin\n${_}end\n"' file &&
        awk -i inplace '$1=="11"{print "111" $2; next}1;' file
    

    or

    printf "begin\n%s\nend" "$(cat file)" 
    
  2. Use awk for the "begin" and add the "end" later:

    awk -i inplace  'NR==1{print "begin"}$1=="11"{print "111" $2; next}1;' file && 
        echo "end" >> file
    
  3. Do the whole thing using a tool that can understand eof. Perl's -a switch makes it act like awk: it will split fields on whitespace into the @F array. The Just like awk, the -F switch lets you set the field delimiter. The -i switch edits the file in place:

    perl -i -lane '$.==1 && print "begin"; eof && print "end"; 
                   if($F[0]=="11"){print "111$F[0] $F[1]"; next} print;' file
    

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.