Długość sznurka w uderzeniu

428

Jak uzyskać długość ciągu przechowywanego w zmiennej i przypisać ją do innej zmiennej?

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

Jak ustawić inną zmienną na wyjściu 11?

AJP
źródło

Odpowiedzi:

270

Długość łańcucha UTF-8

Oprócz poprawnej odpowiedzi fedorqui chciałbym pokazać różnicę między długością łańcucha a długością bajtu:

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

wyrenderuje:

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

możesz nawet przejrzeć zapisane znaki:

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"

odpowie:

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

Nota: Zgodnie z komentarzem Isabell Cowan , dodałem ustawienie $LC_ALLwraz z $LANG.

Długość argumentu

Argument działa tak samo jak zmienne zwykłe

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"
}

będzie działać jako

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

Przydatne printfnarzędzie korekcyjne:

Jeśli ty:

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

Niezbyt ładnie ... W tym celu istnieje mała funkcja:

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

Więc teraz:

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

Niestety nie jest to idealne!

Ale pozostało kilka dziwnych zachowań UTF-8, takich jak znaki podwójnie rozmieszczone, znaki zerowe, odwrotne rozmieszczenie i inne, które nie mogą być tak proste ...

Zajrzyj na diffU8test.sh lub diffU8test.sh.txt, aby uzyskać więcej ograniczeń.

F. Hauri
źródło
Doceniam tę odpowiedź, ponieważ systemy plików narzucają ograniczenia nazw w bajtach, a nie znakach.
Gid
1
Może być również konieczne ustawienie LC_ALL = C i być może innych.
Isabell Cowan
1
@ F.Hauri Niemniej jednak wynika z tego, że w niektórych systemach twoje rozwiązanie nie będzie działać, ponieważ pozostawia LC_ALL w spokoju. Może działać dobrze na domyślnych instalacjach Debiana i jego pochodnych, ale na innych (takich jak Arch Linux) nie poda poprawnej długości bajtu łańcucha.
Isabell Cowan
1
dzięki za wzięcie czegoś prostego i skomplikowanego :)
thistleknot
2
@ thistleknot Przepraszam, 對不起 Czasami prosty to tylko pomysł.
F. Hauri,
474

Aby uzyskać długość ciągu przechowywanego w zmiennej, powiedz:

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

Aby potwierdzić, że został poprawnie zapisany, echoto:

$ echo "$size"
11
fedorqui „SO przestań szkodzić”
źródło
8
Przy użądleniach UTF-8 możesz mieć długość łańcucha i długość bajtów. patrz moja odpowiedź
F. Hauri
Możesz go również użyć bezpośrednio w innych rozszerzeniach parametrów - na przykład w tym teście sprawdzam, że $rulenamezaczyna się od $RULE_PREFIXprzedrostka: [ "${rulename:0:${#RULE_PREFIX}}" == "$RULE_PREFIX" ]
Thomas Guyot-Sionnest
Czy mógłbyś wyjaśnić trochę wyrażenia #myvari {#myvar}?
Lerner Zhang,
1
@lerneradams patrz instrukcja obsługi Bash → 3.5.3 Rozszerzanie parametru powłoki na ${#parameter}: Długość w znakach rozszerzonej wartości parametru jest podstawiana .
fedorqui „SO przestań krzywdzić”
25

Możesz użyć:

MYSTRING="abc123"
MYLENGTH=$(printf "%s" "$MYSTRING" | wc -c)
  • wc -club wc --bytesdla liczby bajtów = znaki Unicode są liczone z 2, 3 lub więcej bajtami.
  • wc -mlub wc --charsdla liczby znaków = znaki Unicode są liczone pojedynczo, dopóki nie użyją więcej bajtów.
atesin
źródło
3
Poważnie? potok, podpowłoka i zewnętrzne polecenie dla czegoś tak trywialnego?
gniourf_gniourf
obsługuje to coś podobnego, mylen=$(printf "%s" "$HOME/.ssh" | wc -c)podczas gdy zaakceptowane rozwiązanie zawodzi i musisz myvar=$HOME/.sshnajpierw.
JL Peyret
23

Chciałem najprostszego przypadku, w końcu jest to wynik:

echo -n 'Tell me the length of this sentence.' | wc -m;
36
dmatej
źródło
4
sorry kolego :( To uderzenie ... przeklęty młot, który widzi wszystko jak gwóźdź, szczególnie twój kciuk. „Powiedz mi długość tego zdania.” zawiera 36 znaków. echo '' | wc -m=> 1. Musisz użyć -n: echo -n '' | wc -m=> 0... w takim przypadku jest to dobre rozwiązanie :)
AJP
1
Dziękuję za poprawienie mnie! Strona podręcznika mówi: -n do not output the trailing newline
dmatej,
17

Jeśli chcesz tego używać z argumentami wiersza poleceń lub funkcji, upewnij się, że używasz size=${#1}zamiast size=${#$1}. Drugi może być bardziej instynktowny, ale ma niepoprawną składnię.

Dick Guertin
źródło
14
Część problemu z „nie można zrobić <niepoprawna składnia>” polega na tym, że ta składnia jest nieprawidłowa, nie jest jasne, co czytelnik powinien to interpretować. size=${#1}jest z pewnością ważny.
Charles Duffy
To nieoczekiwane. Nie wiedziałem, że w tym przypadku numer 1 zastąpił 1 $.
Dick Guertin
16
To nie jest #nie zastępuje $- na $zewnątrz nawiasy klamrowe są nadal operatorem rozszerzenia. Jest #to operator długości, jak zawsze.
Charles Duffy,
Naprawiłem tę odpowiedź, ponieważ jest to użyteczna wskazówka, ale nie wyjątek od reguły - dokładnie przestrzega reguły, jak wskazał @CharlesDuffy
Zane Hooper
16

W odpowiedzi na post rozpoczynający się:

Jeśli chcesz tego używać z argumentami wiersza poleceń lub funkcji ...

z kodem:

size=${#1}

Może się zdarzyć, że chcesz po prostu sprawdzić argument o zerowej długości i nie musisz przechowywać zmiennej. Wierzę, że możesz użyć tego rodzaju składni:

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

Zobacz GNU i wooledge, aby uzyskać pełniejszą listę wyrażeń warunkowych Bash.

JGFMK
źródło
11

Korzystając z podanego przykładu

#KISS (Keep it simple stupid)
size=${#myvar}
echo $size
thistleknot
źródło
9

Oto kilka sposobów obliczania długości zmiennej:

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

i aby ustawić wynik w innej zmiennej, wystarczy przypisać powyższą komendę z powrotem do innej zmiennej w następujący sposób:

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

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

Mukesh Shakya
źródło