File modification help: without manual editing through vi :

Hi Experts,

I want to edit a file from a selected pattern range, please advise how to accomplish from command line, without going vi and doing it manually:

portion of the file given below: (red want to remove and save )

  1. I know the interface number is lan5:5 , that need to match first.
  2. I want to get the pattern for this , i.e the instance number for the line containing lan5:5 is is [8] in this case.
  3. I want to remove all the entries for instance number : [8]
 want to delete from line IP_ADDRESS[8]="10.140.220.62"  to  DHCP_ENABLE[8]="0"  line. 
BROADCAST_ADDRESS[5]="10.140.236.255"
INTERFACE_STATE[5]="up"
DHCP_ENABLE[5]="0"
INTERFACE_MODULES[5]=""
ROUTE_DESTINATION[2]=default
ROUTE_GATEWAY[2]=10.40.118.4
ROUTE_COUNT[2]=1
INTERFACE_NAME[8]="lan5:5"
IP_ADDRESS[8]="10.140.220.62"
SUBNET_MASK[8]="255.255.255.0"
BROADCAST_ADDRESS[8]="10.140.220.255"
INTERFACE_STATE[8]="up"
DHCP_ENABLE[8]="0"
INTERFACE_MODULES[8]=""
INTERFACE_NAME[10]="lan5:7"
IP_ADDRESS[10]="10.140.239.62"
SUBNET_MASK[10]="255.255.255.0"
BROADCAST_ADDRESS[10]="10.140.239.255"
INTERFACE_STATE[10]="up"
DHCP_ENABLE[10]="0"

The output in the file should be like this after deleting the patterns:

BROADCAST_ADDRESS[5]="10.140.236.255"
INTERFACE_STATE[5]="up"
DHCP_ENABLE[5]="0"
INTERFACE_MODULES[5]=""
ROUTE_DESTINATION[2]=default
ROUTE_GATEWAY[2]=10.40.118.4
ROUTE_COUNT[2]=1
INTERFACE_NAME[10]="lan5:7"
IP_ADDRESS[10]="10.140.239.62"
SUBNET_MASK[10]="255.255.255.0"
BROADCAST_ADDRESS[10]="10.140.239.255"
INTERFACE_STATE[10]="up"
DHCP_ENABLE[10]="0"

Thanks,

You problem statement is not clear. If you mean that want to delete every line from your input file that contains the string "[8]", the following script will remove that set of lines from the file named by the argument passed to the script:

#!/bin/ksh
ex -s "$1" <<-EOF
        :g/[[]8[]]/d
        :wq
EOF
1 Like

In a while read loop, you could
check the line for [8] then
echo line >> ip_stuff_file_name
otherwise
echo line >> not_ip_stuff_file_name
end check
done

move original_file_name to original_file_name_backup
move the not_ip_stuff_file_name to original_file_name

As usual, you want to run it on test data files only, until you are sure of the results.

There might be more elegant ways, but that's the first thing that came to mind.

Don,
Thanks,
The desired script should find lan5:5 , and then to decide the [8] string, then remove all the lines having [8] entires. Can we put the condition for finding lan5:5 and then do the editing on the file to remove the lines.

  • As without seeing the file I do not know if the lines I am going to remove has the pattern of [8] , but what I know is lan5:5 . Hope you got it.
BROADCAST_ADDRESS[5]="10.140.236.255"
INTERFACE_STATE[5]="up"
DHCP_ENABLE[5]="0"
INTERFACE_MODULES[5]=""
ROUTE_DESTINATION[2]=default
ROUTE_GATEWAY[2]=10.40.118.4
ROUTE_COUNT[2]=1
INTERFACE_NAME[8]="lan5:5"
IP_ADDRESS[8]="10.140.220.62"
SUBNET_MASK[8]="255.255.255.0"
BROADCAST_ADDRESS[8]="10.140.220.255"
INTERFACE_STATE[8]="up"
DHCP_ENABLE[8]="0"
INTERFACE_MODULES[8]=""
INTERFACE_NAME[10]="lan5:7"
IP_ADDRESS[10]="10.140.239.62"
SUBNET_MASK[10]="255.255.255.0"
BROADCAST_ADDRESS[10]="10.140.239.255"
INTERFACE_STATE[10]="up"
DHCP_ENABLE[10]="0"

