Это последняя версия Fedora 18.
Исходный файл PS насыщен математикой (это набор из 28 слайдов, созданных из файла TeX DVI с растровыми шрифтами). Он отлично читается в evince
(но в перевернутом виде), а результат ps2pdf
также хорошо читается в evince
и xpdf
.
В лучшем случае вы можете надеяться получить (скудный) текст на слайдах, по большей части это математика, которая вообще не переводится в понятный текст даже при идеальных обстоятельствах, а тем более в формулы. Единственный способ снова превратить это в математические формулы LaTeX (или любой другой формат, который вам нужен) - это перепечатать их.
Не используйте команды оболочки, если хотите быть эффективным. Ограничьтесь каналами, перенаправлениями, заменами и т. д., а также программами. Вот почему существуют утилиты xargs
и parallel
-, потому что циклы while в bash неэффективны и очень медленны. Используйте циклы bash только в качестве последнего решения.
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
if
<<<"$list" tr -d -s '[0-9 ]' |
tr -s ' ' | tr ' ' '\n' |
grep -q -x '9'
then
found=true
else
found=false
fi
echo ${found}
Но с хорошим awk
вы, вероятно, должны работать несколько быстрее.
Довольно просто с awk
. Это даст вам значение каждого четвертого поля для ввода любой длины:
$ awk -F' ' '{for( i=1;i<=NF;i+=3) { printf( "%s%s", $i, OFS ) }; printf( "\n" ) }' <<< $list
1 5 6 9 15
При этом используются встроенные -в awk
переменные, такие какNF
(количество полей в записи ), и выполняется некоторый простой for
цикл для перебора полей, чтобы получить те, которые вы хотите, не зная заранее, сколько их будет.
Или, если вам действительно нужны только те конкретные поля, которые указаны в вашем примере:
$ awk -F' ' '{ print $1, $4, $7, $10, $13 }' <<< $list
1 5 6 9 15
Что касается вопроса об эффективности, самым простым путем было бы проверить этот или каждый из ваших других методов и использовать time
, чтобы показать, сколько времени это занимает; вы также можете использовать такие инструменты, как strace
, чтобы увидеть, как проходят системные вызовы. Использование time
выглядит как:
$ time./script.sh
real 0m0.025s
user 0m0.004s
sys 0m0.008s
Вы можете сравнить эти выходные данные между различными методами, чтобы увидеть, какой из них наиболее эффективен с точки зрения времени; другие инструменты могут использоваться для других показателей эффективности.
Первое правило оптимизации программного обеспечения:Не делайте этого .
Пока вы не знаете, что проблема в скорости работы программы, не нужно думать. о том, как быстро. Если ваш список примерно такой длины или просто ~100 -1000 элементов долго, вы, вероятно, даже не заметите, сколько времени это займет. Есть вероятность, что вы тратите больше времени на размышления об оптимизации, чем о разнице.
Второе правило:Мера .
Это верный способ узнать, и он дает ответы для вашей системы. Особенно с оболочками, их так много, и они не все одинаковые. Ан ответ для одной оболочки может не относиться к вашей.
В больших программах сюда также относится профилирование. Самая медленная часть может оказаться не той, о которой вы думаете.
В-третьих, первое правило оптимизации сценариев оболочки:Не использовать оболочку .
Да, действительно. Многие оболочки не предназначены для быстрой работы (с момента запуска внешних программы не обязательно должны быть ), и они могут даже анализировать строки исходного кода. каждый раз кодировать заново.
Вместо этого используйте что-то вроде awk или Perl. В тривиальном микро -тесте, который я сделал, awk
был в десятки раз быстрее, чем любая обычная оболочка при выполнении простого цикла (без ввода-вывода ).
Однако, если вы используете оболочку, используйте встроенные функции оболочки вместо внешних команд. Здесь вы используете expr
, который не встроен ни в одну оболочку, которую я нашел в своей системе, но которую можно заменить стандартным арифметическим расширением. Например.i=$((i+1))
вместо i=$(expr $i + 1)
для увеличения i
. Использование вами cut
в последнем примере также может быть заменено раскрытием стандартных параметров.
См. также:Почему использование цикла оболочки для обработки текста считается плохой практикой?
Шаги #1 и #2 должны относиться к вашему вопросу.
awk
— отличный выбор, если вы можете выполнять всю свою обработку внутри скрипта Awk. В противном случае вы просто перенаправляете вывод Awk в другие утилиты, уничтожая прирост производительности awk
.
bash
Итерация по массиву также хороша, если вы можете уместить весь свой список внутри массива (что для современных оболочек, вероятно, является гарантией)и вы не возражаете против гимнастики синтаксиса массива.
Однако конвейерный подход:
xargs -n3 <<< "$list" | while read -ra a; do echo $a; done | grep 9
Где:
xargs
группирует список, разделенный пробелами -, на группы по три, каждая новая -строка разделена while read
использует этот список и выводит первый столбец каждой группы grep
фильтрует первый столбец (, соответствующий каждой третьей позиции в исходном списке)На мой взгляд, улучшает понимание. Люди уже знают, что делают эти инструменты, поэтому легко читать слева направо и рассуждать о том, что произойдет. Этот подход также четко документирует длину шага(-n3
)и шаблон фильтра (9
), поэтому его легко варьировать :
count=3
find=9
xargs -n "$count" <<< "$list" | while read -ra a; do echo $a; done | grep "$find"
Когда мы задаем вопросы об «эффективности», обязательно думайте об «общей эффективности за весь срок службы». Этот расчет включает в себя усилия сопровождающих по поддержанию работоспособности кода, а мы, -мешки с мясом, — наименее эффективные машины во всей операции.
По моему мнению, самое ясное решение (и, возможно, наиболее эффективное )— это использование awk-переменных RS и ORS:
awk -v RS=' ' -v ORS=' ' 'NR % 3 == 1' <<< "$list"
Использование GNUsed
и сценария оболочки POSIX :
echo $(printf '%s\n' $list | sed -n '1~3p')
Или с заменой параметра bash
на :
echo $(sed -n '1~3p' <<< ${list// /$'\n'})
Не-GNU (, то естьPOSIX)sed
иbash
:
sed 's/\([^ ]* \)[^ ]* *[^ ]* */\1/g' <<< "$list"
Или, что более переносимо, с использованием POSIXsed
и сценария оболочки:
echo "$list" | sed 's/\([^ ]* \)[^ ]* *[^ ]* */\1/g'
Вывод любого из этих:
1 5 6 9 15
В этом ответе я дам только общие советы, а не тесты. Бенчмарки — единственный способ достоверно ответить на вопросы о производительности. Но так как вы не говорите сколько данных вы обрабатываете и как часто вы выполняете эту операцию, нет никакого способа провести полезный тест. Что более эффективно для 10 элементов и что более эффективно для 1000000 элементов, часто не одно и то же.
Как правило, вызов внешних команд обходится дороже, чем выполнение каких-либо действий с конструкциями чистой оболочки, если код чистой оболочки не включает цикл. С другой стороны, цикл оболочки, который перебирает большую строку или большое количество строк, скорее всего, будет медленнее, чем один вызов специального -целевого инструмента. Например, ваш цикл, вызывающий cut
, вполне может быть заметно медленным на практике, но если вы найдете способ сделать все это с помощью одного вызова cut
, это, вероятно, будет быстрее, чем делать то же самое с манипуляциями со строками в оболочка.
Обратите внимание, что точка отсечки может сильно различаться в разных системах. Это может зависеть от ядра, от того, как настроен планировщик ядра, от файловой системы, содержащей внешние исполняемые файлы, от того, насколько велика нагрузка на процессор и память в данный момент, и от многих других факторов.
Не вызывайте expr
для выполнения арифметических операций, если вас вообще беспокоит производительность. На самом деле, вообще не вызывайте expr
для выполнения арифметических операций. Оболочки построили -в арифметике, которая понятнее и быстрее, чем вызов expr
.
Кажется, вы используете bash,поскольку вы используете конструкции bash, которых нет в sh. Так почему бы вам не использовать массив? Массив — наиболее естественное решение, и, вероятно, оно также будет самым быстрым. Обратите внимание, что индексы массива начинаются с 0.
list=(1 2 3 5 9 8 6 90 84 9 3 2 15 75 55)
for ((count = 0; count += 3; count < ${#list[@]})); do
echo "${list[$count]}"
done
Ваш скрипт может быть быстрее, если вы используете sh, если в вашей системе есть dash или ksh как sh
, а не bash. Если вы используете sh, вы не получаете именованные массивы, но вы все равно получаете массив с одним из позиционных параметров, который вы можете установить с помощью set
. Чтобы получить доступ к элементу в позиции, которая неизвестна до времени выполнения, вам нужно использоватьeval
(позаботьтесь о правильном цитировании! ).
# List elements must not contain whitespace or ?*\[
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
set $list
count=1
while [ $count -le $# ]; do
eval "value=\${$count}"
echo "$value"
count=$((count+1))
done
Если вы хотите получить доступ к массиву только один раз и перемещаетесь слева направо (, пропуская некоторые значения ), вы можете использовать shift
вместо индексов переменных.
# List elements must not contain whitespace or ?*\[
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
set $list
while [ $# -ge 1 ]; do
echo "$1"
shift && shift && shift
done
Какой подход быстрее, зависит от оболочки и количества элементов.
Другой возможностью является использование обработки строк. Его преимущество в том, что он не использует позиционные параметры, поэтому вы можете использовать их для чего-то другого. Это будет медленнее для больших объемов данных, но вряд ли это будет иметь заметное значение для небольших объемов данных.
# List elements must be separated by a single space (not arbitrary whitespace)
list='1 2 3 5 9 8 6 90 84 9 3 2 15 75 55'
while [ -n "$list" ]; do
echo "${list% *}"
case "$list" in *\ *\ *\ *) :;; *) break;; esac
list="${list#* * * }"
done