Bash: Разделить переменную после первого появления буквенного символа

Краткий ответ:для ядра 4.4.0 TCP PACING включен по умолчанию и установлен на ~34,36 Гбит/с на сокет. Начиная с ядра 4.13.0 -rc1, TCP PACING по умолчанию отключен. В обоих случаях, поскольку TSO включен, TCP PACING не будет иметь никакого эффекта, если для частоты стимуляции установлено значение по умолчанию.

TCP PACING устанавливается для сокета с помощью функции sock _setsockopt ()(net/core/sock.c ). Флагом для этого является SO _MAX _PACING _RATE, а код обработки выглядит следующим образом:

case SO_MAX_PACING_RATE:
        sk->sk_max_pacing_rate = val;
        sk->sk_pacing_rate = min(sk->sk_pacing_rate,
                     sk->sk_max_pacing_rate);
        break;

При создании сокета соответствующие поля устанавливаются в ~0U в sock _init _data()(net/core/sock.c):

sk->sk_max_pacing_rate = ~0U;
sk->sk_pacing_rate = ~0U;

Таким образом, значениями по умолчанию для частоты стимуляции и максимальной частоты стимуляции является значение max unsigned int, которое составляет (2^32 -1 )или 4 294 967 295 байт в секунду, что составляет 34,36 Гбит/с.

Однако можно достичь более высоких скоростей даже с одним сокетом, используя разгрузку сегментации TCP (TSO ). TSO принимает во внимание частоту стимуляции,но на самом деле не ограничен скоростью по умолчанию. В tcp _tso _autosize ()function (net/ipv4/tcp _output.c )скорость синхронизации может уменьшить размер сеанса TSO, но поскольку значение по умолчанию ~4,3 ГБ, и даже после сдвига вправо на 10 вы все равно получите ~4 МБ, это намного больше, чем типичный сеанс TSO.

static u32 tcp_tso_autosize(const struct sock *sk, unsigned int mss_now)
{
    u32 bytes, segs;

    bytes = min(sk->sk_pacing_rate >> 10,
            sk->sk_gso_max_size - 1 - MAX_TCP_HEADER);

    /* Goal is to send at least one packet per ms,
     * not one big TSO packet every 100 ms.
     * This preserves ACK clocking and is consistent
     * with tcp_tso_should_defer() heuristic.
     */
    segs = max_t(u32, bytes / mss_now, sysctl_tcp_min_tso_segs);

    return min_t(u32, segs, sk->sk_gso_max_segs);
}

Я попытался на самом деле достичь ограничения в 34 Гбит/с, но, к сожалению, без загрузки ЦП TSO на стороне TX ограничение полосы пропускания одного сокета до ~23 Гбит/с.

Патч Эрика Думазе tcp :внутренняя реализация для стимуляции был представлен в ядре 4.13.0 -rc1. Этот патч расширяет возможности TCP PACING. В кои-то веки это позволяет выполнять стимуляцию без использования TC -FQ. Он также полностью отключает стимуляцию, если в поле скорости sk _pacing _установлено значение 0 или ~0U (по умолчанию ). См. обложку патча и код для более подробной информации.

0
08.11.2019, 11:41
5 ответов

Это не совсем то, что вам нужно, но должно подойти для вашего варианта использования. Считайте строку в шесть переменных, разделенных пробелом и символом табуляции, определяемым переменной IFS.

Пример с измененной записью crontab:

$ line='0   22  * * 1-5 echo "foo  bar   baz"'
$ IFS=$'\t ' read -r a b c d e cmd <<<"$line"
$ echo "$a $b $c $d $e"
0 22 * * 1-5
$ echo "$cmd"
echo "foo  bar   baz"

Обратите внимание, что несколько пробелов (или табуляции )в первых пяти полях сжимаются до одного символа пробела, но пробелы/табуляции в команде остаются нетронутыми. При необходимости вы можете присвоить "$a $b $c $d $e"одной переменной.

2
28.01.2020, 02:22

Я думаю, вы могли бы сделать это с помощью awk, в частности, поскольку ваши теги указывают, что вы хотите использовать это в сценарии оболочки. Взгляните на следующий фрагмент кода, где я, как и другие ответчики, ради аргумента назначу переменную bash с содержимым, которое вы, вероятно, обработаете:

#!/bin/bash

# fill 'line' with example content
line='0   22  * * 1-5 echo foo  bar   baz -o outfile.txt *'

