我有以下问题。我有一个包含一些值的数组arr
。我想将每个值排序到一组不同的且已声明的数组中earr$j
,即arr[0]
into earr1
、arr[1]
intoearr2
以及一般情况下的arr[j-1]
into earr$j
。 (稍后,我会将类似 s 的元素arr
附加为目标earr$j
s 的下一个元素)。我尝试使用以下代码片段(这是更大的代码片段的一部分)来执行此操作:
for j in $(seq 1 $number_of_elements); do earr$j+=(${arr[j-1]}); done
有人告诉我(请参阅我的帖子“https://unix.stackexchange.com/questions/675454/for-loop-and-appending-over-list-of-arrays”)看起来我打算创建一个 2 -D 数组(Bash 不支持)。我强调,这不是我的本意,无论我对 Bash 语法的不当使用会带来什么后果。我重新发布此内容是因为我的旧帖子确实描述了这个问题很糟糕。
答案1
从字面上回答这个问题,这通常是以下工作eval
:
for i in "${!arr[@]}"; do
eval '
earr'"$i"'+=( "${arr[i]}" )
'
done
eval
是危险的,但如果使用得当则安全。限制错误风险的一个好方法是用单引号引用所有内容,除了肯定需要进行一些扩展的部分,并确保不在单引号内的部分(这里$i
是双引号,并将扩展为变量的内容i
)完全在您的控制之下。在这种情况下,我们知道$i
将仅包含数字,因此这不是eval
将计算为 shell 代码的随机数据(相比之下${arr[i]}
,您绝对不想省略单引号)。
我仍然不明白为什么你会说二维数组不合适。在ksh93
(bash
从 ksh93 复制了大部分语法,但没有复制多维数组)中,您可以执行以下操作:
for i in "${!arr[@]}"; do
earr[i]+=( "${arr[i]}" )
done
无论如何,除非有特定原因需要使用 shell,否则我同意 @cas 的观点,听起来你最好使用适当的编程语言,例如perl
或python
。
答案2
下面是如何使用 perl 和数组哈希 (HoAoA) 数据结构执行您所描述的操作的示例。
为了帮助理解这一点,以下手册页将很有用:perldata
(perl 数据类型)、 perldsc
(数据结构)、perllol
(lol = 列表列表)、perlref
(参考资料)和perlreftut
(参考资料教程)。您还可以使用perldoc
命令(例如perldoc -f opendir
或 )获取有关特定 perl 函数的详细信息perldoc -f grep
。
请注意,脚本中使用的sort
和grep
是内置的 perl 函数。他们是不是sort
和命令行工具grep
...如果您愿意,您可以从 perl 调用这些工具(使用反引号或qx
引号,或system()
函数,或使用open()
打开管道的函数,以及其他几种方式)。用于perldoc
了解所有这些以及更多内容的详细信息。
$ 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";
}
注意:上面是为了处理简单的逗号分隔数据文件而编写的。对于实际的 CSV,及其所有怪癖和复杂性(例如带有嵌入逗号的多行双引号字段),请使用文本::CSV模块。这是一个第三方库模块,不包含在核心 Perl 发行版中。在 Debian 和相关发行版上,您可以使用apt-get install libtext-csv-perl libtext-csv-xs-perl
.其他发行版可能有类似的包名称。或者您可以使用cpan
(安装和管理 perl 核心中包含的库模块的工具)来安装它。
另请注意:上面的脚本使用数据::转储。这是一个第三方模块,可用于转储结构化数据。不幸的是,它没有包含在 Perl 核心库中。在 Debian 等上apt-get install libdata-dump-perl
。其他发行版将具有类似的包名称。而且,作为最后的手段,您可以使用cpan
.
无论如何,具有以下文件夹结构和 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
运行 HoHoA.pl 脚本会产生以下输出:
$ ./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