Длина строки в баш

428

Как получить длину строки, хранящейся в переменной, и присвоить ее другой переменной?

myvar="some string"
echo ${#myvar}  
# 11

Как установить другую переменную для вывода 11?

AJP
источник

Ответы:

270

Длина строки UTF-8

В дополнение к правильному ответу fedorqui я хотел бы показать разницу между длиной строки и длиной в байтах:

myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
LANG=$oLang LC_ALL=$oLcAll
printf "%s is %d char len, but %d bytes len.\n" "${myvar}" $chrlen $bytlen

окажет:

Généralités is 11 char len, but 14 bytes len.

Вы могли бы даже взглянуть на сохраненные символы:

myvar='Généralités'
chrlen=${#myvar}
oLang=$LANG oLcAll=$LC_ALL
LANG=C LC_ALL=C
bytlen=${#myvar}
printf -v myreal "%q" "$myvar"
LANG=$oLang LC_ALL=$oLcAll
printf "%s has %d chars, %d bytes: (%s).\n" "${myvar}" $chrlen $bytlen "$myreal"

отвечу:

Généralités has 11 chars, 14 bytes: ($'G\303\251n\303\251ralit\303\251s').

Примечание: согласно комментарию Изабелл Коуэн , я добавил параметр $LC_ALLвместе с $LANG.

Длина аргумента

Аргумент работает так же, как обычные переменные

strLen() {
    local bytlen sreal oLang=$LANG oLcAll=$LC_ALL
    LANG=C LC_ALL=C
    bytlen=${#1}
    printf -v sreal %q "$1"
    LANG=$oLang LC_ALL=$oLcAll
    printf "String '%s' is %d bytes, but %d chars len: %s.\n" "$1" $bytlen ${#1} "$sreal"
}

будет работать как

strLen théorème
String 'théorème' is 10 bytes, but 8 chars len: $'th\303\251or\303\250me'

Полезный printfинструмент коррекции:

Если ты:

for string in Généralités Language Théorème Février  "Left: ←" "Yin Yang ☯";do
    printf " - %-14s is %2d char length\n" "'$string'"  ${#string}
done

 - 'Généralités' is 11 char length
 - 'Language'     is  8 char length
 - 'Théorème'   is  8 char length
 - 'Février'     is  7 char length
 - 'Left: ←'    is  7 char length
 - 'Yin Yang ☯' is 10 char length

Не очень красиво ... Для этого есть небольшая функция:

strU8DiffLen () { 
    local bytlen oLang=$LANG oLcAll=$LC_ALL
    LANG=C LC_ALL=C
    bytlen=${#1}
    LANG=$oLang LC_ALL=$oLcAll
    return $(( bytlen - ${#1} ))
}

То теперь:

for string in Généralités Language Théorème Février  "Left: ←" "Yin Yang ☯";do
    strU8DiffLen "$string"
    printf " - %-$((14+$?))s is %2d chars length, but uses %2d bytes\n" \
        "'$string'" ${#string} $((${#string}+$?))
  done 

 - 'Généralités'  is 11 chars length, but uses 14 bytes
 - 'Language'     is  8 chars length, but uses  8 bytes
 - 'Théorème'     is  8 chars length, but uses 10 bytes
 - 'Février'      is  7 chars length, but uses  8 bytes
 - 'Left: ←'      is  7 chars length, but uses  9 bytes
 - 'Yin Yang ☯'   is 10 chars length, but uses 12 bytes

К сожалению, это не идеально!

Но остались некоторые странные свойства UTF-8, такие как символы с двойным интервалом, символы с нулевым интервалом, обратное смещение и другие, которые не могут быть такими простыми ...

Посмотрите на diffU8test.sh или diffU8test.sh.txt для получения дополнительных ограничений.

Ф. Хаури
источник
Я ценю этот ответ, поскольку файловые системы накладывают ограничения на имена в байтах, а не в символах.
Gid
1
Вам также может понадобиться установить LC_ALL = C и, возможно, другие.
Изабелл Коуэн
1
@ F.Hauri Но, тем не менее, из этого следует, что в некоторых системах ваше решение не будет работать, поскольку оно оставляет LC_ALL в покое. Он может нормально работать при установке Debian и его производных по умолчанию, но в других (например, в Arch Linux) он не сможет указать правильную длину строки в байтах.
Изабелл Коуэн
1
спасибо за то, что взяли что-то простое и
обворожили
2
@thistleknot Извините, simple Иногда просто это просто идея.
Ф. Хаури
475

Чтобы получить длину строки, хранящейся в переменной, скажите:

myvar="some string"
size=${#myvar} 

Чтобы подтвердить, что он был правильно сохранен, echoон:

$ echo "$size"
11
Федорки "ТАК прекратить вредить"
источник
8
С UTF-8 укусами, вы могли бы иметь длину строки и длину байт. смотри мой ответ
Ф. Хаури
Вы также можете использовать его непосредственно в других расширениях параметров - например, в этом тесте я проверяю, который $rulenameначинается с $RULE_PREFIXпрефикса: [ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
Thomas Guyot-Sionnest
Не могли бы вы объяснить немного выражения #myvarи {#myvar}?
Лернер Чжан
1
@lerneradams см Bash Reference Manual → 3.5.3 Shell параметр разложения на ${#parameter}: Длина в символах расширенного значения параметра заменяется .
Федорки «ТАК прекрати вредить»
25

Ты можешь использовать:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -cили wc --bytesдля количества байтов = символы Юникода считаются с 2, 3 или более байтами.
  • wc -mили wc --charsдля количества символов = символы Юникода считаются одиночными, пока они не используют больше байтов.
atesin
источник
3
Шутки в сторону? труба, подоболочка и внешняя команда для чего-то такого тривиального?
gniourf_gniourf
это обрабатывает что-то вроде, mylen=$(printf "%s" "$HOME/.ssh" | wc -c)тогда как принятое решение терпит неудачу, и вам нужно myvar=$HOME/.sshсначала.
JL Peyret
23

Я хотел простейший случай, наконец, это результат:

echo -n 'Tell me the length of this sentence.' | wc -m;
36
dmatej
источник
4
извините, приятель :( Это баш ... проклятый молоток, который видит все как гвоздь, особенно большой палец. «Скажите мне длину этого предложения.» содержит 36 символов. echo '' | wc -m=> 1. Вам нужно использовать -n: echo -n '' | wc -m=> 0... в этом случае это хорошее решение :)
AJP
1
Спасибо за исправление! На странице руководства написано: -n do not output the trailing newline
dmatej
17

Если вы хотите использовать это с аргументами командной строки или функции, убедитесь, что вы используете size=${#1}вместо size=${#$1}. Второй может быть более инстинктивным, но имеет неверный синтаксис.

Дик Гертин
источник
14
Отчасти проблема с «вы не можете сделать <неверный синтаксис>» заключается в том, что этот синтаксис является недействительным, поэтому неясно, что читатель должен интерпретировать так, чтобы он означал. size=${#1}это конечно верно.
Чарльз Даффи
Ну, это неожиданно. Я не знал, что # 1 в этом случае заменит 1 доллар.
Дик Гертин
16
Это не так. #не заменяет $- $внешние скобки все еще оператор расширения. Оператор #длины, как всегда.
Чарльз Даффи
Я исправил этот ответ, так как это полезный совет, но не исключение из правила - он точно следует правилу, как указано @CharlesDuffy
Zane Hooper
16

В ответ на пост запуска:

Если вы хотите использовать это с аргументами командной строки или функции ...

с кодом:

size=${#1}

Может случиться так, что вы просто хотите проверить аргумент нулевой длины и вам не нужно хранить переменную. Я считаю, что вы можете использовать этот синтаксис:

if [ -z "$1" ]; then
    #zero length argument 
else
    #non-zero length
fi

Смотрите GNU и wooledge для более полного списка условных выражений Bash.

JGFMK
источник
11

Используя ваш пример при условии

#KISS (Keep it simple stupid)
size=${#myvar}
echo $size
thistleknot
источник
9

Вот несколько способов вычислить длину переменной:

echo ${#VAR}
echo -n $VAR | wc -m
echo -n $VAR | wc -c
printf $VAR | wc -m
expr length $VAR
expr $VAR : '.*'

и чтобы установить результат в другой переменной, просто назначьте указанную выше команду с кавычкой в ​​другую переменную следующим образом:

otherVar=`echo -n $VAR | wc -m`   
echo $otherVar

http://techopsbook.blogspot.in/2017/09/how-to-find-length-of-string-variable.html

Мукеш Шакья
источник