开发者

Adding line with text between pattern and next occurence of the same pattern in bash

开发者 https://www.devze.com 2023-02-08 17:26 出处:网络
I am writing a bash script that modifies a file that looks like this: --- usr1 --- data data data data data data data data

I am writing a bash script that modifies a file that looks like this:

--- usr1 ---

data data data data

data data data data

data data data data

--- usr2 ---

data data data data

data data data data

--开发者_运维知识库- usr3 ---

data data data data

--- endline ---

One question is: How to add next user line --- usrn --- after last user data lines? Second one is: How to delete specific user data lines (data lines and --- userx ---) i.e. I would like to delete usr2 with all his data set.

It must work on bash 2.05 :) and I think it will use awk or sed, but I'm not sure.

A little edit here: In fact usernames are not numbered. We don't know what users will come up with. We only know that the name will be inside --- pattern


Given a username in a variable:

username="kasper"

Delete a user section:

sed "/$username/{:a;N;/\n--- [^[:blank:]]* ---\$/{s/.*\n//;b};ba}" inputfile

or for some versions of sed (edited):

sed -e "/$username/{" -e ':a' -e 'N' -e '/\n--- [^[:blank:]]* ---$/{s/.*\n//' -e 'b' -e '}' -e 'ba' -e '}' inputfile

Edit: a possible variation to accommodate leading and trailing whitespace:

sed -e "/$username/{" -e ':a' -e 'N' -e '/\n[[:blank:]]*---[[:blank:]]*[^[:blank:]]*[[:blank:]]*---[[:blank:]]*$/{s/.*\n//' -e 'b' -e '}' -e 'ba' -e '}' inputfile

Add the next user section:

sed "s/--- end/--- $username ---\ndata data data data\ndata data data data\n&/"

or

sed "/--- end/i--- $username ---\ndatadata data data\ndata data data data\n"

If your version of sed supports in-place changes, you can do:

sed -i ...

Otherwise, you'll have to use a temporary file:

sed ... inputfile > tmpfile && mv tmpfile inputfile

It's best to use a utility like mktemp or tempfile to create a temporary file and use the name provided.


An awk solution for the 2nd part:

awk -v del="usr2" 'match($0,/^--- (.*) ---$/,a) {p = (a[1] != del)} p'

This turns the p flag off if the head matched the user to delete, otherwise it prints every line.


Plain shell can handle this

Updated answer to include the second requirement...

#!/bin/sh

NEWUSER='John Doe'
NEWUSERDATA='how now brown cow'
REMOVEUSER='usr2'

state=COPY
while read x; do
  case "$state/$x" in
    *"/--- endline ---")
      echo --- $NEWUSER ---
      echo "$NEWUSERDATA"
      state=COPY
      ;;
    COPY/*)
      if [ "$x" == "--- $REMOVEUSER ---" ]; then
        state=REMOVE
      fi
      ;;
    REMOVE/---*)
      state=COPY
      ;;
  esac
  if [ $state != REMOVE ]; then
    echo "$x"
  fi
done

Usage is something like: sh newuser.sh < usertable.txt > newusertable.txt

By the way, there is an alternative style of writing shell scripts which I might call "gnu configure" format. In "alternative style" the main loop would be:

while read x; do
  case "$state/$x" in
    *"/--- endline ---")
      echo --- $NEWUSER ---
      echo "$NEWUSERDATA"
      state=COPY;;
    COPY/*)
      [ "$x" == "--- $REMOVEUSER ---" ] && state=REMOVE;;
    REMOVE/---*)
      state=COPY;;
  esac
  [ $state != REMOVE ] && echo "$x"
done
0

精彩评论

暂无评论...
验证码 换一张
取 消