0

I have a file like this format:

a;b;c
e;d;f

how can I use shell to read the file detail information into variables? I would have 6 variables to store the data. more detailed information for this is that as the following shows: I have written a script:

#!/bin/sh

unset ret
ret=0

if [ "$#" -ne 3 ]; then
    logger -p err "Usage: $0 eth_name rule_file table_name"
    ret=1
    exit ret
fi

OFS=$IFS                        # store field separator
IFS=";"                         # define field separator
eth_name=$1                     # ethernet device name
rule_file=$2     # input file name
table_name=$3                   # lookup table name

logger -p notice "$0 $eth_name $rule_file $table_name"

unset a      # reference to line array
unset i j    # index
unset m n    # dimension

### read route configuration
i=0
while read line
do
  a=A$i
  unset $a
  declare -a $a='($line)'
  i=$((i+1))
done < $rule_file
# store number of lines
m=$i

# function for apply route
add_route()
{
    if [ "source" = "$1" ]; then
        src_address=$(ifconfig $eth_name | sed -n 's/.*inet addr:\([0-9.]\+\)\s.*/\1/p')
        ip rule add from $src_address lookup $table_name
        ret=$?
        logger -p notice "ip rule add from $src_address lookup $table_name $ret"
    elif [ "default" = "$1" ]; then
        ip route add default via $2 table $table_name
        ret=$?
        logger -p notice "ip route add default via $2 table $table_name $ret"
    else
        ipaddress_range=$1
        gateway_ipaddress=$2
        ip route add $ipaddress_range via $gateway_ipaddress dev $eth_name table $table_name
        ret=$?
        logger -p notice "ip route add $ipaddress_range via $gateway_ipaddress dev $eth_name table $table_name $ret"
    fi
}

### apply route configuration
for ((i=0; i < $m; i++))
do
  a=A$i
  # get line size
  # double escape '\\' for sub shell '``' and 'echo'
  p0=`eval echo \\${$a[0]}`
  p1=`eval echo \\${$a[1]}`
  add_route $p0 $p1
done

IFS=$OFS

the rule file's format is as the following shows:

source;
default_route;172.20.5.192/26
default_gateway;172.20.5.254
172.17.23.64/26;172.20.5.254
172.31.252.0/24;172.20.5.254
172.31.254.0/24;172.20.5.254
10.217.1.0/24;172.20.5.254
10.217.2.0/24;172.20.5.254

this script is working normally under the bash environment, now my linux system is not having bash now, this script is not working now, how to change the script to make the script running? the function for this script is very simple, write every line into the linux system's ip rule and ip route. need to throw 3 variables to make the script running.

run code
  • 11
  • 2
  • Show us what you've tried, and we'll help you figure it out. StackOverflow is about helping people fix their code. For something as basic as this, you should probably start with the man page for your shell. Test your code at https://www.shellcheck.net before posting. – ghoti Nov 07 '19 at 09:25
  • yes,thanks for your suggestion, this is my first time for StackOverflow usage. I will post my code later and try to ask more specifically. – run code Nov 08 '19 at 01:38
  • Thanks for your update, this looks much better! Can you also add some sample data and show the results you'd like to see based on that data? Your sample input is 6 fields on 2 lines, but your code appears to be processing 8 lines with no fields, and I don't know what `add_route` does. – ghoti Nov 08 '19 at 04:07
  • Also, note that your script starts with `#!/bin/sh`, which is not necessarily bash (and shouldn't behave like bash even if it is). The `declare -a` command is a bashism and will not work in a more strict POSIX shell. Can you tell us exactly what shell you are using? – ghoti Nov 08 '19 at 04:11
  • thanks for your suggestion, I will post the full code and explain it. – run code Nov 08 '19 at 05:49

2 Answers2

0

You can easily achieve it with read.

#!/bin/bash

while IFS=";" read -r var1 var2 var3; do
  command
done <file

exit 0

where:

  • IFS is your delimiter
  • varN are your vars, you can use any name you want

N.B. If you need stdin you need to use a file descriptor:

#!/bin/bash

exec 3<file
while IFS=";" read -r var1 var2 var3 <&3; do
  command
done
exec 3>&-

exit 0

Further readings here.

N.B.#2 The command read is not the faster solution, usually after 5k lines lose 100 ms compared to other tools (E.G. awk).

ingroxd
  • 995
  • 1
  • 12
  • 28
0

The following parses your original sample text and saves fields to sequentially numbered variables, stripping values out of lines using parameter expansion, and doesn't care how many fields you have per line.

#!/bin/sh

i=0

while read line; do
  while [ -n "$line" ]; do
    eval this_$((i=i+1))="\${line%%;*}"
    last="$line"
    line="${line#*;}"
    [ "$last" = "$line" ] && break
  done
done < input.txt

I've tested this successfully with both bash and FreeBSD's /bin/sh (which is based on ash). (Note that FreeBSD's /bin/sh doesn't seem to like arithmetic expressions like $((i++)), but both shells I tested are fine with the notation above.)

But from the look of the script you updated your question with, this isn't what you need. It seems that you have input data with:

  • one record per line, and
  • two fields, separated by semicolons.

But I wonder if you even need to store things in variables. It seems to me you'd be looking more for the following type of structure.

#!/bin/sh

...

while IFS=";" read range gateway; do
  case "$range" in
    source)
      : Note that $gateway is blank
      ;;
    default_route)
      : do something
      ;;
    default_gateway)
      : do something else
      ;;
    [0-9]*)
      ip route add "$range" via "$gateway" dev "$eth_name" table "$table_name"
      ;;
    *)
      printf 'ERROR: unknown range in rule file: %s\n' "$range" >&2
      ;;
  esac
done < $rule_file

Additional input validation wouldn't hurt.

ghoti
  • 45,319
  • 8
  • 65
  • 104
  • Hi ghoti,thanks for your feedback, actually based on my analysis, my bash does not support 'declare' function, so this means a=A$i will not have any values in it, do you have any idea how to change the declare function? – run code Nov 11 '19 at 06:18
  • @runcode .. That's very strange. Can you tell me the output of `echo $BASH_VERSION`? 5+ would be current, 3.2 would be on a Mac, but all of them support the `declare` built-in. Perhaps you're not actually running bash? (Note my comment on your question about `#!/bin/sh`.) – ghoti Nov 11 '19 at 14:54
  • finally I moved bash function into my embedded system.....it really drives me carzy, thanks for your great support. – run code Nov 12 '19 at 07:47