I'm sorry, but I don't understand your requirements. It is obvious that we have a language barrier. Are you saying that you:

  1. want to find a line that contains lan5:5 ,
  2. find a digit string between [ and ] on that line, and then
  3. remove every line in the file that contains that digit string between [ and ] ?

Don,
>Are you saying that you:

  • yes, this is corrrect .
  1. want to find a line that contains lan5:5 ,
  2. find a digit string between [ and ] on that line, and then
  3. remove every line in the file that contains that digit string between [ and ] ?

Thank you..
Reveri.

I wonder if GNU 'sed -i' would work for you. Note that vi in : modes is ex, vi is visual ex, and both vi and ex drive OK in scripts. sed is a stream version of ex. sed -i makes a temp file and then swaps files. (sed ... old_file >/tmp/xrandom ; mv -f /tmp/xrandom old_file)

sed -i '/^INTERFACE_NAME\[8\]="lan5:5"$,/^INTERFACE_MODULES\[8\]=""$/d'

To delete by index takes two passes, one to find the index and another to remove those lines.

Hi DGPickett,
Thanks for the explanation...

I tried the sed -i on Suse Linix, but it did not work..

# sed -i '/^INTERFACE_NAME\[8\]="lan5:5"$,/^INTERFACE_MODULES\[8\]=""$/d' file
sed: -e expression #1, char 34: unknown command: `^'

Am i missing anything, Thanks..

OK. Doing exactly what you're requesting, I have an example that uses sed and ex. If you only need to delete lines that contain the digit string between [ and ] after finding lan5:5 , I also have an awk example that does that. The following script includes both of these examples. Hopefully, one or both of these will do what you want:

#!/bin/ksh
# Usage: tester file pattern
# For the example given in this thread, file is the name of the input file and
# pattern is lan5:5.
if [ $# -ne 2 ] || ! cp $1 backup$$
then    printf "Usage: %s file pattern\n" ${0##*/} >&2
        exit 1
fi
# Gather operands:
file="$1"
pattern="$2"
printf "A backup copy of \"%s\" has been saved as \"%s\"\n" "$file" backup$$

# Try 1st example using sed to create a pattern to match and ex to remove lines
# containing that pattern.
echo 'using sed and ex:'
# Set RE to a BRE that will match "[digits]" where digits is the value found on
# the line that contains the given pattern inside double quotes, and print that
# BRE for reference..
RE="$(sed -n "s/.*[[]\\([0-9]*\\)[]].*\"$pattern\".*/[[]\\1[]]/p" "$file")"
printf "RE is \"%s\"\n" "$RE"

# Use ex to delete every line from file that matches the computed RE and check
# the exit status:
if ex -s "$file" <<-EOF
        :g/$RE/d
        :wq
EOF
then    # ex complted successfully, verify that we got the expected results:
        if diff "$file" expected
        then    echo 'Succeeded'
        fi
else    echo 'ex or sed error or no match found'
fi

# Print the updated file:
cat "$file"

# Now try 2nd example using awk:
echo
echo 'using awk:'

# Restore the given file to its original contents
cp backup$$ "$file"

if awk -v pat="\"$pattern\"" '
$0 ~ pat {
        # We found a line that contains the given pattern inside double quotes.
        if(match($0, /[[][0-9]+[]]/)) {
                # And we found a digit string between "[" an "]", create a
                # pattern to match that digit string and the "[" before it and
                # the "]" after it
                RE = "[[]" substr($0, RSTART + 1, RLENGTH - 2) "[]]"
                # Note that we have found the pattern and created an ERE to
                # match the correponding digit string in square brackets:
                found = 1       # Note that we have found the pattern and
                                # created an ERE to match the digit string.
        }
}
!found || $0 !~ RE      # print the current line if we have not found the
                        # pattern or have found the pattern but this line does
                        # not match the ERE.
END {   exit !found     # Set exit status: 0->success, 1->pattern not found
}' "$file" > _tmp$$
then    # awk completed successfully, cp results back to the input file and
        # remove the temp file:
        cp _tmp$$ "$file"
        rm _tmp$$
else    echo "awk failed or no match found"
fi
# Verify that we got what we expected:
if diff "$file" expected
then    # Success, print the updated file.
        echo 'Succeeded'
        cat "$file"
fi
if [ -f _tmp$$ ]
then    # awk failed, print the contents of our temp file:
        echo _tmp$$
        cat "_tmp$$"
fi

I use the Korn shell, but this will also work with bash or any other POSIX conforming shell. If you are using a Solaris/SunOS system, use /usr/xpg4/bin/awk , /usr/xpg6/bin/awk , or nawk instead of awk .

If you save the above script in a file named tester , make it executable by running the command:

chmod +x tester

and then run the command:

./tester file "lan5:5"

(assuming that the input file you showed us in message #1 in this thread is stored in a file named file and the expected output shown in that message is stored in a file named expected ), the output produced will be something like:

A backup copy of "file" has been saved as "backup94188"
using sed and ex:
RE is "[[]8[]]"
Succeeded
BROADCAST_ADDRESS[5]="10.140.236.255"
INTERFACE_STATE[5]="up"
DHCP_ENABLE[5]="0"
INTERFACE_MODULES[5]=""
ROUTE_DESTINATION[2]=default
ROUTE_GATEWAY[2]=10.40.118.4
ROUTE_COUNT[2]=1
INTERFACE_NAME[10]="lan5:7"
IP_ADDRESS[10]="10.140.239.62"
SUBNET_MASK[10]="255.255.255.0"
BROADCAST_ADDRESS[10]="10.140.239.255"
INTERFACE_STATE[10]="up"
DHCP_ENABLE[10]="0"

using awk:
Succeeded
BROADCAST_ADDRESS[5]="10.140.236.255"
INTERFACE_STATE[5]="up"
DHCP_ENABLE[5]="0"
INTERFACE_MODULES[5]=""
ROUTE_DESTINATION[2]=default
ROUTE_GATEWAY[2]=10.40.118.4
ROUTE_COUNT[2]=1
INTERFACE_NAME[10]="lan5:7"
IP_ADDRESS[10]="10.140.239.62"
SUBNET_MASK[10]="255.255.255.0"
BROADCAST_ADDRESS[10]="10.140.239.255"
INTERFACE_STATE[10]="up"
DHCP_ENABLE[10]="0"

The number after "backup" in the output shown above in orange, is the process ID of the shell running this script.

If you then copy the backup file created by this script back to your input file and run it again with something like:

./tester file "lan5:x"

it will display error messages indicating that it couldn't find lan5:x , show the updated input file produced by sed and ex, show the diffs between what awk script produced and what you wanted, and show the temp file created by awk.

If everything works as expected, remove the backup file after you have verified that your input file has been updated to contain what you wanted.

If the awk script fails, it may also leave around a file named _tmpdigits where digits is also the process ID of the shell running this script. If it was an actual awk error (instead of not finding the given pattern in your input file), this temp file may help you debug what went wrong.

I hope the comments in the script explain what is going on, but if part of it doesn't make sense, feel free to ask for an explanation of the parts you don't understand.

Hope this helps...

Forgot / after first $. Sorry!

The index search is similar:

for dix in $(
 sed -n 's/^INTERFACE_NAME\[\([0-9]\{1,9\}\)\]="lan5:5"$/\1/p' yourfile
 )
do
 sed -i '/\['"$idx"'\]/d' yourfile
done

Narrative: sed no auto print mode find the key line and cut out the digits inside the square braces and print them to the for loop, which for each index found, removes all the lines indexed by that number in the file. Normally I use a while read, but since we are changing the file we are getting indexes from, I get all indexes before doing index deletes.

1 Like