Я пытаюсь найти наиболее эффективный способ перебора определенных значений, которые представляют собой одинаковое количество значений друг от друга в списке слов, разделенных пробелами (я не хочу использовать массив). Например,
list="1 ant bat 5 cat dingo 6 emu fish 9 gecko hare 15 i j"
Поэтому я хочу иметь возможность просто перебирать список и получать доступ только к 1,5,6,9 и 15.
РЕДАКТИРОВАТЬ: я должен был дать понять, что значения, которые я пытаюсь получить из списка, не должны отличаться по формату от остальной части списка. Что делает их особенными, так это исключительно их позиция в списке (в данном случае это позиция 1,4,7 ...). Таким образом, список мог бы быть,1 2 3 5 9 8 6 90 84 9 3 2 15 75 55
но я все еще хотел бы те же самые числа. А также, я хочу быть в состоянии сделать это, предполагая, что я не знаю длину списка.
Методы, о которых я думал до сих пор:
Способ 1
set $list
found=false
find=9
count=1
while [ $count -lt $# ]; do
if [ "${@:count:1}" -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
done
Способ 2
set list
found=false
find=9
while [ $# ne 0 ]; do
if [ $1 -eq $find ]; then
found=true
break
fi
shift 3
done
Метод 3 Я почти уверен, что пайпинг делает это худшим вариантом, но я пытался найти метод, который не использует set, из любопытства.
found=false
find=9
count=1
num=`echo $list | cut -d ' ' -f$count`
while [ -n "$num" ]; do
if [ $num -eq $find ]; then
found=true
break
fi
count=`expr $count + 3`
num=`echo $list | cut -d ' ' -f$count`
done
Итак, что будет наиболее эффективным, или я упускаю более простой метод?
источник
Ответы:
Довольно просто с
awk
. Это даст вам значение каждого четвертого поля для ввода любой длины:Это работает с использованием встроенных
awk
переменных, таких какNF
(количество полей в записи), и выполнением некоторого простогоfor
цикла для итерации по полям, чтобы получить те, которые вы хотите, без необходимости заранее знать, сколько их будет.Или, если вы действительно хотите просто эти конкретные поля, как указано в вашем примере:
Что касается вопроса об эффективности, самым простым способом было бы проверить этот или каждый из ваших других методов и использовать,
time
чтобы показать, сколько времени это займет; Вы также можете использовать такие инструменты, какstrace
видеть, как системные вызовы потока. Использованиеtime
выглядит как:Вы можете сравнить этот результат между различными методами, чтобы увидеть, какой из них наиболее эффективен с точки зрения времени; другие инструменты могут быть использованы для других показателей эффективности.
источник
echo
vs<<<
, «идентичный» - слишком сильное слово. Можно сказать, чтоstuff <<< "$list"
это почти идентичноprintf "%s\n" "$list" | stuff
. Что касаетсяecho
противprintf
, я направляю вас к этому ответу<<<
добавляет новую строку в конце. Это похоже на то, как$()
удаляет перевод строки с конца. Это потому, что строки заканчиваются символами новой строки.<<<
Выдает выражение в виде строки, поэтому оно должно заканчиваться символом новой строки."$()"
берет строки и предоставляет их в качестве аргумента, поэтому имеет смысл конвертировать, удаляя завершающий символ новой строки.awk
, это отдельный двоичный файл, который должен запускаться. В отличие от perl или особенно Python, интерпретатор awk запускается быстро (все еще обычные накладные расходы динамического компоновщика при выполнении нескольких системных вызовов, но awk использует только libc / libm и libdl. Например, используетсяstrace
для проверки системных вызовов при запуске awk) , Многие оболочки (например, bash) работают довольно медленно, поэтому запуск одного процесса awk может быть быстрее, чем зацикливание токенов в списке со встроенными оболочками даже для списков небольшого размера. А иногда вы можете написать#!/usr/bin/awk
скрипт , а не в виде#!/bin/sh
сценария.Первое правило оптимизации программного обеспечения: не надо .
Пока вы не знаете, скорость программы является проблемой, нет необходимости думать о том, как быстро она работает. Если ваш список примерно такой длины или всего ~ 100-1000 пунктов, вы, вероятно, даже не заметите, сколько времени это займет. Существует вероятность того, что вы тратите больше времени на обдумывание оптимизации, чем на разницу.
Второе правило: мера .
Это верный способ узнать, и тот, который дает ответы для вашей системы. Особенно со снарядами их так много, и они не все одинаковые. Ответ на одну оболочку может не относиться к вашей.
В больших программах профилирование идет и здесь. Самая медленная часть может быть не той, о которой вы думаете.
В-третьих, первое правило оптимизации сценария оболочки: не используйте оболочку .
Да, правда. Многие оболочки не созданы быстрыми (поскольку запускать внешние программы не обязательно), и они могут даже каждый раз анализировать строки исходного кода.
Вместо этого используйте что-то вроде awk или Perl. В простейшем микропроцессоре, который я сделал, он
awk
был в десятки раз быстрее любой обычной оболочки при выполнении простого цикла (без ввода-вывода).Однако, если вы используете оболочку, используйте встроенные функции оболочки вместо внешних команд. Здесь вы используете,
expr
который не встроен ни в какие оболочки, которые я нашел в моей системе, но который можно заменить стандартным арифметическим расширением. Например,i=$((i+1))
вместоi=$(expr $i + 1)
увеличенияi
. Использованиеcut
в последнем примере также может быть заменено стандартными расширениями параметров.См. Также: Почему использование цикла оболочки для обработки текста считается плохой практикой?
Шаги № 1 и № 2 должны применяться к вашему вопросу.
источник
awk
петли обязательно лучше или хуже, чем петли оболочки. Дело в том, что оболочка действительно хороша для запуска команд и для направления ввода и вывода в процессы и из процессов, и, откровенно говоря, довольно неуклюжа во всем остальном; в то время как такие инструменты , какawk
это фантастическое при обработке текстовых данных, потому что это то , что снаряды и инструменты , такие какawk
сделано для (соответственно) , в первую очередь.dash
чем сgawk
, иdash
были самой быстрой оболочкой, которую я тестировал ...dash
иbusybox
не поддерживаю(( .. ))
- я думаю, что это нестандартное расширение.++
также явно упоминается как необязательный, насколько я могу судить,i=$((i+1))
или: $(( i += 1))
являются безопасными.В этом ответе я дам лишь общие советы, а не тесты. Тесты - это единственный способ достоверно ответить на вопросы о производительности. Но так как вы не говорите, сколько данных вы манипулируете и как часто вы выполняете эту операцию, нет никакого способа сделать полезный тест. Что более эффективно для 10 предметов, а что более эффективно для 1000000 предметов, зачастую не одно и то же.
Как общее правило, вызов внешних команд обходится дороже, чем выполнение каких-либо операций с чистыми конструкциями оболочки, если чистый код оболочки не включает цикл. С другой стороны, цикл оболочки, который выполняет итерации по большой строке или большому количеству строки, вероятно, будет медленнее, чем один вызов специального инструмента. Например, ваш вызов цикла
cut
на практике может быть заметно медленным, но если вы найдете способ сделать все это однимcut
вызовом, это, вероятно, будет быстрее, чем то же самое с манипулированием строками в оболочке.Обратите внимание, что точка отсечки может сильно различаться в разных системах. Это может зависеть от ядра, от того, как настроен планировщик ядра, от файловой системы, содержащей внешние исполняемые файлы, от того, какая нагрузка на ЦП и память в данный момент существует, и от многих других факторов.
Не звоните,
expr
чтобы выполнить арифметику, если вас вообще беспокоит производительность. На самом деле, не призывайтеexpr
выполнять арифметику вообще. Оболочки имеют встроенную арифметику, которая понятнее и быстрее, чем вызовexpr
.Кажется, вы используете bash, поскольку вы используете конструкции bash, которых нет в sh. Так почему же вы не используете массив? Массив является наиболее естественным решением, и, вероятно, он также будет самым быстрым. Обратите внимание, что индексы массива начинаются с 0.
Ваш скрипт может быть быстрее, если вы используете sh, если в вашей системе
sh
вместо bash используется dash или ksh . Если вы используете sh, вы не получите именованные массивы, но вы все равно получите массив с одним из позиционных параметров, который вы можете установитьset
. Чтобы получить доступ к элементу в позиции, которая не известна до времени выполнения, вам нужно использоватьeval
(позаботьтесь о правильном цитировании!).Если вы когда-нибудь захотите получить доступ к массиву только один раз и идете слева направо (пропуская некоторые значения), вы можете использовать
shift
вместо переменных индексы.Какой подход быстрее, зависит от оболочки и количества элементов.
Другая возможность - использовать обработку строк. Он имеет преимущество в том, что не использует позиционные параметры, поэтому вы можете использовать их для чего-то другого. Это будет медленнее для больших объемов данных, но вряд ли это будет заметно для небольших объемов данных.
источник
shift && shift && shift
наshift 3
в третьем примере - если оболочка, которую вы используете, не поддерживает ее.shift 3
потерпит неудачу, если будет слишком мало оставшихся аргументов. Вам нужно что-то вродеif [ $# -gt 3 ]; then shift 3; else set --; fi
awk
отличный выбор, если вы можете выполнять всю свою обработку внутри скрипта Awk. В противном случае вы просто отправляете выходные данные Awk другим утилитам, снижая прирост производительностиawk
.bash
Итерация по массиву также хороша, если вы можете разместить весь список внутри массива (что для современных оболочек, вероятно, является гарантией), и вы не против гимнастики синтаксиса массива.Тем не менее, конвейерный подход:
Где:
xargs
группирует разделенный пробелами список по три в каждой, каждая новая строка разделяетсяwhile read
потребляет этот список и выводит первый столбец каждой группыgrep
фильтрует первый столбец (соответствующий каждой третьей позиции в исходном списке)Улучшает понятность, на мой взгляд. Люди уже знают, что делают эти инструменты, поэтому легко читать слева направо и рассуждать о том, что произойдет. Этот подход также четко документирует длину шага (
-n3
) и шаблон фильтра (9
), поэтому его легко варьировать:Когда мы задаем вопросы «эффективности», обязательно подумайте об «общей эффективности жизни». Этот расчет включает в себя усилия сопровождающих по поддержанию работоспособности кода, а мы, мешки с мясом, являемся наименее эффективными машинами за всю операцию.
источник
Возможно это?
источник
Не используйте команды оболочки, если вы хотите быть эффективными. Ограничьте себя каналами, перенаправлениями, заменами и т. Д. И программами. Вот почему
xargs
иparallel
утилиты существуют - потому что циклы bash while неэффективны и очень медленны. Используйте петли bash только в качестве последнего решения.Но вы должны быть, вероятно, немного быстрее с хорошим
awk
.источник
На мой взгляд, самое ясное решение (и, вероятно, самое эффективное тоже) заключается в использовании переменных awk RS и ORS:
источник
Использование сценария оболочки GNU
sed
и POSIX :Или с
bash
«S подстановки параметров :Не- GNU ( то есть POSIX )
sed
, иbash
:Или, что более удобно, используя POSIX
sed
и shell-скрипт:Вывод любого из них:
источник