For 循環 - 使用數組名稱中的迭代器附加到數組

For 循環 - 使用數組名稱中的迭代器附加到數組

我有以下問題。我有一個包含一些值的陣列arr。我想將每個值排序到一組不同的且已聲明的數組中earr$j,即arr[0]into earr1arr[1]intoearr2以及一般情況下的arr[j-1]into earr$j。 (稍後,我會將類似 s 的元素arr附加為目標earr$js 的下一個元素)。我嘗試使用以下程式碼片段(這是更大的程式碼片段的一部分)來執行此操作:

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]},您絕對不想省略單引號)。

我仍然不明白為什麼你會說二維數組不合適。在ksh93bash從 ksh93 複製了大部分語法,但沒有複製多維數組)中,您可以執行以下操作:

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

無論如何,除非有特定原因需要使用 shell,否則我同意 @cas 的觀點,聽起來你最好使用適當的程式語言,例如perlpython

答案2

以下是如何使用 perl 和陣列雜湊 (HoAoA) 資料結構執行您所描述的操作的範例。

為了幫助理解這一點,以下手冊頁將很有用:perldata(perl 資料類型)、 perldsc(資料結構)、perllol(lol = 清單清單)、perlref(參考資料)和perlreftut(參考資料教學)。您也可以使用perldoc命令(例如perldoc -f opendir或 )獲取有關特定 perl 函數的詳細資訊perldoc -f grep

請注意,腳本中使用的sortgrep是內建的 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

相關內容