Подтвердить что ты не робот

Какой самый элегантный способ добавить Redis в/etc/services?

Итак, сегодня, 23 апреля 2015 года, авторитет Internet Assigned Numbers постановил использовать порт 6379 для Redis, правда, действительно!

Я хочу, чтобы com · mem · o · оценил этот великолепный случай, добавив следующую строку в мой файл /etc/services:

redis     6379/tcp

Каким будет лучший способ? К лучшему я имею в виду, конечно, следующее:

  • Разумеется, новая строка должна быть вставлена ​​в нужное место (например, в блоке Assigned Numbers, сразу после gnutella-rtr 6347/udp в моей системе).
  • Я рассмотрел использование различных текстовых редакторов, но он чувствует себя неуместно.
  • В идеале решение должно быть одноразовым однострочным экземпляром
  • Я могу представить себе awk script, который мог бы это сделать, но я ищу что-то большее, определенную je ne sais quoi

Обновить предложение re @Markus sed: я боюсь, что проблема будет применяться к этому "патчу" в других системах, которые не обязательно должны иметь один и тот же файл /etc/services, поэтому, расширяя в пункте № 1 выше, решение должно гарантировать, что, несмотря на специфику предшествующей услуги в файле, порядок сохраняется.

Обновление 2: несколько моментов, которые кажутся важными для состояния - а), хотя и не обязательные, длина решения (или отсутствие довольно), безусловно, является важной частью его элегантности (аналогично для внешних зависимостей (т.е. их отсутствия)); б) Мы предположили, что /etc/services сортируется, но было бы интересно посмотреть, что произойдет, когда это не так; c) предположим, что у вас есть права root и будьте осторожны с этой командой rm / -rf.

4b9b3361

Ответ 1

Сортировка одной строки, которая помещает ее в нужное место:

echo -e "redis\t\t6379/tcp" | sort -k2 -n -o /etc/services -m - /etc/services

Ответ 2

Ничто в правилах не отвечает на мой собственный вопрос - этот использует исключительно Redis:

cat /etc/services | redis-cli -x SET services; redis-cli --raw EVAL 'local s = redis.call("GET", KEYS[1]) local b, p, name, port, proto; p = 1 repeat b, p, name, port, proto = string.find(s, "([%a%w\-]*)%s*(%d+)/(%w+)", p) if (p and tonumber(port) > tonumber(ARGV[2])) then s = string.sub(s, 1, b-1) .. ARGV[1] .. "\t\t" .. ARGV[2] .. "/" .. ARGV[3] .. "\t\t\t# " .. ARGV[4] .. "\n" .. string.sub(s, b, string.len(s)) return s end until not(p)' 1 services redis 6379 tcp "remote dictionary server" > /etc/services

Отформатированный код Lua:

local s = redis.call("GET", KEYS[1])
local b, p, name, port, proto

p = 1
repeat
    b, p, name, port, proto = string.find(s, "([%a%w\-]*)%s*(%d+)/(%w+)", p)
    if (p and tonumber(port) > tonumber(ARGV[2])) then
        s = string.sub(s, 1, b-1) .. ARGV[1] .. "\t\t" .. ARGV[2] 
        .. "/" .. ARGV[3] .. "\t\t\t# " .. ARGV[4] .. "\n" 
        .. string.sub(s, b, string.len(s))
        return s
    end 
until not(p)

Примечание: подобный вызов (https://gist.github.com/jorin-vogel/2e43ffa981a97bc17259#comment-1440996) вдохновил этот ответ. Я выбрал чистый подход Lua script вместо использования Сортированных наборов... хотя я мог:)

Ответ 3

Как насчет python script, Itamar?

Он работает над понятием извлечения номера порта (называемого индексом в коде), и если мы выше 6378, но еще не напечатали нашу линию Redis, напечатаем его, а затем отметьте, что это верно, и просто напечатайте все строки ( включая тот, на котором мы находимся) после.

#!/usr/bin/python
lines = open("/etc/services").readlines()
printed=False
for line in lines:
    if printed:
        print line.rstrip()
        continue
    datafields = line.split()
    if line[0] == "#":
        print line.rstrip()
    else:
        datafields = line.split()
        try:
            try:
                index,proto = datafields[1].split("/")
                index = int(index)
            except:
                index,proto = datafields[0].split("/")
                index = int(index)
            if index > 6378:
                if not printed:
                    print "redis           6379/tcp    #Redis DSS"
                    printed = True
            print line.rstrip()
        except:
            print datafields
            raise

Соответствующий раздел моего файла для сравнения:

gnutella-rtr    6347/tcp    # gnutella-rtr
#                          Serguei Osokine <[email protected]>
#               6348-6381  Unassigned
redis           6379/tcp    #Redis DSS
metatude-mds    6382/udp    # Metatude Dialogue Server
metatude-mds    6382/tcp    # Metatude Dialogue Server

Обратите внимание на строку выше. Redis - это диапазон. Если не считать разрыва диапазона, это будет приемлемым решением для меня. Вы можете сломать диапазон, но IMO это работает отлично. Разделение диапазона кажется немного большим для простого, элегантного script. Особенно учитывая, что большинство файлов служб не имеют перечисленных ненаписанных диапазонов (это относится к OS X) - и что они все равно в комментариях.

UPDATE

Если вам не нужен локальный файл и он комментирует это, вы получаете все назначенные в настоящее время порты, которые не зарезервированы или не отменены: curl -s http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv| awk -F',' '$4!~/(Discard|Unassigned|Reserved)/ && $1 && $2+0>0 && $1!~/FIX/ {printf "%-16s\t%s/%s\t#%s\n", $1,$2,$3,$4}' > /etc/services

Тест FIX заключается в том, что некоторые из этих строк имеют встроенные новые строки, что может быть болью в awk.

Ответ 4

Идемпотентный однострочный awk, который вставляет 6379 в порядке, будет:

awk -v inserted=0 '/^[a-z]/ { if ($2 + 0 == 6379) { inserted=1 }; if (inserted == 0 && $2 + 0 > 6379) { print "redis\t\t6379/tcp"; inserted=1 }; print $0 }' /etc/services > /tmp/services && mv /tmp/services /etc/services

Ответ 5

Реальные мужчины не используют sed/awk:)

TMP_SERVICES=/tmp/services.$RANDOM

while read line
do
  printf %b "$line\n" >> $TMP_SERVICES
  if [[ $line == *"6347/udp"* ]]; then
    printf %b "redis\t\t6379/tcp\n" >> $TMP_SERVICES
  fi
done<"/etc/services"

mv -fb $TMP_SERVICES /etc/services

Ответ 6

sed -i '/6347\/udp/a redis     6379\/tcp' /etc/services

Удачи!

Обновление

sed -i '/6347\/udp/a redis \t\t 6379\/tcp' /etc/services

выглядит лучше...

обновление 2

лол

sed -i ''"$(echo $(echo $(grep -n $(awk {'print$2'} /etc/services | awk -F "/" '$1<6379'{'print$1'} | tail -1) /etc/services | awk -F ':' {'print$1'}|tail -1) + 1)|bc)"'i redis \t\t 6379\/tcp' /etc/services

Ответ 7

Мне нравится внутренняя подстановка sed, поэтому я совмещаю ее с awk-поиском следующей строки в services

R="redis\t\t6379/tcp\t\t\t# data structure server" N=$(awk '{if($2+0 == 6379) exit(1);if ($2+0 > 6379) {print $0;exit(0)}}' /etc/services) && sed -i "s~$N~$R\n$N~" /etc/services