Tengo el siguiente problema. Tengo una matriz arr
con algunos valores. Quiero ordenar cada valor en un conjunto de matrices diferentes (y ya declaradas) earr$j
, es decir, arr[0]
en earr1
, arr[1]
en earr2
y, en general, arr[j-1]
en earr$j
. (Más adelante, haré que arr
se agreguen los elementos de s similares como los siguientes elementos de los earr$j
s objetivo). Intenté hacerlo con el siguiente fragmento de código (que es parte de un fragmento de código más grande):
for j in $(seq 1 $number_of_elements); do earr$j+=(${arr[j-1]}); done
Me dijeron (consulte mi publicación "https://unix.stackexchange.com/questions/675454/for-loop-and-appending-over-list-of-arrays") que parece que tengo la intención de crear un 2 -D matriz (que Bash no admite). Hago hincapié en que esta no es mi intención, independientemente de lo que pueda sugerir el resultado de mi mal uso de la sintaxis de Bash. Vuelvo a publicar esto porque mi publicación anterior realmente describía mal el problema.
Respuesta1
Para responder la pregunta literalmente, este suele ser un trabajo para eval
:
for i in "${!arr[@]}"; do
eval '
earr'"$i"'+=( "${arr[i]}" )
'
done
eval
Es peligroso, pero seguro si se usa correctamente. Un buen enfoque para limitar el riesgo de error es citar todo entre comillas simples, excepto las partes que definitivamente necesitan someterse a alguna expansión y asegurarse de que la parte que no está entre comillas simples (aquí, $i
que está entre comillas dobles y se ampliará a el contenido de la i
variable) está totalmente bajo su control. En este caso, sabemos $i
que contendrá solo dígitos, por lo que no son datos aleatorios que eval
se evaluarían como código de shell (compárelo con ${arr[i]}
eso que definitivamente no desea dejar fuera de las comillas simples).
Todavía no veo por qué dices que las matrices 2D no son apropiadas. En ksh93
( bash
copié la mayor parte de su sintaxis de ksh93, pero no copié matrices multidimensionales), harías:
for i in "${!arr[@]}"; do
earr[i]+=( "${arr[i]}" )
done
En cualquier caso, a menos que haya una razón específica por la que necesites usar un shell, estoy de acuerdo con @cas en que parece que sería mejor que usaras un lenguaje de programación adecuado como perl
o python
.
Respuesta2
A continuación se muestra un ejemplo de cómo hacer lo que usted describió usando Perl y una estructura de datos Hash-of-Array-of-Arrays (HoAoA).
Para ayudar a comprender esto, las siguientes páginas de manual serán útiles: perldata
(tipos de datos de Perl), perldsc
(estructuras de datos), perllol
(risas = listas de listas), perlref
(referencias) y perlreftut
(tutorial para referencias). También puede obtener detalles sobre funciones específicas de Perl con el perldoc
comando, por ejemplo perldoc -f opendir
o perldoc -f grep
.
Tenga en cuenta que sort
y grep
se utilizan en el script son funciones integradas de Perl. Ellos sonnolas herramientas de línea de comandos sort
y grep
... puede llamarlas desde Perl si lo desea (con comillas invertidas o qx
comillas, o la system()
función, o con la open()
función para abrir una tubería, y varias otras formas). Úselo perldoc
para obtener detalles sobre todo esto y más.
$ 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";
}
Nota: lo anterior está escrito para procesar archivos de datos simples delimitados por comas. Para CSV real, con todas sus peculiaridades y complicaciones (como campos de varias líneas entre comillas dobles y comas incrustadas), utilice elTexto::CSVmódulo. Este es un módulo de biblioteca de terceros que no se incluye con la distribución principal de Perl. En Debian y distribuciones relacionadas, puede instalar esto con apt-get install libtext-csv-perl libtext-csv-xs-perl
. Otras distribuciones probablemente tengan nombres de paquetes similares. O puede instalarlo con cpan
(una herramienta para instalar y administrar módulos de biblioteca que SE incluye con Perl Core).
También tenga en cuenta: el script anterior utiliza elDatos::Volcado. Este es un módulo de terceros que es útil para volcar datos estructurados. Desafortunadamente, no está incluido como parte de la biblioteca principal de Perl. En Debian apt-get install libdata-dump-perl
, etc. Otras distribuciones tendrán un nombre de paquete similar. Y, como último recurso, puedes instalarlo con cpan
.
De todos modos, con la siguiente estructura de carpetas y archivos data.txt:
$ 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
La ejecución del script HoHoA.pl produce el siguiente resultado:
$ ./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