LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   advice required to do a replace task based on input criteria (https://www.linuxquestions.org/questions/programming-9/advice-required-to-do-a-replace-task-based-on-input-criteria-923994/)

contra04 01-16-2012 01:32 AM

advice required to do a replace task based on input criteria
 
Centos.
I have a load of config files - cisco configs actually. I need to change the interface names per device. There are 30 configs for each device, however the changes I need to make are the same despite the differences in the configs. I though Ill try and get soem help scripting this !

So if I have R1, R2... etc I need to perform some replace text on a number of lines based on a set of rules that I define. I am not sure wether to use a script that has a load of sed and case statements? - not sure how to do case in bash, or should I try to learn perl for this task?

E.g for an input file named R1 do this:

case: input filename = R1
S1/0/1 replace with S1/0
FastEthernet 0/0 replace with Ethernet 0/0

case: input filename = R2
S1/0/1 replace with S1/2
GigabitEthernet 0/0 replace with Ethernet 0/0

etc..

Nominal Animal 01-16-2012 03:27 AM

Perhaps something like this in Bash:
Code:

for OUT in R[1-9]* ; do
    IN="$OUT.old"

    # Rename old file, keeping it as a backup (and input!)
    mv -f "$OUT" "$IN" || exit $?

    # Index is the file name, without starting R
    INDEX="${OUT#R}"

    # List of sed patterns (arguments) to apply.
    regex=()

    # Change S1/0/1 to S1/$INDEX
    regex=("${regex[@]}" -e "s|S1/0/1|S1/$INDEX|g")

    # Replace (something)Ethernet with Ethernet
    regex=("${regex[@]}" -e "s|[A-Za-z]*Ethernet|Ethernet|g")

    # Apply.
    sed "${regex[@]}" "$IN" > "$OUT" || exit $?

    # Make sure new file has the same owner and mode
    chown --reference="$IN" "$OUT" &>/dev/null
    chmod --reference="$IN" "$OUT" &>/dev/null
done

Obviously if you have some expressions that are only applied to specific files, you could easily add a case statement adding such, i.e. just before applying the sed command, add
Code:

    case "$OUT" in
    R7)  regex=("${regex[@]}" -e "s|foo|bar|g") ;;
    R11) regex=("${regex[@]}" -e "s|baz|foo|g") ;;
    esac

or, if the regex sets overlap -- remember that only one case is selected and applied in a case statement in Bash --, perhaps
Code:

    if [ "$OUT" = "R7" ] || [ "$OUT" = "R13" ]; then
        regex=("${regex[@]}" -e "s|foo|bar|g")
    fi
    if [ "$OUT" = "R11" ] || [ "$OUT" = "R13" ]; then
        regex=("${regex[@]}" -e "s|baz|foo|g")
    fi

Hope this helps,

contra04 01-16-2012 06:32 AM

Hi thank you very much for the prompt reply - I will give this a try now. I'll write a set of psuedo rules and then convert them.

So what kind of variable is regex() ? I am terrible at bash scripting.

Nominal Animal 01-16-2012 07:22 AM

Quote:

Originally Posted by contra04 (Post 4575781)
So what kind of variable is regex() ?

regex is an array variable. See Arrays chapter in the the Bash Reference Manual for details.

As an example,
Code:

array=('first thing' foo 'bar' *.jpeg)
ls -1 "${array[@]}"

Code:

array=()
array=("${array[@]}" 'first thing')
array=("${array[@]}" foo)
array=("${array[@]}" 'bar')
array=("${array[@]}" *.jpeg)
ls -1 "${array[@]}"

Code:

ls -1 'first thing' foo 'bar' *.jpeg
all run ls with the exact same parameters.

contra04 01-16-2012 02:41 PM

# \bin\bash
for OUT in r[1-6]* ; do
IN="$OUT.old"
mv -f "$OUT" "$IN" || exit $?
regex=()
if [ "$OUT" = "r1.txt" ] || [ "$OUT" = "r2.txt" ];then
regex=("${regex[@]}" -e "s|interface Serial 0\/0|interface Serial 1\/0|g")
#r1.txt
#interface Serial 0/0 --> interface Serial 1/0
#interface Serial 0/0.1 --> interface Serial 1/0.1
fi
#r2.txt
#interface Serial 0/0 --> interface Serial 1/0
#interface Serial 0/0.1 --> interface Serial 1/0.1
if [ "$OUT" = "r3.txt" ];then
regex=("${regex[@]}" -e "s|interface FastEthernet0\/0|interface Ethernet0\/0|g")
regex=("${regex[@]}" -e "s|interface FastEthernet0\/1|interface Ethernet0\/1|g")
fi

#r3.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1

