Ответ 1
Сортировка одной строки, которая помещает ее в нужное место:
echo -e "redis\t\t6379/tcp" | sort -k2 -n -o /etc/services -m - /etc/services
Итак, сегодня, 23 апреля 2015 года, авторитет Internet Assigned Numbers постановил использовать порт 6379 для Redis, правда, действительно!
Я хочу, чтобы com · mem · o · оценил этот великолепный случай, добавив следующую строку в мой файл /etc/services
:
redis 6379/tcp
Каким будет лучший способ? К лучшему я имею в виду, конечно, следующее:
gnutella-rtr 6347/udp
в моей системе).awk
script, который мог бы это сделать, но я ищу что-то большее, определенную je ne sais quoiОбновить предложение re @Markus sed
: я боюсь, что проблема будет применяться к этому "патчу" в других системах, которые не обязательно должны иметь один и тот же файл /etc/services
, поэтому, расширяя в пункте № 1 выше, решение должно гарантировать, что, несмотря на специфику предшествующей услуги в файле, порядок сохраняется.
Обновление 2: несколько моментов, которые кажутся важными для состояния - а), хотя и не обязательные, длина решения (или отсутствие довольно), безусловно, является важной частью его элегантности (аналогично для внешних зависимостей (т.е. их отсутствия)); б) Мы предположили, что /etc/services
сортируется, но было бы интересно посмотреть, что произойдет, когда это не так; c) предположим, что у вас есть права root и будьте осторожны с этой командой rm / -rf
.
Сортировка одной строки, которая помещает ее в нужное место:
echo -e "redis\t\t6379/tcp" | sort -k2 -n -o /etc/services -m - /etc/services
Ничто в правилах не отвечает на мой собственный вопрос - этот использует исключительно 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 вместо использования Сортированных наборов... хотя я мог:)
Как насчет 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) - и что они все равно в комментариях.
Если вам не нужен локальный файл и он комментирует это, вы получаете все назначенные в настоящее время порты, которые не зарезервированы или не отменены:
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.
Идемпотентный однострочный 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
Реальные мужчины не используют 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
sed -i '/6347\/udp/a redis 6379\/tcp' /etc/services
Удачи!
sed -i '/6347\/udp/a redis \t\t 6379\/tcp' /etc/services
выглядит лучше...
лол
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
Мне нравится внутренняя подстановка 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