запомнить совпадение в какой-то предыдущей строке и вывести его только один раз и только в том случае, если в последовательной строке есть совпадение с sed - PullRequest
1 голос
/ 21 мая 2019

Sed magic хотел:

Я задавался вопросом, как сохранить / запомнить совпадение в строке (например, часть заголовка) без немедленной печати

и распечатать его, только если какое-то другое совпадениечто-то еще находится где-то в следующей строке,

, но сохраненная / запомненная часть заголовка должна быть напечатана только один раз для любого числа следующих других совпадений под этим заголовком

и не должна быть напечатанавообще, если нет совпадения другой вещи в любой следующей строке под запомненной строкой заголовка

, например, для ifconfig

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
    inet 127.0.0.1 netmask 0xff000000
    inet6 ::1 prefixlen 128
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
    inet 127.94.0.2 netmask 0xff000000
    inet 127.94.0.1 netmask 0xff000000
    nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
XHC20: flags=0<> mtu 0
en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
    ether 68:5b:35:c1:b3:91
    inet6 fe80::8ef:5953:53b:7058%en3 prefixlen 64 secured scopeid 0x5
    inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255
    inet6 2a02:810d:9c0:59bb:c0d:c8af:7e27:42f1 prefixlen 64 autoconf secured
    inet6 2a02:810d:9c0:59bb:643f:a2cb:ac5f:7c71 prefixlen 64 autoconf temporary
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (1000baseT <full-duplex,flow-control,energy-efficient-ethernet>)
    status: active
en0: flags=8823<UP,BROADCAST,SMART,SIMPLEX,MULTICAST> mtu 1500
    ether 6c:40:08:9c:45:ce
    nd6 options=201<PERFORMNUD,DAD>
    media: autoselect (<unknown type>)
    status: inactive

с ifconfig | gsed -n -E '/^[a-z0-9]*:/h; /\tinet (addr:)?[0-9.a-fA-F:]*/{x;p;x;p}'

Я получаю (что уже довольно здорово, но не очень):

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.0.0.1 netmask 0xff000000
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.94.0.2 netmask 0xff000000
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
    inet 127.94.0.1 netmask 0xff000000
en3: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
    inet 192.168.0.2 netmask 0xffffff00 broadcast 192.168.0.255

но я хочу получить заголовки только один раз для всех его ips в результате (и без помех)

lo0:
    inet 127.0.0.1
    inet 127.94.0.2
    inet 127.94.0.1
en3:
    inet 192.168.0.2

Ответы [ 5 ]

2 голосов
/ 21 мая 2019

Не могли бы вы попробовать один раз.

Your_command | awk '
/^[a-zA-Z]+/{
  count=""
  val=$1
  next
}
val && match($0,/.*[0-9]+\.[-0-9]+\.[0-9]+\.[0-9]+ +/){
  if(++count==1){
    print val
  }
  value=substr($0,RSTART,RLENGTH)
  sub(/ +$/,"",value)
  print value
  value=""
}
' 
1 голос
/ 21 мая 2019

У меня есть два предложения для вас:

  1. При очистке строк ввода иногда проще удалить, чем выделить.
  2. В этом случае легче собрать конечный результат в удерживающем пространстве.

например:.

parse.sed

/^[^ ]+/ {        # For lines starting with non-space
  x               # \
  /\n/p           #  Did we collect any IP addresses?
  x               # /

  s/ .*//         # Clean up interface name
  h               # Overwrite hold-space
}

/inet / {
  s/ netmask.*//
  H               # Collect ip addresses in hold-space
}

$ {
  x               # Print the last interface if
  /\n/p           # it contained IP addresses
}

Запустите его так:

ifconfig | gsed -nEf parse.sed

Выход:

lo0:
    inet 127.0.0.1
    inet 127.94.0.2
    inet 127.94.0.1
en3:
    inet 192.168.0.2
1 голос
/ 21 мая 2019

так что я понял, как это сделать за последнюю ночь:)

ifconfig | gsed -n -E '
/^[a-z0-9]*:/ {                     # for lines starting with this
    s/^([a-z0-9]*:).*/\1/;h         # extract the start and put it into hold space
}
/\tinet (addr:)?[0-9.a-fA-F:]*/ {   # for lines containing this
    s/^.*(\tinet (addr:)?[0-9.a-fA-F:]*).*/\1/    # extract it
    x                               # swap hold space and pattern space
    G                               # and then append the hold space (former pattern space) to it 
    s/^\n//                         # replace leading \n if former hold space was empty
    p                               # print the concatenated former hold space and modified pattern space
    s/^.*$//                        # empty the pattern space
    x                               # swap the now empty pattern space to hold space
}
'

или как (более или менее) однострочник:

ifconfig | gsed -n -E '
/^[a-z0-9]*:/                   { s/^([a-z0-9]*:).*/\1/;h };
/\tinet (addr:)?[0-9.a-fA-F:]*/ { s/^.*(\tinet (addr:)?[0-9.a-fA-F:]*).*/\1/;x;G;s/^\n//;p;s/^.*$//;x}'

дает мнежелаемое для

lo0:
    inet 127.0.0.1
    inet 127.94.0.2
    inet 127.94.0.1
en3:
    inet 192.168.0.2
0 голосов
/ 22 мая 2019

Это может сработать для вас (GNU sed):

sed -En '/^\S/h;s/^(\s+inet\s+\S+).*/\1/;T;x;s/\s.*//p;x;p' file

Сохранить строку заголовка в поле удержания.

Удалите все, что следует за ip-адресом для строк, содержащих inet.

Если замена не удалась, выручите.В противном случае вернитесь в область удержания и попробуйте удалить конец строки заголовка и распечатать его.

Вернитесь в пространство шаблона и напечатайте строку исходного IP-адреса.

NB Заголовок будетбудет напечатан только один раз, потому что подстановка завершится неудачей, когда строка уже отредактирована.

0 голосов
/ 21 мая 2019

sed предназначен для выполнения простых s/old/new, , то есть всего , для всего остального просто используйте awk:

$ ifconfig | awk '/^[[:alpha:]]/{hdr=$1 ORS} $1=="inet"{print hdr "    " $1, $2; hdr=""}'
lo0:
    inet 127.0.0.1
    inet 127.94.0.2
    inet 127.94.0.1
en3:
    inet 192.168.0.2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...