0

I'm supposed to follow a logfile, I want to initiate a sed command to edit conffile upon appearance of certain line in the log. I did little research and found out that it can be done with awk.

and the syntax is like:

tail -f /path/to/serverLog | awk '
                    /Printer is on fire!/ { system("shutdown -h now") }
                    /new USB high speed/  { system("echo \"New USB\" | mail admin") }'

as proposed in this answer

so I wrote my own like with a sed variant:

tail -f logfile | awk '/^Jit ended**/  { system("sed -E '/^Jit/{s/enabled=false/enabled=true/; s/From=[0-9]+-[0-9]+-[0-9]+/From=2021-02-01/}' conffile") }'

but it doesn't work, and throws errors like

(^ syntax error|^ unterminated string)

I guess this is to do something with {} in the sed command not getting compatible with awk syntax, but no idea where exactly, and how to get it work.

schrodingerscatcuriosity
  • 12,087
  • 3
  • 29
  • 57
Sollosa
  • 1,887
  • 4
  • 19
  • 32
  • 2
    You are trying to use a single-quoted string constant (the `sed` expression) inside a single-quoted string constant (the `awk` program) ... that won't work, unfortunately. – AdminBee Oct 05 '21 at 15:29
  • 4
    Whatever you want to do with `sed` from `awk` can most likely be done in `awk` directly. – Kusalananda Oct 05 '21 at 15:32
  • you might also want to read [Why does awk do full buffering when reading from a pipe](https://unix.stackexchange.com/q/33650/72456). – αғsнιη Oct 05 '21 at 15:49
  • @AdminBee ok but I want to add changes into file. So what's the way to do if not with sed? – Sollosa Oct 05 '21 at 15:53
  • [GNU Awk manual (sub, gsub, gensub)](https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html) do replacement. – schrodingerscatcuriosity Oct 05 '21 at 15:56
  • @they please if you can propose a method – Sollosa Oct 05 '21 at 15:57
  • @schrodigerscatcuriosity actually I want to do in-place substitution, and logfile following is necessary, so can you suggest any alternate? – Sollosa Oct 05 '21 at 16:17
  • 3
    GNU awk has `-i inplace`, just like GNU sed has `-i` but awk is a tool to manipulate text while a shell is the tool to manipulate files and processes and sequence calls to commands - you're trying to use awk as if it was a shell, don't do that. – Ed Morton Oct 05 '21 at 16:41

2 Answers2

3

By calling awk to call other commands you're doing:

shell { awk { system { subshell { cmd } } } }

instead of simply

shell { cmd }

which is very inefficient and fragile.

Best I can tell you should just be doing something like (untested):

while IFS= read -r line; do
    case $line in
        *"Printer is on fire!"* ) shutdown -h now ;;
        *"new USB high speed"* )  echo 'New USB' | mail admin ;;
        "Jit ended"* )            tmp=$(mktemp) &&
                                  sed 's/foo/bar/' conffile > "$tmp" &&
                                  mv -- "$tmp" conffile
                                  ;;
    esac
    sleep 1
done < '/path/to/serverLog'
Ed Morton
  • 28,789
  • 5
  • 20
  • 47
  • Thanks Ed, this code seems promising, but it looks like input redirection only reads logfile single time, whereas I need it to follow logfile, till it finds required occurence of the text. – Sollosa Oct 05 '21 at 18:12
  • 2
    @Sollosa There is nothing stopping you from piping `tail -f logfile` into the loop instead of reading from the log with a redirection. – Kusalananda Oct 05 '21 at 18:22
  • @they I'm using this syntax, `done < <(tail -f logfile)` but it's not reading logfile instead it throws syntax error – Sollosa Oct 05 '21 at 19:14
  • 2
    @Sollosa Possibly because you are running the script with the wrong interpreter (`sh` instead of `bash`?). In any case, why don't you try what you used in the question and what I alluded to when I suggested piping the input from `tail`, i.e. `tail -f logfile | while ...; do ...; done` – Kusalananda Oct 05 '21 at 19:19
1

You can use GNU awk's replacement function sub, given the sample input from your previous question, and using the inplace extension to write the file (always do a backup before executing your command):

$ cat conffile
Jit .... enabled=false
Jit ..shoes.. From=2021-01-01
Jit ..gloves.. From=2021-01-01

$ tail -f logfile | while IFS= read -r i; do 
  if echo "$i" | grep '^Jit'; then
    awk -i inplace '/^Jit/ { sub(/=false$/, "=true"); sub(/[0-9\-]+$/, "2021-02-01"); print  }' conffile
    exit # exit if a match is found
  fi
done
$ cat conffile
Jit .... enabled=true
Jit ..shoes.. From=2021-02-01
Jit ..gloves.. From=2021-02-01

I should add my commentary from my answer to that question regarding the substitution:

I propose a solution assuming (until more comprehensive sample input) that the lines have strict end patterns. If that's the case it wouldn't be necessary more complex matching.

And a note: I'm using grep because I'm not savvy enough with awk, I'm sure other answers will be much better (and this one criticized or corrected), but maybe you can get a glimpse of what you can do.

schrodingerscatcuriosity
  • 12,087
  • 3
  • 29
  • 57