29

I have an awk script, new.awk:

BEGIN { FS = OFS = "," }

NR == 1 { for (i = 1; i <= NF; i++) f[$i] = i }

NR > 1 { begSecs = mktime(gensub(/[":-]/, " ", "g", $(f["DateTime"]))) endSecs = begSecs + $(f["TotalDuration"]) $(f["CallEndTime"]) = strftime("%Y-%m-%d %H:%M:%S", endSecs) }

{ print }

I am calling this in shell

awk new.awk sample.csv

... but I can see the changes in the terminal. How to make the change in-place in the file, like when using sed -i?

Kusalananda
  • 333,661
mittu
  • 397

2 Answers2

42

GNU awk (commonly found on Linux systems), since version 4.1.0, can include an "awk source library" with -i or --include on the command line (see How to safely use gawk's -i option or @include directive? along with Stéphane's comment below for security issues related to this). One of the source libraries that is distributed with GNU awk is one called inplace:

$ cat file
hello
there
$ awk -i inplace '/hello/ { print "oh,", $0 }' file
$ cat file
oh, hello

As you can see, this makes the output of the awk code replace the input file. The line saying there is not kept as the program does not output it.

With an awk script in a file, you would use it like

awk -i inplace -f script.awk datafile

If the awk variable INPLACE_SUFFIX is set to a string, then the library would make a backup of the original file with that as a filename suffix.

awk -i inplace -v INPLACE_SUFFIX=.bak -f script.awk datafile

If you have several input files, each file with be individually in-place edited. But you can turn in-place editing off for a file (or a set of files) by using inplace=0 on the command line before that file:

awk -i inplace -f script.awk file1 file2 inplace=0 file3 inplace=1 file4

In the above command, file3 would not be edited in place.


For a more portable "in-place edit" of a single file, use

tmpfile=$(mktemp)
cp file "$tmpfile" &&
awk '...some program here...' "$tmpfile" >file
rm "$tmpfile"

This would copy the input file to a temporary location, then apply the awk code on the temporary file while redirecting to the original filename.

Doing the operations in this order (running awk on the temporary file, not on the original file) ensures that the file meta-data (permissions and ownership) of the original file is not modified.

Kusalananda
  • 333,661
  • 1
    I am running ubuntu 20.04 and getting error awk: not an option: -i – Satish May 09 '22 at 16:17
  • @Satish What is important is what awk you're using. My answer is about GNU awk. What does awk --version say on your system? (I'm assuming it will say awk: not an option: --version, which will mean it's probably mawk, which is not GNU awk). – Kusalananda May 09 '22 at 18:40
  • thanks for the tip. very helpful. – Dudi Boy May 27 '22 at 10:41
  • Should be awk -i /usr/share/awk/inplace.awk or wherever that extension is on your system. Without a full path, gawk tries to load the inplace extension (as inplace or inplace.awk) from the current working directory first, where someone could have planted malware. The path of the inplace extension supplied with gawk may vary with the system, see the output of gawk 'BEGIN{print ENVIRON["AWKPATH"]}'. See How to safely use gawk's -i option or @include directive? for details. – Stéphane Chazelas Aug 23 '23 at 07:17
  • @StéphaneChazelas Thanks for the heads-up about this. I'll leave your comment in place and have added a parenthesis in my answer referencing the other question. – Kusalananda Aug 24 '23 at 07:04
7

Try this.

awk  new.awk sample.csv > tmp.csv && mv -f tmp.csv sample.csv
  • redirect the output to a temp file.
  • then move content of temp file to original file.
Siva
  • 9,077
  • 5
    If you look at the awk inplace source code this is exactly what it does: creates a temporary file, redirects stdout there and at the end renames it to the input file. – chx Jan 23 '19 at 12:18