Change specific part of file via a shell script

What is the simplest way to search for a word in a file given in the command line, with this format

./<file1> -f <file2> --edit <id> <column> <value>

I want to search a persons <id> in <file2> and change the word in <column> given with <value>.

I have tried

awk -F '|' -v ID="$4" -v Column="$5" 
           -v Value="$6" 'ID==$1 {$Column=Value ;}1' 
           OFS='|' $2>NewFile
mv NewFile $2 ;

But I want it done without a temporary file

For example:

1000|text1|text2|text3
1001|text4|text5|text6
1002|text7|text8|text9

After I execute

./<file> -f file2 --edit 1001 2 Marios

it should have this change:

1000|text1|text2|text3
1001|Marios|text5|text6
1002|text7|text8|text9

Answer

Editing text files without a temporary file is a bad idea and usually isn’t done in Unix scripting. It requires rewriting the entire file, or at least the suffix portion which is affected by an edit. If the write is interrupted, the file is corrupt.

Of course, we do this everyday with text editors: we hold files in memory and overwrite them on disk when we save. The differences are that all decent editors keep at least one backup, unless that feature is explicitly disabled (and that is an extra file, not acceptable to you, presumably), and that editors are interactive: if a save fails for any reason (disk full, system crash, whatever) a person knows about it. In cases when it isn’t a crash, the editor is still running and has the file in memory in spite of the failed save. The user can execute commands to save the file elsewhere, or to try saving again after taking some actions outside of the program to fix some situation.

Solution in TXR: overwrite from in-memory copy with no backup or recovery strategy:

#!/usr/local/bin/txr --lisp

(defvarl myname [*args-full* 2])

;; check for required arguments syntax
(unless (and (= (length *args*) 6)
             (equal [*args* 0] "-f")
             (equal [*args* 2] "--edit"))
  (put-line `usage: @myname -f <file> --edit <col1-key> <col-num> <replace>`)
  (exit 1))

;; do in-memory update and overwrite
(let ((file [*args* 1])
      (key [*args* 3])
      (col (pred (tointz [*args* 4]))) ;; pred, because [f #] is zero based
      (val [*args* 5])
      (ss (make-strlist-output-stream))) ;; in-memory string list stream

  ;; awk into memory
  (awk (:inputs file) ;; input from file
       (:output ss) ;; output stream is in-memory string list
       (:set fs "|") ;; field separator is pipe
       ((equal [f 0] key) (set [f col] val)) ;; do replacement
       (t)) ;; true condition with no action -> default print action

  ;; overwrite original file with string list
  (with-stream (out (open-file file "w"))
    (put-lines (get-list-from-stream ss) out)))

Session:

$ diff -u data.orig data
$ ./inplace 
usage: ./inplace -f <file> --edit <col1-key> <col-num> <replace>
$ ./inplace -f data --edit 1001 2
usage: ./inplace -f <file> --edit <col1-key> <col-num> <replace>
$ ./inplace -f data --edit 1001 2 Marios
$ diff -u data.orig data
--- data.orig   2016-10-16 08:05:03.233736781 -0700
+++ data    2016-10-16 08:15:57.412394022 -0700
@@ -1,3 +1,3 @@
 1000|text1|text2|text3
-1001|text4|text5|text6
+1001 Marios text5 text6
 1002|text7|text8|text9

Leave a Reply

Your email address will not be published. Required fields are marked *