Здесь есть несколько вещей, которые следует учитывать.
i=`cat input`
может быть дорогим, и есть много вариантов между оболочками.
Эта функция называется подстановкой команд. Идея состоит в том, чтобы сохранить весь вывод команды за вычетом символов новой строки в i
переменной в памяти.
Для этого оболочка раскладывает команду в подоболочку и считывает ее вывод через канал или пару сокетов. Вы видите много вариантов здесь. Например, в файле размером 50 МБ, например, bash работает в 6 раз медленнее, чем ksh93, но немного быстрее zsh и в два раза быстрее yash
.
Основная причина bash
замедления заключается в том, что он читает из канала 128 байтов за раз (в то время как другие оболочки читают по 4 КБ или 8 КБ за раз) и подвергается штрафным расходам из-за системных вызовов.
zsh
необходимо выполнить некоторую постобработку, чтобы избежать байтов NUL (другие оболочки разбиваются на байты NUL), и yash
выполняет еще более тяжелую обработку, анализируя многобайтовые символы.
Всем оболочкам нужно убрать завершающие символы новой строки, которые они могут делать более или менее эффективно.
Некоторые могут захотеть обрабатывать байты NUL более изящно, чем другие, и проверять их наличие.
Затем, когда у вас есть эта большая переменная в памяти, любая манипуляция с ней обычно включает выделение дополнительной памяти и копирование данных.
Здесь вы передаете (намеревались передать) содержимое переменной echo
.
К счастью, echo
он встроен в вашу оболочку, в противном случае выполнение, скорее всего, завершилось бы неудачей со слишком длинной ошибкой списка аргументов . Даже в этом случае создание массива списка аргументов может потребовать копирования содержимого переменной.
Другая основная проблема в вашем подходе подстановки команд заключается в том, что вы вызываете оператор split + glob (забывая процитировать переменную в кавычках).
Для этого оболочкам необходимо обрабатывать строку как строку символов (хотя некоторые оболочки этого не делают и содержат ошибки), поэтому в локалях UTF-8 это означает синтаксический анализ последовательностей UTF-8 (если это еще не сделано, как в yash
случае с) ищите $IFS
символы в строке. Если он $IFS
содержит пробел, символ табуляции или новую строку (по умолчанию), алгоритм еще более сложный и дорогой. Затем слова, полученные в результате этого разделения, должны быть выделены и скопированы.
Часть шара будет еще дороже. Если любые из этих слов содержат Глобы символы ( *
, ?
, [
), то оболочка будет читать содержимое некоторых каталогов и сделать некоторый дорогой по шаблону ( bash
реализация «s, например , как известно , очень плохо в этом).
Если вход содержит что-то вроде этого /*/*/*/../../../*/*/*/../../../*/*/*
, это будет очень дорого, поскольку это означает перечисление тысяч каталогов, и это может увеличиться до нескольких сотен МБ.
Затем echo
обычно делают дополнительную обработку. Некоторые реализации расширяют \x
последовательности в полученном аргументе, что означает анализ содержимого и, возможно, другое выделение и копирование данных.
С другой стороны, OK, в большинстве оболочек cat
не является встроенным, так что это означает, что процесс должен быть разветвлен и выполнен (загрузка кода и библиотек), но после первого вызова этот код и содержимое входного файла будет кешироваться в памяти. С другой стороны, посредника не будет. cat
будет читать большое количество за раз и записывать его сразу же без обработки, и ему не нужно выделять огромное количество памяти, только тот буфер, который он использует повторно.
Это также означает, что он намного более надежен, так как не душит байты NUL и не обрезает завершающие символы новой строки (и не использует split + glob, хотя вы можете избежать этого, заключив переменную в кавычки, и не расширить escape-последовательность, хотя вы можете избежать этого, используя printf
вместо echo
).
Если вы хотите оптимизировать его дальше, вместо cat
нескольких вызовов , просто перейдите input
несколько раз к cat
.
yes input | head -n 100 | xargs cat
Будет работать 3 команды вместо 100.
Чтобы сделать версию переменной более надежной, вам нужно использовать zsh
(другие оболочки не могут справиться с байтами NUL) и сделать это:
zmodload zsh/mapfile
var=$mapfile[input]
repeat 10 print -rn -- "$var"
Если вы знаете, что входные данные не содержат байтов NUL, то вы можете надежно сделать это POSIXly (хотя он может не работать там, где printf
не встроено) с помощью:
i=$(cat input && echo .) || exit # add an extra .\n to avoid trimming newlines
i=${i%.} # remove that trailing dot (the \n was removed by cmdsubst)
n=10
while [ "$n" -gt 10 ]; do
printf %s "$i"
n=$((n - 1))
done
Но это никогда не будет более эффективным, чем использование cat
в цикле (если вход не очень маленький).
cat $(for i in $(seq 1 10); do echo "input"; done) >> output
? :)