В качестве решения оболочки, getopts
, вероятно, проще всего. Дело в том, что getopts
- это POSIX-специфическое решение, позволяющее делать именно то, что вы просите - обрабатывать байт-поток в цикле оболочки. Я знаю, что это звучит странно, потому что, если ты такой же, как я, до того, как я узнал это, ты, наверное, думаешь, , ну, блин, я думал, что это должно было работать с переключателями командной строки. Это правда, но это первое, что я думаю. Подумайте:
-thisisonelongstringconsistingofseparatecommandlineswitches
Да, getopts
должен справиться с этим. Он должен разделить этот символ на символ char в цикле и вернуть вам каждый символ либо в переменной shell $OPTARG
, либо в другой, которую вы указываете по имени, в зависимости от того, насколько конкретно вы получаете, когда вызываете этот символ. Более того, он должен возвращать ошибки в переменных оболочки и сохранять прогресс, когда он делает это в переменной оболочки $OPTIND
, чтобы он мог возобновить работу с того места, где он остановился, если вы можете каким-то образом адресовать его. И он должен сделать всю работу, не вызывая ни одной под-оболочки.
Скажем так:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm.... Интересно, сработало ли это?
echo "$((${#arg}/6))" "$#"
482 482
Это мило...
eval '
printf %.1s\\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Итак, как вы видите, команда getopts
полностью устанавливает массив для каждого шестого байта в строке. И это не обязательно должны быть такие числа - и даже не должны быть символы, безопасные для оболочки - и вам даже не нужно указывать целевые символы, как я делал это выше с 01234565789
. Я неоднократно тестировал это во многих оболочках, и все они просто работают. Есть некоторые причуды - bash
выкинет первый символ, если это символ пробела - dash
принимает двоеточие :
в качестве заданного параметра, даже если это практически единственный POSIX, который конкретно запрещает. Но ничего из этого не имеет значения, так как getopts
все еще вносит текущее значение оптического символа в $OPTARG
, даже когда он возвращает вам ошибку (представленную ?, назначенной вашей указанной var опции), и в противном случае явно не устанавливает $OPTARG
, если только вы не объявили опцию, которая должна иметь аргумент. А пробельные символы - это вроде как хорошо - они только отбрасывают ведущий пробел, что отлично, потому что при работе с неизвестными значениями можно сделать:
getopts : o -" $unknown_value"
...чтобы запустить цикл без всякой опасности того, что первый символ на самом деле находится в вашей принятой строке аргументов - в результате чего getopts
засовывает все это в $OPTARG
сразу - в качестве аргумента.
Вот еще один пример:
OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\\%04o' "'$OPTARG"; done
\0040\0150\0071\0365\0320\0070\0161\0064\0274\0115\0012\0215\0222\0271\0146\0057\0166
Я установил $OPTIND=1
в первой строке, потому что я только что использовал getopts
и, пока вы его не сбросите, он ожидает, что его следующий вызов продолжится с того места, на котором он остановился - он хочет "${arg2}"
, иными словами. Но мне не хочется отдавать, и я делаю сейчас другую вещь, поэтому я даю ему знать, сбросив $OPTIND
, и в этот момент это хорошо.
В этом я использовал zsh
- который не рассуждает о ведущем пробеле - и поэтому первый символ - восьмеричный 40 - символ пробела. Обычно я не использую getopts
таким образом, хотя - я обычно использую его, чтобы избежать выполнения write()
для каждого байта и вместо этого присваивать его вывод - который приходит в переменной - другой переменной оболочки - как я делал это выше с set
после моды. Затем, когда я буду готов, я смогу взять всю строку, а когда я обычно удаляю первый байт.