For-Schleife - Anhängen an Arrays mit Iterator im Array-Namen

For-Schleife - Anhängen an Arrays mit Iterator im Array-Namen

Ich habe folgendes Problem. Ich habe ein Array arrmit einigen Werten. Ich möchte jeden Wert in eine Reihe verschiedener – und bereits deklarierter – Arrays sortieren earr$j, also arr[0]in earr1, arr[1]in earr2und allgemein arr[j-1]in earr$j. (Später werde ich die Elemente ähnlicher arrs als nächste Elemente der Ziel- s anhängen lassen earr$j.) Ich habe dies mit dem folgenden Codeausschnitt versucht (der Teil eines größeren Codestücks ist):

for j in $(seq 1 $number_of_elements); do earr$j+=(${arr[j-1]}); done

Mir wurde gesagt (siehe meinen Beitrag „https://unix.stackexchange.com/questions/675454/for-loop-and-appending-over-list-of-arrays“), dass es so aussieht, als ob ich beabsichtige, ein 2D-Array zu erstellen (was Bash nicht unterstützt). Ich betone, dass dies nicht meine Absicht ist, unabhängig davon, was das Ergebnis meiner schlechten Verwendung der Bash-Syntax vermuten lässt. Ich poste dies erneut, da mein alter Beitrag das Problem wirklich schlecht beschrieben hat.

Antwort1

Um die Frage wörtlich zu beantworten: Das ist normalerweise ein Job für eval:

for i in "${!arr[@]}"; do
  eval '
    earr'"$i"'+=( "${arr[i]}" )
  '
done

evalist gefährlich, aber sicher, wenn es richtig verwendet wird. Ein guter Ansatz, um das Fehlerrisiko zu begrenzen, besteht darin, alles in einfache Anführungszeichen zu setzen, außer die Teile, die unbedingt erweitert werden müssen, und sicherzustellen, dass der Teil, der nicht in einfachen Anführungszeichen steht (hier $isteht er stattdessen in doppelten Anführungszeichen und wird zum Inhalt der iVariable erweitert), vollständig unter Ihrer Kontrolle ist. In diesem Fall wissen wir, dass $ier nur Ziffern enthält, also sind das keine zufälligen Daten, die evalals Shell-Code ausgewertet würden (zum Vergleich: ${arr[i]}Sie möchten die einfachen Anführungszeichen auf keinen Fall weglassen).

Ich verstehe immer noch nicht, warum Sie sagen, dass 2D-Arrays nicht geeignet sind. In ksh93( bashhat den Großteil der Syntax von ksh93 kopiert, aber keine mehrdimensionalen Arrays kopiert) würden Sie Folgendes tun:

for i in "${!arr[@]}"; do
  earr[i]+=( "${arr[i]}" )
done

Sofern es keinen besonderen Grund für die Verwendung einer Shell gibt, stimme ich mit @cas überein, dass es sich anhört, als wäre die Verwendung einer richtigen Programmiersprache wie perloder besser python.

Antwort2

Hier ist ein Beispiel, wie Sie das von Ihnen beschriebene mit Perl und einer Hash-of-Array-of-Arrays (HoAoA)-Datenstruktur erreichen.

Zum besseren Verständnis sind die folgenden Manpages hilfreich: perldata(Perl-Datentypen), perldsc(Datenstrukturen), perllol(lol = Listen von Listen), perlref(Referenzen) und perlreftut(Tutorial für Referenzen). Sie können mit dem Befehl auch Details zu bestimmten Perl-Funktionen erhalten perldoc– z. B. perldoc -f opendiroder perldoc -f grep.

Beachten Sie, dass die im Skript verwendeten sortund grepeingebaute Perl-Funktionen sind. Sie sindnichtdie sortund grepKommandozeilentools... Sie können diese von Perl aus aufrufen, wenn Sie möchten (mit Backticks oder qxAnführungszeichen, oder der system()Funktion, oder mit der open()Funktion zum Öffnen einer Pipe und auf verschiedene andere Arten). perldocWeitere Informationen zu all diesen und weiteren finden Sie unter.

$ cat HoAoA.pl 
#!/usr/bin/perl

use strict;
use Data::Dump qw(dd);

# $h is a ref to Hash-of-Array-ofArrays (HoAoA).
#
# This will be a data structure with the directory names
# (Folder1, Folder2, Folder3) as the hash keys of the top-level
# hash.  Each element of that hash will be an array where the
# indexes are the line numbers of the data.txt files in each
# of those directories. The data in these second-level arrays
# will be an array containing the three values in each line of
# data.txt: $$h{directory}[line number][element]
my $h;

# get the directory name from the first command line arg, default to ./
my $dir = shift // './';

# get a list of subdirectories that contain 'data.txt',
# excluding . and ..
opendir(my $dh, "$dir") || die "Couldn't open directory $dir: $!\n";
my @dirs = sort grep { $_ !~ /^\.+$/ && -d $_ && -f "$_/data.txt" } readdir($dh);
closedir($dh);

dd \@dirs;   # Data::Dump's dd function is great for showing what's in an array
print "\n";

foreach my $d (@dirs) {
  my $f = "$d/data.txt";
  open(my $fh,"<",$f) || die "Couldn't open file $f: $!\n";
  my $lc=0;  # line counter
  while(<$fh>) {
    chomp;   # strip trailing newline char at end-of-line
    my @row = split /\s*,\s*/;   # assume simple comma-delimited values
    push @{ $$h{$d}[$lc++] }, @row;
  }
  close($fh);
}

# dd is even better for showing complex structured data
dd $h;
print "\n";

# show how to access individual elements, e.g. by changing the
# zeroth element of line 0 of 'Folder1' to 999.
$$h{'Folder1'}[0][0] = 999;

dd $h;
print "\n";

# show how to print the data without using Data::Dump
# a loop like this can also be used to process the data.
# You could also process the data in the main loop above
# as the data is being read in.
foreach my $d (sort keys %{ $h }) {   # `foreach my $d (@dirs)` would work too
  print "$d/data.txt:\n";
  foreach my $lc (keys @{ $$h{$d} }) {
    print "  line $lc: ", join("\t",@{ $$h{$d}[$lc] }), "\n";
  }
  print "\n";
}

Hinweis: Das Obige ist für die Verarbeitung einfacher, durch Kommas getrennter Datendateien gedacht. Für echte CSV-Dateien mit all ihren Eigenheiten und Komplikationen (wie mehrzeilige Felder in Anführungszeichen mit eingebetteten Kommas) verwenden Sie dasText::CSVModul. Dies ist ein Bibliotheksmodul eines Drittanbieters, das nicht in der Perl-Kerndistribution enthalten ist. Unter Debian und verwandten Distributionen können Sie dies mit installieren apt-get install libtext-csv-perl libtext-csv-xs-perl. Andere Distributionen haben wahrscheinlich ähnliche Paketnamen. Oder Sie können es mit installieren cpan(ein Tool zum Installieren und Verwalten von Bibliotheksmodulen, das im Perl-Kern enthalten ist).

Beachten Sie auch: das obige Skript verwendet dieDatenmüll. Dies ist ein Drittanbietermodul, das zum Dumpen strukturierter Daten nützlich ist. Leider ist es nicht Teil der Perl-Kernbibliothek. Unter Debian usw. apt-get install libdata-dump-perlhaben andere Distributionen einen ähnlichen Paketnamen. Und als letzte Möglichkeit können Sie es mit installieren cpan.

Wie auch immer, mit der folgenden Ordnerstruktur und den folgenden data.txt-Dateien:

$ tail */data.txt
==> Folder1/data.txt <==
1,2,3
4,5,6
7,8,9

==> Folder2/data.txt <==
7,8,9
4,5,6
1,2,3

==> Folder3/data.txt <==
9,8,7
6,5,4
3,2,1

Das Ausführen des Skripts HoHoA.pl erzeugt die folgende Ausgabe:

$ ./HoAoA.pl 
["Folder1", "Folder2", "Folder3"]

{
  Folder1 => [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
  Folder2 => [[7, 8, 9], [4, 5, 6], [1, 2, 3]],
  Folder3 => [[9, 8, 7], [6, 5, 4], [3, 2, 1]],
}

{
  Folder1 => [[999, 2, 3], [4, 5, 6], [7, 8, 9]],
  Folder2 => [[7, 8, 9], [4, 5, 6], [1, 2, 3]],
  Folder3 => [[9, 8, 7], [6, 5, 4], [3, 2, 1]],
}

Folder1/data.txt:
  line 0: 999   2       3
  line 1: 4     5       6
  line 2: 7     8       9

Folder2/data.txt:
  line 0: 7     8       9
  line 1: 4     5       6
  line 2: 1     2       3

Folder3/data.txt:
  line 0: 9     8       7
  line 1: 6     5       4
  line 2: 3     2       1

verwandte Informationen