if [ "$OUT" = "r4.txt" ] || [ "$OUT" = "r5.txt" ];then
regex=("${regex[@]}" -e "s|interface FastEthernet0\/0|interface Ethernet0\/0|g")
regex=("${regex[@]}" -e "s|interface FastEthernet0\/1|interface Ethernet0\/1|g")
regex=("${regex[@]}" -e "s|interface Serial 0\/0\/0|interface Serial 1\/0|g")
fi

#r4.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1
#interface Serial0/0/0 --> interface Serial1/0
#interface Serial0/0/0.1 point-to-point --> interface Serial1/0.1 point-to-point

#r5.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1
#interface Serial0/0/0 --> interface Serial1/0
#interface Serial0/1/0 --> interface Serial1/1

if [ "$OUT" = "r6.txt" ] ;then
regex=("${regex[@]}" -e "s|interface FastEthernet0\/0|interface Ethernet0\/0|g")
regex=("${regex[@]}" -e "s|interface Serial 0\/0\/0|interface Serial 1\/0|g")
fi

#r6.txt
#interface Serial0/0/0 --> interface Serial1/0
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/0.67 --> interface Ethernet0/0.67
#interface FastEthernet0/0.146 --> interface Ethernet0/0.146
echo $IN
echo $OUT
# Apply.
sed "${regex[@]}" "$IN" > "$OUT" || exit $?
chown --reference="$IN" "$OUT" &>/dev/null
chmod --reference="$IN" "$OUT" &>/dev/null
done


This is what I have so far and the changes I need to make in the comments. I figure that changing the subinterfaces will be covered by the global replace, e.g. 0/0 -> 1/0 and 0/0.1 -> 1/0.1 as the 0/0 and the 1/0 are the beginnings of the other. Was the escaping correct for the forward slashes?

also what is the $? character?

There is 20 different configs for each router, but the interface changes are always the same, so it would be great to bea able to send them through the script before pasting them onto the routers.

contra04 01-16-2012 03:49 PM

Ok awesome that worked now the last piece of the puzzle. Sed is just a monster to understand

I am trying to add a new line after every occurence of the "interface Ethernet0/*" the text in the new line sets the interfaces to full duplex.

so

interface Ethernet0/0

would become:

interface Ethernet0/0
Duplex Full

as would

interface Ethernet0/1

become


interface Ethernet0/1
duplex full

so far I have a mash up:

sed -e |interface Ethernet 0\/0.*|/a\Duplex Full|g r7.txt

any ideas on the syntax of this one?

Nominal Animal 01-17-2012 02:48 AM

Please use [CODE][/CODE] tags around code and exact strings. It keeps indentation intact, and makes it much easier to read.

Quote:

Originally Posted by contra04 (Post 4576155)
regex=("${regex[@]}" -e "s|interface Serial 0\/0|interface Serial 1\/0|g")

No need to escape slashes, as the separator character is | . (At least GNU sed does not mind an extra backslash at all, so your version would work with it at least. To adhere to standards, drop the backslash there.)

These two sed commands do the exact same thing:
Code:

s|foo/bar|baz|g
s/foo\/bar/baz/g

The backslash is only necessary if the character would be interpreted differently otherwise.

Quote:

Originally Posted by contra04 (Post 4576155)
also what is the $? character?

Each command has an exit status code. If the command is successful, the status is 0. If the command fails, the status is nonzero. $? is the status from the last command.

This also means that
Code:

some-command...
if [ $? -eq 0 ]; then
    echo "Successful"
else
    echo "Failed"
fi

if some-command... ; then
    echo "Successful"
else
    echo "Failed"
fi

both do and test the exact same thing: whether some-command... exit status was zero/success or nonzero/error.

Additionally,
Code:

some-command...
if [ $? -ne 0 ]; then
    exit $?
fi

if ! some-command... ; then
    exit $?
fi

some-command... || exit $?

all three do the same thing: they run some-command... and abort the script (using the same exit status as the command) if it failed, i.e. returned a non-zero exit status.

Quote:

Originally Posted by contra04 (Post 4576227)
so far I have a mash up:
Code:

sed -e |interface Ethernet 0\/0.*|/a\Duplex Full|g r7.txt
any ideas on the syntax of this one?

How about
Code:

-e '/^[\t\v\f\r ]*interface[\t\v\f\r ]/ a\\Duplex full'
This one matches any line that begins with optional whitespace, word "interface", and a whitespace character, and inserts "Duplex full" before the next line. The interface line itself is not edited, it is kept intact; it's just used to find the spot where to add the new line.

Note that you seem to be having some issues with proper quoting. I recommend you take a look at the Quoting chapter in the Bash Reference Manual; the first three subsections (Escape Character, Single Quotes, and Double Quotes) are the most important.

