Что определяет максимальный размер для отдельного аргумента команды?

Способы проверить, пуста ли переменная:

# bash
if [[ -z $var ]]; then
        echo var is empty
fi

# POSIX sh
if [ -z "$var" ]; then
        echo var is empty
fi
printf %s\\n "${var:-var is unset or null}"
: "${var:=default value for var}"

Это все еще не решает проблему пропавших без вести или неправильно выровненных полей в Вашем входном файле. Если ORAUSER поле отсутствует ORAUSER переменной присвоят значение ROLE поле. Если существует слишком много полей, последняя переменная, ROLE, получит все дополнительные поля. Я рекомендовал бы переключить Ваш формат текстового файла для использования разделителя, который не является пробелом, так, чтобы пустые поля были явно разграничены. Например, /etc/passwd двоеточия использования (:).

Если бы Вы еще не имеете, я также рекомендовал бы читать BashFAQ 1 для некоторой коллективной мудрости при надлежащем парсинге текстовых файлов линию за линией / поле полем в сценариях оболочки и bash конкретно.

50
20.03.2014, 20:32
3 ответа

Ответы

  1. Определенно не ошибка.
  2. Параметр, определяющий максимальный размер одного аргумента - MAX_ARG_STRLEN. Для этого параметра нет никакой документации, кроме комментариев в binfmts.h:

    /*
     * Это максимальная длина и максимальное количество строк, передаваемых системному вызову
     * системному вызову execve(). MAX_ARG_STRLEN по сути является случайным, но служит для того, чтобы.
     * предотвращения чрезмерного воздействия на ядро из-за неправильно адресованных указателей.
     * MAX_ARG_STRINGS выбирается таким образом, чтобы поместиться в знаковое 32-битное целое число.
     */
    #define MAX_ARG_STRLEN (PAGE_SIZE * 32)
    #define MAX_ARG_STRINGS 0x7FFFFFFF
    

    Как показано, Linux также имеет (очень большое) ограничение на количество аргументов команды.

  3. Ограничение на размер одного аргумента (которое отличается от общего ограничения на аргументы плюс окружение), похоже, специфично для Linux. В этой статье приводится подробное сравнение ARG_MAX и эквивалентов в Unix-подобных системах. MAX_ARG_STRLEN обсуждается для Linux, но нет никаких упоминаний о каких-либо эквивалентах в других системах.

    В вышеприведённой статье также говорится, что MAX_ARG_STRLEN был введён в Linux 2.6.23, наряду с рядом других изменений, связанных с максимальными значениями командных аргументов (обсуждаются ниже). Журнал/дифф для фикса можно найти здесь.

  4. Пока неясно, чем объясняется дополнительное расхождение между результатом getconf ARG_MAX и реальным максимально возможным размером аргументов плюс окружение. Ответ Стефана Шазеласа предполагает, что часть пространства приходится на указатели на каждую из строк аргументов/окружения. Однако мое собственное расследование показывает, что эти указатели не создаются на ранней стадии системного вызова execve, когда он еще может вернуть вызывающему процессу ошибку E2BIG (хотя указатели на каждую строку argv, конечно, создаются позже).

    Кроме того, насколько я могу судить, строки смежны в памяти, поэтому здесь нет пробелов в памяти из-за выравнивания. Хотя очень вероятно, что это фактор, связанный с тем, что делает использование дополнительной памяти. Понимание того, что использует дополнительное пространство, требует более детального знания того, как ядро распределяет память (это полезное знание, которое я буду исследовать и обновлять позже).

ARG_MAX Confusion

Начиная с Linux 2.6.23 (в результате этого коммита), были внесены изменения в способ обработки максимальных значений командных аргументов, что отличает Linux от других Unix-подобных систем. В дополнение к добавлению MAX_ARG_STRLEN и MAX_ARG_STRINGS, результат getconf ARG_MAX теперь зависит от размера стека и может отличаться от ARG_MAX в limits.h.

Обычно результат getconf ARG_MAX будет равен 1/4 размера стека. Рассмотрим следующее в bash, используя ulimit для получения размера стека:

$ echo $(( $(ulimit -s)*1024 / 4 ))  # ulimit output in KiB
2097152
$ getconf ARG_MAX
2097152

Однако, вышеописанное поведение было немного изменено этим commit (добавлен в Linux 2.6.25-rc4~121). ARG_MAX в limits.h теперь служит жесткой нижней границей для результата getconf ARG_MAX. Если размер стека установлен так, что 1/4 размера стека меньше, чем ARG_MAX в limits.h, то limits. h будет использовано значение:

$ grep ARG_MAX /usr/include/linux/limits.h 
#define ARG_MAX       131072    /* # bytes of args + environ for exec() */
$ ulimit -s 256
$ echo $(( $(ulimit -s)*1024 / 4 ))
65536
$ getconf ARG_MAX
131072

Заметим также, что если размер стека установлен меньше минимально возможного ARG_MAX, то размер стека (RLIMIT_STACK) становится верхней границей размера аргумента/среды до возврата E2BIG (хотя getconf ARG_MAX все равно покажет значение в limits. h).

