Eu tenho o seguinte problema. Eu tenho uma matriz arr
com alguns valores. Quero ordenar cada valor em um conjunto de arrays diferentes - e já declarados - earr$j
, ou seja, arr[0]
into earr1
, arr[1]
into earr2
e, em geral, arr[j-1]
into earr$j
. (Mais tarde, farei com que os elementos de arr
s semelhantes sejam anexados como os próximos elementos dos earr$j
s de destino). Tentei fazer isso com o seguinte trecho de código (que faz parte de um trecho maior de código):
for j in $(seq 1 $number_of_elements); do earr$j+=(${arr[j-1]}); done
Disseram-me (veja minha postagem "https://unix.stackexchange.com/questions/675454/for-loop-and-appending-over-list-of-arrays") que parece que pretendo criar um 2 Matriz -D (que o Bash não suporta). Enfatizo que esta não é minha intenção, independentemente do que o resultado do meu mau uso da sintaxe Bash possa sugerir. Estou repassando isso porque meu post antigo realmente descreveu mal o problema.
Responder1
Para responder à pergunta literalmente, aqui, normalmente é um trabalho para eval
:
for i in "${!arr[@]}"; do
eval '
earr'"$i"'+=( "${arr[i]}" )
'
done
eval
é perigoso, mas seguro se usado corretamente. Uma boa abordagem para limitar o risco de erro é citar tudo entre aspas simples, exceto as partes que definitivamente precisam sofrer alguma expansão e certificar-se de que a parte que não está entre aspas simples (aqui, $i
que está entre aspas duplas e será expandida para o conteúdo da i
variável) está totalmente sob seu controle. Nesse caso, sabemos que $i
conterá apenas dígitos, portanto, não são dados aleatórios que eval
seriam avaliados como código shell (compare com ${arr[i]}
isso, você definitivamente não deseja deixar de fora as aspas simples).
Ainda não vejo por que você diria que arrays 2D não são apropriados. Em ksh93
( bash
copiou a maior parte de sua sintaxe do ksh93, mas não copiou matrizes multidimensionais), você faria:
for i in "${!arr[@]}"; do
earr[i]+=( "${arr[i]}" )
done
De qualquer forma, a menos que haja um motivo específico para você precisar usar um shell, concordo com @cas que parece que seria melhor usar uma linguagem de programação adequada, como perl
ou python
.
Responder2
Aqui está um exemplo de como fazer o que você descreveu usando perl e uma estrutura de dados Hash-of-Array-of-Arrays (HoAoA).
Para ajudar na compreensão disso, as seguintes páginas de manual serão úteis: perldata
(tipos de dados perl), perldsc
(estruturas de dados), perllol
(lol = listas de listas), perlref
(referências) e perlreftut
(tutorial para referências). Você também pode obter detalhes sobre funções perl específicas com o perldoc
comando - por exemplo, perldoc -f opendir
ou perldoc -f grep
.
Observe que sort
e grep
usados no script são funções perl integradas. Eles sãonãoas ferramentas de linha de comando sort
e grep
... você pode chamá-las de perl se quiser (com crases ou qx
aspas, ou a system()
função, ou com a open()
função para abrir um canal, e várias outras maneiras). Use perldoc
para obter detalhes sobre tudo isso e muito mais.
$ 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: o texto acima foi escrito para processar arquivos de dados simples delimitados por vírgulas. Para CSV real, com todas as suas peculiaridades e complicações (como campos multilinhas entre aspas duplas e vírgulas incorporadas), use oTexto::CSVmódulo. Este é um módulo de biblioteca de terceiros que não está incluído na distribuição principal do Perl. No Debian e distros relacionadas você pode instalar isso com apt-get install libtext-csv-perl libtext-csv-xs-perl
. Outras distros provavelmente têm nomes de pacotes semelhantes. Ou você pode instalá-lo com cpan
(uma ferramenta para instalar e gerenciar módulos de biblioteca que ESTÁ incluída no núcleo perl).
Observe também: o script acima usa oDados::Despejo. Este é um módulo de terceiros útil para despejar dados estruturados. Infelizmente, ele não está incluído como parte da biblioteca principal do Perl. No Debian apt-get install libdata-dump-perl
etc. Outras distros terão um nome de pacote semelhante. E, como último recurso, você pode instalá-lo com o cpan
.
De qualquer forma, com a seguinte estrutura de pastas e arquivos 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
executar o script HoHoA.pl produz a seguinte saída:
$ ./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