![Wie kann ich die Zeichenfolgenlänge der Indexnummer eines Bash-Arrays herausfinden?](https://rvso.com/image/231058/Wie%20kann%20ich%20die%20Zeichenfolgenl%C3%A4nge%20der%20Indexnummer%20eines%20Bash-Arrays%20herausfinden%3F.png)
Angenommen, eine Funktion in Bash generiert ein einfaches Array unbekannter Länge. Das Array wird transformiert und auf dem Bildschirm ausgegeben: jede Zeile mit einer kleinen Indexnummer daneben, aus der man etwas auswählen kann. Der Platz ist knapp, die Präsentation muss tadellos sein, der Werkzeugsatz ist … ähm, schnörkellos, würde ich sagen.
Wie erfahre ich,String-LängevonIndexnummer selbst(zu Formatierungszwecken), was kein Wert im Array ist. Wenn das Array beispielsweise 334.345 Elemente hätte (dessen letztes Element Sie mit drucken würden echo ${hugearray[334344]}
), dann 6
wäre das, wonach ich suche.
Ich muss bei Bash bleiben, da es ncurses
im Zielsystem weder Tput noch andere „ausgefallene“ Formatierungsprogramme gibt. Nur Bash selbst.
${#WORD[@]}
Gibt mir die Gesamtzahl der Elemente, aus denen ich eine rudimentäre Konstruktion erstellt habe, um sie zu erhalten:
$〉pkglist+=($(brew list -1)) # ← I'm testing on a Mac.
$〉printf "%s\n" ${pkglist[@]}
automake
…
xquartz
$〉echo ${#pkglist[@]}
68
$〉indexlength=${#pkglist[@]}
$〉echo ${#indexlength}
2
Es funktioniert, ist aber nicht gerade elegant.Zu meiner Verteidigung: Ich bin kein Entwickleraber ich hatte gehofft, dass es schon einen besseren, einfacheren Weg gibt, verschachtelte Indirektionen oder irgendeinen anderen Bash-Voodoo. Aber dienormales Zeugdas auf die tatsächlichen Daten einwirkt, war schon verwirrend genug, ich dachte, es wäre am besten, zu fragen, bevor man etwas bereut (das Zielsystem hat NUR das Root-Konto). Gibt es das?
Danke!
Antwort1
Wenn Sie macOS verwenden, sollten Sie Zugriff auf zsh haben, sodass Sie Bash nicht verwenden müssen.
In zsh ist die Array-Länge genau $#array
wie in csh und Sie können Parametererweiterungsoperatoren verschachteln, sodass Sie ${#${#array}}
¹ ausführen können, um die Länge der Dezimaldarstellung der Länge des Arrays zu erhalten.
$ a=(qwe asdasd qewqw)
$ () {printf "%${#${#a}}d %s\n" "${@:^a}"} {1..$#a}
1 qwe
2 asdasd
3 qweqe
$ a=( {a..m} )
$ () {printf "%${#${#a}}d %s\n" "${@:^a}}" {1..$#a}
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
11 k
12 l
13 m
Beachten Sie, dass alle array=(...)
, array+=(...)
, {x..y}
von Bash aus Zsh kopiert wurden (oft indirekt über Ksh). {1..$expansion}
funktioniert in Zsh oder Ksh, aber nicht in Bash.
Der ${a:^b}
Array-Zipping-Operator und () {code} args
anonyme Funktionen sind weiterhin spezifisch für zsh.
Wenn Sie Bash verwenden müssen, ist die Vorgehensweise in zwei Schritten wie von Ihnen wahrscheinlich die beste Lösung.
Sie könnten dies in einer Erweiterung mit tun $(((l=${#array[@]}),SHLVL[l=\${#l}],l))
und sich dabei auf die verzögerte Auswertung der Array-Indizes verlassen, aber das trägt nicht zur Lesbarkeit bei und ist möglicherweise nicht zukunftssicher.
Beachten Sie auch, dass in Bash (wie auch in KSH, dessen Array-Design Bash leider kopiert hat) Arrays keine echten Arrays sind, sondern eher assoziative Arrays, deren Schlüssel positive Ganzzahlen sind und bei 0 beginnen (nicht 1 wie in den meisten anderen Shells)
bash-5.1$ a=( [5]=123 [123]=qwe [4567]=asd )
bash-5.1$ typeset -p a
declare -a a=([5]="123" [123]="qwe" [4567]="asd")
bash-5.1$ echo "${#a[@]}"
3
3
ist die Anzahl der Elemente im Array, aber nicht der höchste Schlüsselwert, den Sie benötigen:
bash-5.1$ keys=("${!a[@]}")
bash-5.1$ echo "${keys[@]: -1}"
4567
Wenn Sie also ausgerichtete Schlüssel und Werte drucken möchten, benötigen Sie etwas wie
printarray() {
local -n arr="$1"
local -a keys=("${!arr[@]}")
local highest_key="${keys[@]: -1}"
local key_width="${#highest_key}"
local key
for key in "${keys[@]}"; do
printf '%*d %s\n' "$key_width" "$key" "${arr[key]}"
done
}
bash-5.1$ printarray a
5 123
123 qwe
4567 asd
(funktioniert nicht mit der alten Bash von macOS; local -n
benötigt Bash 4.3 oder neuer)
¹ ${#$#array}
funktioniert nicht, da es als angenommen wird ${#${$#array}}
, die Länge von , nachdem es mit dem Korn-Operator von Anfang an $$
entfernt wurde .array
${var#pattern}
Antwort2
Wäre dies eine Art BASH-Vodoo, wie Sie es genannt haben? - keine zusätzliche Variable,
: $((${#pkglist[*]}-1)); echo ${#_};
aber $_ kann zu Debugging-Problemen führen - lange Skripte
Antwort3
BEARBEITEN:Ich entschuldige mich; wie @ilkkachu mich freundlicherweise erinnert hat, ist der einfachste Weg, die Länge einer Variablen zu ermitteln, mit ${#variable}
, ohne die Verwendung externer Tools. Bitte ignorieren Sie diese Antwort; ich werde sie in Kürze löschen. Schande über mich!
Um zu ermitteln, wie viele Ziffern eine Zahl hat, können Sie den Logarithmus der Basis 10 der Zahl plus eins verwenden und dann den Dezimalteil weglassen, aber der Binärrechner bc
hat hierfür eine praktische Funktion:
echo "length(334344)" | bc
gibt wie gewünscht 6 aus. Wenn das Argument length
keine Ganzzahl ist, wird die tatsächliche Anzahl der Ziffern ohne Dezimalkomma gezählt (d. h. es length(3.4560)
wird 5 ausgegeben).
Um eine Bash-Variable einzufügen, echo
verwenden Sie die Bash-Variablenerweiterung innerhalb der Anführungszeichen:
x=334344
echo "length($x)" | bc
Wir können das als Funktion schreiben, um es einfach aufrufen zu können:
function num_digits {
#determine hoy many digits has a number
#param number
local number="$1"
echo "length($number)" | bc
}
dann benutze es wie folgt:
lendigits=$(num_digits 334344)
oder mit einer Variablen
x=334344
lendigits=$(num_digits $x)
Notiz:Wenn bc
es in Ihrer Linux-Distribution noch nicht installiert ist, können Sie es mit dem Paketmanager der Distribution hinzufügen.
Anmerkung 2:Natürlich können Sie den Ausdruck direkt ohne Funktion verwenden:
x=334344
lendigits=$(echo "length($x)" | bc)
ist aber weniger lesbar und ich bevorzuge ein etwas längeres Skript, in dem spezielle Operationen identifiziert und in einer Funktion gekapselt sind, die ich später zur Verwendung in anderen Skripten kopieren kann.