Most of the problems I've seen in shell scripts stem for incorrect/bad quoting. You really should learn it first, and apply it always. I recommend "defensive quoting", i.e. using the strictest form of quoting that will work, even when quoting might not be technically required, and it has served me extremely well.

The most basic rules are actually quite simple:
  • Remember that the strings you write will be interpreted by the shell first. They're only given to the actual commands after the shell has applied its own magic first.
  • Single quotes '...' tell the shell to keep the content intact, not apply its magic to the contents. The single quote characters themselves are not given to the command, but whatever is between them is.
  • Double quotes "..." tell the shell to apply its magic to the contents, but keep the result as a single string, as a single parameter to the command.
    The shell will interpret escapes like \" and variable references like $foo and so on.
    Expressions "$@" and "${array[@]}" are exceptions to this rule. They expand the lists (positional parameters for the first, array items for the second) as individual parameters.
  • Omitting quotes tells the shell to split the words into separate parameters.
    Details depend on shell options, though.
  • Quotes can be joined. "a"'b'cde"f" is the same as 'abcdef' and abcdef .
    This is useful if you want to insert a variable into a complex string, such as a sed pattern.
    'this is the fixed string part'"$variable"'the fixed string part continues' is a single string to the shell.
The magic that the shell applies in different situations is described in Shell Expansions chapter in the Bash Reference Quide. It's a bit heavy chapter, but knowing the stuff in it does make script writing a whole lot easier. If you haven't found my replies too tedious to read, the chapters in the manual I've referred to here should be pretty easy to understand ;)

contra04 01-17-2012 03:38 PM

I only need full duplex on 10meg ethernet interfaces, as 100 meg fast enthernet auto detects the correct speed. This is the one I got working in the end. It must look like spaghetti!

Code:


# \bin\bash
for OUT in r[1-6]* ; do
        IN="$OUT.old"
mv -f "$OUT" "$IN"  || exit $?
regex=()
echo $OUT
if [ "$OUT" = "r1.txt" ] || [ "$OUT" = "r2.txt" ];then
regex=("${regex[@]}" -e "s|interface Serial0/|interface Serial1/|g")
#r1.txt
#interface Serial 0/0 --> interface Serial 1/0
#interface Serial 0/0.1 --> interface Serial 1/0.1
fi
#r2.txt
#interface Serial 0/0 --> interface Serial 1/0
#interface Serial 0/0.1 --> interface Serial 1/0.1
if [ "$OUT" = "r3.txt" ];then
regex=("${regex[@]}" -e "s|interface FastEthernet0/0|interface Ethernet0/0|g")
regex=("${regex[@]}" -e "s|interface FastEthernet0/1|interface Ethernet0/1|g")
fi

#r3.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1

if [ "$OUT" = "r4.txt" ] || [ "$OUT" = "r5.txt" ];then
regex=("${regex[@]}" -e "s|interface FastEthernet0/0|interface Ethernet0/0|g")
regex=("${regex[@]}" -e "s|interface FastEthernet0/1|interface Ethernet0/1|g")
regex=("${regex[@]}" -e "s|interface Serial0/0/0|interface Serial1/0|g")
regex=("${regex[@]}" -e "s|interface Serial0/1/0|interface Serial1/1|g")
fi

#r4.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1
#interface Serial0/0/0 --> interface Serial1/0
#interface Serial0/0/0.1 point-to-point --> interface Serial1/0.1 point-to-point

#r5.txt
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/1 --> interface Ethernet0/1
#interface Serial0/0/0 --> interface Serial1/0
#interface Serial0/1/0 --> interface Serial1/1

if [ "$OUT" = "r6.txt" ] ;then
regex=("${regex[@]}" -e "s|interface FastEthernet0/0|interface Ethernet0/0|g")
regex=("${regex[@]}" -e "s|interface Serial0/0/0|interface Serial1/0|g")
fi
regex=("${regex[@]}" -e ' / Ethernet/ a\\ duplex full')
#r6.txt
#interface Serial0/0/0 --> interface Serial1/0
#interface FastEthernet0/0 --> interface Ethernet0/0
#interface FastEthernet0/0.67 --> interface Ethernet0/0.67
#interface FastEthernet0/0.146 --> interface Ethernet0/0.14

# Apply.
sed "${regex[@]}" "$IN" > "$OUT" || exit $?
chown --reference="$IN" "$OUT" &>/dev/null
chmod --reference="$IN" "$OUT" &>/dev/null
done

I guess I have a lot of reading to do, well got alot of configs to do a work for a xymon server so I guess Ill have a good reason to sharpen up.
Thank you for all of your help.


All times are GMT -5. The time now is 11:39 AM.