Bash에서 숫자 범위로 두 배열 정렬

Bash에서 숫자 범위로 두 배열 정렬

작업하려는 요소 수와 동일한 수의 요소를 가진 두 개의 배열이 있습니다. 파일에서 두 개의 배열로 읽습니다(홀수 행은 배열 1로, 짝수 행은 배열 2로 이동).

arr1=("1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5")
arr2=("4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6")

이 데이터는 두 어레이의 동일한 위치에 있는 시즌 및 에피소드 수를 나타냅니다. 따라서 배열 1( arr1)은 시즌이고 배열 2( arr2)는 에피소드이며 요소 번호를 기준으로 정렬됩니다. 등등 ${arr1[0]}에 해당됩니다 .${arr2[0]}

제가 하고 싶은 것은 시즌별로 먼저 정렬한 다음 에피소드별로 정렬하는 것입니다. 따라서 원래 배열(어떤 항목이 어떤 요소인지 주석 포함)은 다음과 같습니다.

       1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16
arr1=("1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5")
arr2=("4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6")
       1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16

이 되다:

       2   9   1   4   10  12  3   12  13  14  5   16  6   7   8   15
arr1=("1" "1" "1" "2" "2" "3" "3" "3" "3" "3" "4" "5" "7" "7" "7" "7")
arr2=("1" "2" "4" "5" "9" "2" "3" "6" "8" "9" "7" "6" "1" "2" "3" "4")
       2   9   1   4   10  12  3   12  13  14  5   16  6   7   8   15

가능한 아이디어:

  1. i의 각 항목에 대해 ${arr1[@]}해당 요소가 ${arr2[n]}파일에 기록됩니다. 그런 다음 파일을 sort실행할 수 있습니다.
n="0"
for i in "${arr1[@]}"; do
    echo "${arr2[${n}]}" >> "${i}.txt"
    (( n++ ))
done

그러나 나는 디스크 쓰기가 포함되는 것을 피하고 싶습니다( sort필요하지 않은 경우).

  1. 데이터를 일종의 별도 배열로 정렬 하시겠습니까? 각 계절에는 고유한 배열이 있으며 비슷한 방법을 사용하여 정렬할 수 있지만 sort -n "${season1Arr[@]}"이것이 어떻게 수행되는지는 모르겠습니다.

  2. 데이터 처리 방식을 변경하시겠습니까? 입력 파일을 변경할 수는 없지만 처리 방법은 변경할 수 있습니다. 짝수/홀수 라인 번호를 기반으로 라인을 두 개의 배열로 읽는 대신 다른 방법으로 관리할 수 있을까요?

호환성을 위해 가능한 한 순수한 bash를 유지하려고 노력하고 있지만 외부 프로그램을 사용해야 할 가능성이 높다는 것을 알고 있습니다. 어떤 아이디어라도 감사드립니다.

답변1

다음은 주석에서 논의한 예입니다. Perl 및 연관 배열(해시)을 사용하지만 제가 언급한 자연 순서(간단한 숫자 또는 영숫자 순서, 예를 들어 1.10 이전) 대신 버전 순서를 사용합니다. 1.2, 즉 "series.episode"는 다음과 같습니다. 분명히 틀렸어요):

#!/usr/bin/perl

use strict;

use Sort::Versions;

my %data;
my ($key, $series, $episode);

while (<>) {
  chomp; # remove trailing newline from input

  if ($. % 2 == 1) {
    $series = $_;
  } else {
    $episode = $_;
    $key = "$series.$episode";
    $data{$key} = 1;
  };
}

print join(", ", sort { versioncmp($a, $b) } keys %data), "\n";

이것은 약간 단축될 수 있습니다. 실제로 필요한 유일한 변수는 입력 값이 사용되는 방법을 명확하게 하기 위해 사용된 %data해시 와 입니다 . BTW는 기본 입력/값/반복자입니다. 이는 Perl에서 많은 용도로 사용되며, 다른 변수가 제공되지 않으면 많은 함수 및 구문 요소가 이를 사용합니다. 이 스크립트에서는 루프가 읽는 현재 행의 값입니다 . "일반 변수"를 보고 검색합니다.$series$key$episode$_while (<>)man perlvar

