Я пишу скрипт, который должен подсчитать количество символов в выводе команды водин шаг.
Например, использование команды 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) с использованием только встроенных или стандартных утилит.
решение1
$ expr length + "$(readlink -f /etc/fstab)"
10
В +
GNU есть специальная функция, expr
которая гарантирует, что следующий аргумент будет обработан как строка, даже если это expr
оператор вроде match
, length
, +
...
Вышеуказанное удалит любой завершающий перевод строки вывода. Чтобы обойти это:
$ expr length + "$(readlink -f /etc/fstab; printf .)" - 2
10
Результат был вычтен из2потому что последняя новая строка readlink
и символ .
мы добавили.
Со строкой Unicode, expr
похоже, не работает, поскольку возвращает длину строки в байтах вместо количества символов (см.строка 654)
$ LC_ALL=C.UTF-8 expr length ăaa
4
Итак, вы можете использовать:
$ printf "ăaa" | LC_ALL=C.UTF-8 wc -m
3
ПОСИКЛИ:
$ expr " $(readlink -f /etc/fstab; printf .)" : ".*" - 3
10
Пробел перед подстановкой команды предотвращает сбой команды со строкой, начинающейся с -
, поэтому нам нужно вычесть 3.
решение2
Не уверен, как это сделать с помощью встроенных функций оболочки (Gnouc, хотя) но стандартные инструменты могут помочь:
Вы можете использовать
wc -m
which counts characters. К сожалению, он также учитывает последний перевод строки, поэтому вам придется сначала избавиться от него:readlink -f /etc/fstab | tr -d '\n' | wc -m
Конечно, вы можете использовать
awk
readlink -f /etc/fstab | awk '{print length($0)}'
Или Перл
readlink -f /etc/fstab | perl -lne 'print length'
решение3
Я обычно делаю это так:
$ echo -n "$variable" | wc -m
10
Для выполнения команд я бы адаптировал его следующим образом:
$ echo -n "$(readlink -f /etc/fstab)" | wc -m
10
Этот подход похож на тот, который вы делали в двух шагах, за исключением того, что мы объединяем их в одну строку.
решение4
Это работает, dash
но требует, чтобы целевая var была определенно пустой или неустановленной. Вот почему это на самом деледвакоманды - я явно опустошаю $l
в первой:
l=;printf '%.slen is %d and result is %s\n' \
"${l:=$(readlink -f /etc/fstab)}" "${#l}" "$l"
ВЫХОД
len is 10 and result is /etc/fstab
Это все встроенные функции оболочки ( readlink
конечно, не включая ), но вычисление их в текущей оболочке таким образом подразумевает, что вы должны выполнить присваивание до получения len, поэтому я %.s
отключаю первый аргумент в printf
строке формата и просто добавляю его снова для буквального значения в конце printf
списка аргументов .
С eval
:
l=$(readlink -f /etc/fstab) eval 'l=${#l}:$l'
printf %s\\n "$l"
ВЫХОД
10:/etc/fstab
Вы можете приблизиться к тому же результату, но вместо вывода в переменной в первой команде вы получаете его на stdout:
PS4='${#0}:$0' dash -cx '2>&1' "$(readlink -f /etc/fstab)"
...который пишет...
10:/etc/fstab
...в файловый дескриптор 1 без присвоения каких-либо значений каким-либо переменным в текущей оболочке.