И последнее, что следует отметить: если ядро собрано без CONFIG_MMU (поддержка аппаратного управления памятью), то проверка ARG_MAX отключена, поэтому ограничение не применяется. Хотя MAX_ARG_STRLEN и MAX_ARG_STRINGS по-прежнему применяются.

Further Reading

52
27.01.2020, 19:33

Итак, @StephaneChazelas правильно меня поправляет в комментариях ниже - сама оболочка никоим образом не диктует максимальный размер аргумента, разрешенный вашей системой, а скорее он установлен вашим ядром.

Как уже говорили некоторые другие, кажется, что ядро ​​ограничивает 128 Кбайт максимальным размером аргумента, который вы можете передать новому процессу из любого другого при его первом запуске. Эта проблема возникает, в частности, из-за множества вложенных подоболочек $ (подстановка команд) , которые должны выполняться на месте и передавать весь свой вывод от одной к другой.

И это своего рода дикая догадка, но поскольку несоответствие ~ 5 КБ кажется настолько близким к стандартному размеру системной страницы, я подозреваю, что она предназначена для страницы, которую bash использует для обработки подоболочки. ваш $ (подстановка команд) требует, чтобы в конечном итоге доставил свой вывод и / или стек функций, который он использует для связывания вашей таблицы массива с вашими данными. Могу только предположить, что ни то, ни другое не дается бесплатно.

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

Для этого я в основном использовал трубы. Но я также оценил массив оболочки в здесь-документе , указывающем на стандартный ввод cat. Результаты ниже.

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

time bash <<-\CMD
    ( for arg in `seq 1 6533` ; do
        printf 'args+=(' ; printf b%.0b `seq 1 6533` ; echo ')'
    done ;
    for arg in `seq 1 6533` ; do
        printf %s\\n printf\ '%s\\n'\ \""\${args[$arg]}"\" ;
    done ) | . /dev/stdin >&2
CMD
bash <<<''  66.19s user 3.75s system 84% cpu 1:22.65 total

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

Я попытался изменить часть генератора printf во второй строке на:

printf \ b%.0b

Он также работает:

bash <<<''  123.78s user 5.42s system 91% cpu 2:20.53 total

Так что, может быть, я немного болен. Я использую заполнение нулями здесь и добавляю в предыдущем значение «$ arg» к текущему значению «$ arg» . Я выхожу за пределы 6500 ...

time bash <<-\CMD
    ( for arg in `seq 1 33` ; do
        echo $arg >&2
        printf 'args+=('"${args[$((a=arg-1))]}$(printf "%0${arg}0d" \
            `seq 1 6533` ; printf $((arg-1)))"')\n'
    done ;
    for arg in `seq 1 33` ; do
        printf '/usr/bin/cat <<HERE\n%s\nHERE\n' "\${args[$arg]}"
    done ) | . /dev/stdin >&2
CMD

bash <<<''  14.08s user 2.45s system 94% cpu 17.492 total

И если я изменю строку cat , чтобы она выглядела так:

printf '/usr/bin/cat <<HERE | { printf '$arg'\  ; wc -c ;}
    %s\nHERE\n' "\${args[$arg]}"

Я могу получить количество байтов из wc. Помните, что это размеры каждого ключа в массиве args . Общий размер массива - это сумма всех этих значений.

1 130662
2 195992
3 261322
4 326652
5 391982
6 457312
7 522642
8 587972
9 653302
10 718633
11 783963
12 849293
13 914623
14 979953
15 1045283
16 1110613
17 1175943
18 1241273
19 1306603
20 1371933
21 1437263
22 1502593
23 1567923
24 1633253
25 1698583
26 1763913
27 1829243
28 1894573
29 1959903
30 2025233
31 2090563
32 2155893
33 2221223
-1
27.01.2020, 19:33

В eglibc-2.18 / NEWS

* ARG_MAX is not anymore constant on Linux.  Use sysconf(_SC_ARG_MAX).
Implemented by Ulrich Drepper.

В eglibc-2.18 / debian / patches / kfreebsd / local-sysdeps.diff

+      case _SC_ARG_MAX:
+   request[0] = CTL_KERN;
+   request[1] = KERN_ARGMAX;
+   if (__sysctl(request, 2, &value, &len, NULL, 0) == -1)
+       return ARG_MAX;
+   return (long)value;

В linux / include / uapi / linux / limits.h

#define ARG_MAX       131072    /* # bytes of args + environ for exec() */

И 131072 - это ваш $ (getconf ARG_MAX) / 16-1 , возможно, вам следует начать с 0.

] Вы имеете дело с glibc и Linux. Было бы хорошо также исправить getconf, чтобы получить "правильное" ARG_MAX возвращаемое значение.

Изменить:

Чтобы немного прояснить (после короткого, но горячего обсуждения)

Константа ARG_MAX , которая определена в limits.h , дает максимальную длину одного аргумента, переданного с помощью exec.

Команда getconf ARG_MAX возвращает максимальное значение размера накопленных аргументов и размера среды, переданных в exec.

0
27.01.2020, 19:33

Теги

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