#!/usr/bin/perl

use strict;
use Sort::Versions;

my (%data, $series);

while (<>) {
  chomp; 

  if ($. % 2 == 1) {
    $series = $_;
  } else {
    $data{"$series.$_"} = 1;
  };
}

print join(", ", sort { versioncmp($a, $b) } keys %data), "\n";

실행 예시(두 버전 모두 동일한 출력을 생성함):

$ ./sort-series-episode.pl input.txt  
1.1, 1.2, 1.4, 1.15, 2.5, 2.9, 3.2, 3.3, 3.6, 3.8, 3.9, 4.7, 5.6, 7.1, 7.2, 7.3, 7.4

메모:정렬::버전모듈은 핵심 Perl 모듈이 아니며 cpan배포 패키지를 통해 별도로 설치해야 합니다. 예를 들어 Debian에서는apt-get install libsort-versions-perl

답변2

perl이런 종류의 작업에는 훨씬 더 좋지만 순수 bash에서 거의 수행할 수 있습니다(외부 사용에 안전함 ) sort.

#!/bin/bash
arr1=( "1" "1" "3" "2" "4" "7" "7" "7" "1" "2" "3" "3" "3" "3" "7" "5" )
arr2=( "4" "1" "3" "5" "7" "1" "2" "3" "2" "9" "2" "6" "8" "9" "4" "6" )

# make an array with strings from both initial arrays:
arr_length=${#arr1[@]}
for ((i=0; i< $arr_length; i++)); do
   arr_combined[$i]="${arr1[$i]}!${arr2[$i]}"
done

# sort the combined strings
arr_sorted=( $(printf "%s\n" "${arr_combined[@]}" | sort) )

# split the elements of sorted array back into two arrays
for pair in ${arr_sorted[@]} ; do
    arr1n+="${pair%!*} "
    arr2n+="${pair#*!} "
done

# print the results
printf "%s\n" "${arr1n[@]}"
printf "%s\n" "${arr2n[@]}"

답변3

원본 파일의 숫자를 먼저 정렬한 다음 배열을 채우면 작업이 더 쉬워집니다. 원본 파일에 다음과 같은 내용이 포함되어 있다고 가정합니다.

$ cat file1
1 1 3 2 4 7 7 7 1 2 3 3 3 3 7 5
4 1 3 5 7 1 2 3 2 9 2 6 8 9 4 6

따라서 이 명령은 다음과 같습니다.

transpose file1 | sort | transpose

당신이 원하는 것을 생산할 것입니다 :

1 1 1 2 2 3 3 3 3 3 4 5 7 7 7 7
1 2 4 5 9 2 3 6 8 9 7 6 1 2 3 4

조옮김 기능은 매우 간단합니다. 직접 작성하셔도 됩니다. 예는 다음과 같습니다.

transpose () 
{ 
    awk '{ for (i=1; i<=NF; i++) a[i]= (i in a?a[i] OFS :"") $i; } 
    END{ for (i=1; i<=NF; i++) print a[i] }' $1
}

답변4

귀하의 설명에 따르면 다음이 원래 입력 데이터일 수 있습니다.

1
4
1
1
3
3
2
5
4
7
7
1
7
2
7
3
1
2
2
9
3
2
3
6
3
8
3
9
7
4
5
6

원하는 방식으로 이를 처리하려면 다음을 수행하십시오.

paste - - <file | sort -k 1,1n -k 2,2n

먼저 데이터를 탭으로 구분된 두 개의 열로 변환합니다. 첫 번째 열은 시즌이고 두 번째 열은 에피소드입니다.

행은 첫 번째 열에 숫자순으로 정렬되고, 첫 번째 열이 동일한 행은 두 번째 열에 숫자순으로 정렬됩니다.

위에 표시된 입력을 사용하면 다음이 생성됩니다.

1       1
1       2
1       4
2       5
2       9
3       2
3       3
3       6
3       8
3       9
4       7
5       6
7       1
7       2
7       3
7       4

관련 정보