3

I'm parsing output of avahi-browse tool and my script should be POSIX compatible.

I'm doing it next way:

      local _dnssd=`avahi-browse -apt`
      if [ -z "$_dnssd" ]; then
        echo "No info"
      else
        IFS='
    ' # it's new line character in IFS
        for _row in $_dnssd
         do  
          local _tmpIFP="$IFS"
          IFS=";"
            case "$_row" in
            ...
            esac
            IFS="$_tmpIFS"
        done
      fi  

I really don't like line with newline assignment to IFS. Is it possible to replace it in better way?

I tried some suggestions from stackoverflow, but it doesn't work:

IFS=$(echo -e '\n')

avahi-browse output:

+;br0;IPv4;switch4B66E4;_http._tcp;local
+;br0;IPv4;switch4B66E4;_csco-sb._tcp;local
Jurasic
  • 1,845
  • 1
  • 23
  • 29

3 Answers3

6

Add a space after \n in the IFS variable, then remove that space again:

IFS="$(printf '\n ')" && IFS="${IFS% }"
#IFS="$(printf '\n ')" && IFS="${IFS%?}"
printf '%s' "$IFS" | od -A n -c 
larz
  • 76
  • 1
2

It's better to use a while loop than trying to iterate over a string that contains the entire output.

avahi-browse -apt | while IFS=";" read field1 field2 ...; do
    case ... in
    ...
    esac
done

Note you should need one name per field for the read command. The ... is just a placeholder, not valid shell syntax for a variable number of fields.

This simply does nothing if the program produces no output. If you really need to detect that case, try

avahi-browse -apt | {
    read line || { echo "No info"; exit; }
    while : ; do
        IFS=";" read field1 field2 ... <<EOF
        $line
EOF
        case ... in
        ...
        esac
        read line || break
    done
}

In both cases, any variables set in the right-hand side of the pipe are local to that shell. If you need to set variables for later use, you'll need to make some further adjustments.

chepner
  • 497,756
  • 71
  • 530
  • 681
  • what if field could have spaces like =;eth0;IPv4;\039s\032remote\032desktop\032on\032O;VNC Remote Access;local;O.local;192.168.26.19;5900. After IFS=";" it would be impossible to detect whole field. – Jurasic Feb 19 '14 at 15:42
  • 1
    By setting `IFS` to a semicolon, the spaces are treated as part of a field, rather than as a field separator. – chepner Feb 19 '14 at 15:46
0

If one can rely* that IFS has its default value (space, tab, new line) then one can simply strip the first two characters (space and tab) and the new line character will remain:

IFS=${IFS#??}

*You can rely on it if IFS has not been modified by the script before and if it is a POSIX shell (as the topic implies):

The shell shall set IFS to <space><tab><newline> when it is invoked.

See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_03

linuxball
  • 187
  • 1
  • 4