# split 'line' into "scheduling" and "command" part:
SCHED=$(awk '{match($0, "^([0-9* -]*)([[:print:]]*)", a); print a[1]}' <<< "$line")
echo "$SCHED"
CMD=$(awk '{match($0, "^([0-9* -]*)([[:print:]]*)", a); print a[2]}' <<< "$line")
echo  "$CMD"

Если вы проверите это, он должен вывести

user@host $./split_test.sh 
0   22  * * 1-5 
echo foo  bar   baz -o outfile.txt *

Объяснить

  • В обеих строках, где выполняется фактическое «разделение», я присваиваю переменной с левой стороны вывод команды в $(... )части (, см., например. Advanced Bash Scripting Guide для примеров ), в котором я передаю содержимое переменной $line, защищенной от подстановки двойными круглыми скобками("... "' )до awk(, так что, среди прочего,белое -пространство сохраняется "как есть" ).
  • Вызов awkиспользует внутреннюю функцию match()для поиска определенных шаблонов в строке, в нашем случае это одна группа (... )в начале строки, содержащая любую комбинацию цифр, звездочек, пробелов и тире, до неограниченного количества, за которым следует группа, содержащая любые печатные символы, также до неограниченного количества. Поскольку регулярные выражения жадны, обеим частям будет соответствовать максимально длинная подстрока -, поэтому группа 1 действительно продолжается до первого символа, который не помещается в нее (, то есть до первого буквенно-цифрового символа или даже ./). ] находится. Вторая группа — это просто оставшаяся часть строки, которая может содержать все печатные символы, включая цифры и специальные символы.
  • Идея использования функции awk's matchзаключается в том, что если вы указали подгруппы -, заключив их в круглые скобки, вы можете указать awkзаполнить фактическое значение шаблонов в массив, в данном примере a, такой, что a[1]содержит фактическое значение группы 1 (часть «планирования» ), а a[2]фактическое значение группы 2 («команда часть ).
  • Хотя это немного не -элегантно, мы применяем это два раза, awkвыводя первую часть в первом вызове, а вторую часть во втором вызове, чтобы присвоить значения отдельным переменным bash..

Если вы хотите узнать больше об этом, вы можете взглянуть на Руководство пользователя GNU Awk .

0
28.01.2020, 02:22

В Crontab есть пять полей, описывающих время, остальные — ваши команды. См. здесь .

Итак, если вы хотите отделить поля времени (1 -5 )от команды, вам просто нужно разделить по 5-му разделителю полей.

sed "s/\s+/\n/5" <<<'1 2 3 * * echo hello * 7 8 9'
1 2 3 * *
echo hello * 7 8 9

Использование новой строки \nпозволяет читать две строки в массив без цикла

readarray cron <<< $(sed 's/ /\n/5' <<<'1 2 3 * * echo hello * 7 8 9')

Это зависит от того, что \nне появляется в качестве escape-последовательности или внутри строки в вашем crontab, хотя вы всегда можете объединить элементы cron[1..n]для повторной сборки команды, если это произойдет.

0
28.01.2020, 02:22

Вы можете использовать cut для разделения значений, разделенных пробелом, например:

#/bin/sh

var='0 22 * * 1-5 echo hello'

first=$(echo -n "$var" | cut -d' ' -f1)
second=$(echo -n "$var" | cut -d' ' -f2)
third=$(echo -n "$var" | cut -d' ' -f3)
fourth=$(echo -n "$var" | cut -d' ' -f4)
fifth=$(echo -n "$var" | cut -d' ' -f5)

restofvar=$(echo -n "$var" | cut -d' ' -f6,7)

E :Как было указано в комментарии, если в строке несколько пробелов, вы должны заменить их одним пробелом:

var=$(echo "$var" | tr -s " ")
0
28.01.2020, 02:22

Это можно сделать только с помощью Расширение параметров оболочки и Сопоставление шаблонов

$ line='0 22 * * 1-5 echo hello'
$ prefix=${line%%[[:alpha:]]*}     #(a)
$ echo "$prefix"
0 22 * * 1-5 
$ suffix=${line#"$prefix"}.       #(b)
$ echo "$suffix"
echo hello

a :с конца, найти самую длинную подстроку, соответствующую альфе, за которой следует любой символ, и удалить ее из $line

b :с самого начала удалите префикс test из $line.

1
28.01.2020, 02:22

Теги

Похожие вопросы