Ich wäre Ihnen für Ihre Hilfe bei folgendem Problem dankbar:
Ich versuche, ein Array festzulegen, das eine Variable als Teil des Array-Namens enthält, Beispiel: Arr_$COUNTER
(wobei $COUNTER
basierend auf einer Schleifenanzahl geändert wird)
Alle möglichen Methoden, die ich ausprobiert habe, führten zu einem Fehler, wie etwa „falsche Ersetzung“ oder „Syntaxfehler bei unerwartetem Token“.
Hier ist der gesamte Ablauf:
Es gibt eine Datei, die mehrere Zeilen enthält. Jede Zeile hat 6 Werte, die durch Leerzeichen getrennt sind
10 20 30 40 50 60 100 200 300 400 500 600
Das Skript soll jede Zeile aus der Datei lesen und als Array deklarieren (mit der Zeilennummer, die die Variable ist).
Als Test sollte jeder Wert ausgedruckt werden und eventuell wird für jeden Wert eine andere Funktion ausgeführt.
#!/bin/bash COUNTER=1 LINES=`wc -l VALUES_FILE.txt | awk '{print $1}'` echo "Total number of lines "$LINES echo while [ $COUNTER -le $LINES ] do echo "Counter value is $COUNTER" field=`awk "NR == $COUNTER" VALUES_FILE.txt` echo "Field = $field" declare -a "arr$COUNTER=($field)" echo "arr$COUNTER[0] = ${arr$COUNTER[0]}" echo "arr$COUNTER[1] = ${arr$COUNTER[1]}" echo "arr$COUNTER[2] = ${arr$COUNTER[2]}" echo "arr$COUNTER[3] = ${arr$COUNTER[3]}" echo "arr$COUNTER[4] = ${arr$COUNTER[4]}" echo "arr$COUNTER[5] = ${arr$COUNTER[5]}" let COUNTER=COUNTER+1 echo done echo "The End" echo
Hier ist das Ergebnis:
Gesamtzahl der Zeilen 2 Der Zählerwert ist 1 Feld = 10 20 30 40 50 60 ./sort.sh: Zeile 12: arr$COUNTER[0] = ${arr$COUNTER[0]}: ungültige Ersetzung Das Ende
Was muss geändert/repariert werden, damit es ordnungsgemäß funktioniert?
Dank !
Antwort1
Einige Ideen:
Eine „Parametererweiterung“ eines Variablenwertes (der ${...}-Teil):
echo "arr$COUNTER[0] = ${arr$COUNTER[0]}"
wird nicht funktionieren. Sie können es umgehen, indem Sie eval verwenden (aber ich empfehle es nicht):
eval echo "arr$COUNTER[0] = \${arr$COUNTER[0]}"
Diese Zeile könnte wie folgt geschrieben werden:
i="arr$COUNTER[0]"; echo "$i = ${!i}"
Dies wird in Bash als Indirektion (das !) bezeichnet.
Ein ähnliches Problem tritt mit dieser Zeile auf:
declare -a "arr$COUNTER=($field)"
Dies sollte in zwei Zeilen aufgeteilt und eval verwendet werden:
declare -a "arr$COUNTER" eval arr$COUNTER\=\( \$field \)
Auch hier empfehle ich (in diesem Fall) nicht, eval zu verwenden.
Da Sie die gesamte Datei in den Speicher der Shell lesen, können wir auch eine einfachere Methode verwenden, um alle Zeilen in ein Array zu bekommen:
readarray -t lines <"VALUES_FILE.txt"
Das sollte schneller sein, als awk für jede Zeile aufzurufen.
Ein Skript mit all dem oben genannten könnte wie folgt aussehen:
#!/bin/bash
valfile="VALUES_FILE.txt"
readarray -t lines <"$valfile" ### read all lines in.
line_count="${#lines[@]}"
echo "Total number of lines $line_count"
for ((l=0;l<$line_count;l++)); do
echo "Counter value is $l" ### In which line are we?
echo "Field = ${lines[l]}" ### kepth only to help understanding.
k="arr$l" ### Build the variable arr$COUNTER
IFS=" " read -ra $k <<<"${lines[l]}" ### Split into an array a line.
eval max=\${#$k[@]} ### How many elements arr$COUNTER has?
#echo "field $field and k=$k max=$max" ### Un-quote to "see" inside.
for ((j=0;j<$max;j++)); do ### for each element in the line.
i="$k[$j]"; echo "$i = ${!i}" ### echo it's value.
done
done
echo "The End"
echo
Dennoch könnte AWK schneller sein, wenn wir das, was Sie benötigen, in AWK ausführen könnten.
Eine ähnliche Verarbeitung könnte in awk durchgeführt werden. Angenommen, die 6 Werte werden als IP verwendet (4 davon) und die anderen beiden sind eine Zahl und eine Epochenzeit.
Nur ein sehr einfaches Beispiel eines AWK-Skripts:
#!/bin/sh
valfile="VALUES_FILE.txt"
awk '
NF==6 { printf ( "IP: %s.%s.%s.%s\t",$1,$2,$3,$4)
printf ( "number: %s\t",$5+2)
printf ( "epoch: %s\t",$6)
printf ( "\n" )
}
' "$valfile"
Stellen Sie einfach eine neue Frage mit den Einzelheiten.
Antwort2
Sie können die Variablenindirektion verwenden, wenn Sie einer Variablen sowohl den Namen als auch den Index zuweisen:
s="arr$COUNTER[0]"
echo "arr$COUNTER[0] = ${!s}"
Antwort3
Die Standardmethode zum Speichern mehrdimensionaler Array-Daten in einem Array der Dimension 1 besteht darin, jede Zeile an einem bestimmten Offset im Array zu speichern.
Das Element (i,j)
befindet sich am Index, i*m + j
wobei i
der nullbasierte Zeilenindex, j
der nullbasierte Spaltenindex und m
die Anzahl der Spalten ist.
Dies erleichtert auch das Einlesen der Daten, da wir einfach Ihre Eingabedatei nehmen, alle Leerzeichen in Zeilenumbrüche ändern und verwenden können readarray
.
In der Befehlszeile:
$ readarray -t arr < <( tr -s ' ' '\n' <data )
$ printf '%s\n' "${arr[@]}"
10
20
30
40
50
60
100
200
300
400
500
600
Wir können die Anzahl der Spalten in den Daten ermitteln mit
$ m=$( awk '{ print NF; exit }' <data )
Und die Anzahl der Zeilen:
$ n=$( wc -l <data )
Anschließend können wir wie üblich in einer Doppelschleife über Spalten und Zeilen iterieren:
for (( i = 0; i < n; ++i )); do
for (( j = 0; j < m; ++j )); do
printf '%4d' "${arr[i*m + j]}"
done
printf '\n'
done
Für die gegebenen Daten würde dies ergeben
10 20 30 40 50 60
100 200 300 400 500 600
Idealerweise verwenden Sie eine Sprache wie Perl, Python oder C, die mehrdimensionale Arrays unterstützt. Das heißt, wenn Sie tatsächlich den gesamten Datensatz im Speicher speichern müssen und ihn nicht zeilenweise verarbeiten können.
Für die zeilenweise Verarbeitung awk
wäre ein guter Kandidat zum Ersetzen von bash
(beliebigSprache wäre ein guter Kandidat, um eine Shell für jede Art der Datenverarbeitung zu ersetzen):
awk '{ for (i = 1; i <= NF; ++i) printf("%4d", $i); printf("\n") }' data
Antwort4
Sie können Namen generieren mit eval
, z. B.
eval declare -a '"arr'$COUNTER'=($field)"'
Zitieren Sie im Wesentlichen alle Metazeichen außer denen, die Sie auswerten möchten.
Wenn also $COUNTER
1 ist, würde Ihr Skript
declare -a "arr1=($field)"
Weiterführende Literatur: