Forループ - 配列名にイテレータを使用して配列に追加する

Forループ - 配列名にイテレータを使用して配列に追加する

次の質問があります。いくつかの値を含む配列がありますarr。各値を異なる宣言された配列のセット、earr$jつまりarr[0]into earr1arr[1]into、earr2および通常はarr[j-1]intoでソートしたいと思いますearr$j。 (後でsと同じ要素をtarget sの次の要素arrとして追加します)。earr$j私は次のコードスニペット(より大きなコードスニペットの一部)を使用してこれを実行しようとしています。

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次元配列(Bashではサポートされていません)私は、Bash構文を不適切に使用した結果に関係なく、これが私の意図ではないことを強調します。以前の投稿でこの問題を正しく説明していないため、再公開します。

答え1

質問に文字通り答えるには、通常、次のように機能しますeval

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

eval危険ですが、正しく使用すると安全です。エラーのリスクを制限する良い方法は、確実に拡張が必要な​​部分を除いて、すべてを一重引用符で囲み、一重引用符の内側にない部分を確認すること$iです。変数のi)は完全にあなたの制御下にあります。この場合、$i数字のみが含まれることがわかっているため、これはevalシェルコードで評価される任意のデータではありません(逆に一重引用符を${arr[i]}省略したくありません)。

なぜ二次元配列が不適切だと言われるのか、まだ理解されていません。ksh93bashksh93の構文のほとんどをコピーしましたが、多次元配列はコピーしません)から、次のことができます。

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

とにかくシェルを使用する特別な理由がない場合は、@casの言葉に同意しますperlpython

答え2

以下は、Perl および HoAoA (Hash of Arrays) データ構造を使用して説明するタスクを実行する方法の例です。

これを理解するのに役立つように、次のマニュアルページが役に立ちます: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コアライブラリには含まれていません。 Debianapt-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

関連情報