Я пишу сценарий, который должен рассчитать количество символов в выводе команды за один шаг .
Например, использование команды readlink -f /etc/fstab
должно возвращаться, 10
потому что длина этой команды составляет 10 символов.
Это уже возможно с сохраненными переменными, используя следующий код:
variable="somestring";
echo ${#variable};
# 10
К сожалению, использование той же формулы с командной строкой не работает:
${#(readlink -f /etc/fstab)};
# bash: ${#(readlink -f /etc/fstab)}: bad substitution
Я понимаю, что это можно сделать, предварительно сохранив вывод в переменную:
variable=$(readlink -f /etc/fstab);
echo ${#variable};
Но я бы хотел убрать лишний шаг.
Это возможно? Совместимость с оболочкой Almquist (sh) с использованием только встроенных или стандартных утилит является предпочтительной.
readlink -f /etc/fstab
составляет 11 символов. Не забывайте перевод строки. В противном случае вы увидите,/etc/fstabluser@cern:~$
когда вы запускаете его из оболочки.Ответы:
С GNU expr :
+
Есть специальная особенность GNU ,expr
чтобы убедиться , что следующий аргумент трактуется как строка , даже если это случается,expr
оператор , какmatch
,length
,+
...Выше будет лишить любой завершающий перевод строки. Чтобы обойти это:
Результат был вычтен до 2, потому что последний перевод строки
readlink
и символ.
мы добавили.С Unicode string,
expr
похоже, не работает, потому что он возвращает длину строки в байтах вместо количества символов (см. Строку 654 )Итак, вы можете использовать:
POSIXLY:
Пробел перед заменой команды предотвращает сбой команды при начале строки
-
, поэтому нам нужно вычесть 3.источник
LC_ALL=C.UTF-8
, что значительно упрощает вещи, если кодировка строки не будет известна заранее.expr length $(echo "*")
- нет. По крайней мере , использовать двойные кавычки:expr length "$(…)"
. Но это убирает завершающие переводы строк из команды, это неизбежная особенность подстановки команд. (Вы можете обойти это, но тогда ответ становится еще более сложным.)Не уверен, как это сделать с помощью встроенных командных оболочек ( хотя Gnouc есть ), но стандартные инструменты могут помочь:
Вы можете использовать,
wc -m
который считает символы. К сожалению, он также учитывает окончательный перевод строки, поэтому вам придется сначала избавиться от этого:Вы можете, конечно, использовать
awk
Или Perl
источник
expr
встроенный? В какой оболочке?Я обычно делаю это так:
Для выполнения команд я бы адаптировал это так:
Этот подход аналогичен тому, что вы делали в два этапа, за исключением того, что мы объединяем их в один вкладыш.
источник
-m
вместо-c
. С юникод-символами ваш подход будет нарушен.readlink -f /etc/fstab | wc -m
?${#variable}
? По крайней мере используйте двойные кавычкиecho -n "$variable"
, но это все равно не сработает, если, например, значениеvariable
равно-e
. Когда вы используете его в сочетании с подстановкой команд, имейте в виду, что завершающие символы новой строки удаляются.Вы можете вызвать внешние утилиты (см. Другие ответы), но они сделают ваш скрипт медленнее, и сложно правильно разобраться с сантехникой.
Zsh
В zsh вы можете написать,
${#$(readlink -f /etc/fstab)}
чтобы получить длину подстановки команд. Обратите внимание, что это не длина вывода команды, а длина вывода без завершающей строки.Если вам нужна точная длина вывода, выведите дополнительный не-символ новой строки в конце и вычтите его.
Если вы хотите получить полезную нагрузку в выводе команды, то вам нужно вычесть два здесь, потому что вывод
readlink -f
- это канонический путь плюс новая строка.Это отличается от
${#$(readlink -f /etc/fstab)}
того редкого, но возможного случая, когда сам канонический путь заканчивается новой строкой.Для этого конкретного примера вам вообще не нужна внешняя утилита, потому что zsh имеет встроенную конструкцию, которая эквивалентна
readlink -f
модификатору историиA
.Чтобы получить длину, используйте модификатор истории в расширении параметра:
Если у вас есть имя файла в переменной
filename
, это будет${#filename:A}
.Оболочки в стиле Bourne / POSIX
Ни одна из чистых оболочек Bourne / POSIX (Bourne, ash, mksh, ksh93, bash, yash ...) не имеет аналогичного расширения, о котором я знаю. Если вам нужно применить подстановку параметров к выходным данным подстановки команд или для подстановки подстановок параметров, используйте последовательные этапы.
Вы можете добавить обработку в функцию, если хотите.
или
но обычно нет никакой выгоды; кроме как с ksh93, это приводит к тому, что дополнительная вилка может использовать выходные данные функции, так что это замедляет работу вашего скрипта и редко дает какие-либо преимущества для удобства чтения.
Еще раз, выходные данные
readlink -f
- канонический путь плюс новая строка; если вы хотите длину канонического пути, вычтите 2 вместо 1 вcommand_output_length
. Использованиеcommand_output_length_sans_trailing_newlines
дает правильный результат только тогда, когда сам канонический путь не заканчивается новой строкой.Байт против символов
${#…}
должна быть длина в символах, а не в байтах, что имеет значение в многобайтовых локалях. Разумно обновленные версии ksh93, bash и zsh вычисляют длину в символах в соответствии со значениемLC_CTYPE
в момент раскрытия${#…}
конструкции. Многие другие распространенные оболочки в действительности не поддерживают многобайтовые локали: по состоянию на dash 0.5.7, mksh 46 и posh 0.12.3${#…}
возвращает длину в байтах. Если вы хотите, чтобы длина в символах надежным образом, используйтеwc
утилиту:Пока вы
$LC_CTYPE
указываете действительный языковой стандарт, вы можете быть уверены, что это приведет к ошибке (на древней или ограниченной платформе, которая не поддерживает многобайтовые языковые стандарты) или вернет правильную длину в символах. (Для Unicode «длина в символах» означает количество кодовых точек - количество глифов - это еще одна история из-за сложностей, таких как объединение символов.)Если вы хотите длину в байтах, установите
LC_CTYPE=C
временно или используйтеwc -c
вместоwc -m
.Подсчет байтов или символов
wc
включает любые завершающие символы новой строки из команды. Если вы хотите, чтобы длина канонического пути в байтах, этоЧтобы получить это в символах, вычтите 2.
источник
echo .
добавляет два символа, но второй символ - это завершающий символ новой строки, который удаляется подстановкой команды.readlink
вывода, плюс.
поecho
. Мы оба согласны, чтоecho .
добавим два символа, но завершающий символ новой строки был удален. Попробуйтеprintf .
или посмотрите мой ответ unix.stackexchange.com/a/160499/38906 .readlink
- цель ссылки плюс новая строка.Это работает,
dash
но требует, чтобы целевой var был определенно пуст или не установлен. Вот почему это на самом деле две команды - я явно пустой$l
в первой:ВЫХОД
Это все встроенные функции оболочки - не считая,
readlink
конечно, - но ее оценка в текущей оболочке таким образом подразумевает, что вы должны выполнить присваивание перед получением len, поэтому я%.s
выбираю первый аргумент вprintf
строке формата и просто добавляю его снова для буквальное значение вprintf
конце списка аргументов.С
eval
:ВЫХОД
Вы можете приблизиться к тому же самому, но вместо вывода в переменной в первой команде вы получите его в stdout:
... который пишет ...
... для файлового дескриптора 1 без присвоения какого-либо значения любым переменным в текущей оболочке.
источник
variable=$(readlink -f /etc/fstab); echo ${#variable};
но я бы хотел удалить дополнительный шаг».expr
. Это, вероятно, имеет значение только в том случае, если каким-то образом получение len перекрывает значение, которое, я признаю, мне трудно понять, почему это может быть, но я подозреваю, что может быть случай, в котором это имеет значение.eval
Способ, кстати, является , вероятно , самым чистым здесь - он назначает выход и Лен к тому же имени вара в одном исполнении - очень близок к этомуl=length(l):out(l)
. Делатьexpr length $(command)
ли закупорить значение в пользу Len, кстати.