테이블 값으로 파일 연결

테이블 값으로 파일 연결

ABC1234001내 데이터의 특정 그룹(여러 열의 테이블)에 대한 정보를 전달하는 것과 같이 이름에 특정 패턴을 포함하는 여러 파일이 있습니다 . 또한 다음과 같은 테이블이 있습니다 info.tsv.

group1    ABC1234001    ABC1234010
group2    ABC1234011    ABC1234018
group3    ABC1234019    ABC1234028
...       ...           ...

여기에는 다음이 포함됩니다.

  • 그룹을 지정하는 "그룹" 열,
  • 해당 그룹에 대한 정보가 포함된 첫 번째 파일의 패턴(알파벳순)을 지정하는 "첫 번째 파일" 열,
  • 해당 그룹에 대한 정보가 포함된 마지막 파일의 패턴(알파벳순)을 지정하는 "마지막 파일" 열입니다.

그래서 내가 해야 할 일은 각 그룹의 파일을 하나의 파일로 결합하는 것입니다.

cat ABC123401{1..8}* >> group2.tsv

예를 들어 이 info.tsv파일을 읽는 동안 group2에 대한 것입니다. 이 예제에서는 모든 파일( ABC1234011.tsv, ABC1234012.tsv, ABC1234013.tsv, ABC1234014.tsv, ABC1234015.tsv, ABC1234016.tsv, )이 하나의 파일 로 연결됩니다 ABC1234017.tsv.ABC1234018.tsvgroup2.tsv

내가 할 일은 다음과 같다:

while read $file; do
  #assign columns to variables like $1="group", $2="firstfile", $3="lastfile"
  cat *{$2..$3}* > $1.tsv;
done < info.tsv

하지만 이 접근 방식에 대해 변수를 반복적으로 변경하는 방법을 잘 모르겠습니다. 어쩌면 사용하는 것이 awk더 유용할 수도 있지만 잘 모르겠습니다. 스크립트는 테이블의 "첫 번째 파일"부터 "마지막 파일"까지 해당 파일의 내용을 포함하는 , group1.tsv라는 파일 묶음을 생성해야 합니다 . group2.tsv그렇게 할 수 있도록 스크립트를 작성하도록 도와주세요.

답변1

아래 스크립트는 연결하려는 모든 파일이 패턴과 일치한다고 가정합니다 *.tsv. 모두 일치한다는 것을 알고 있다면 ABC*.tsv스크립트 시작 부분에서 대신 해당 패턴을 사용할 수 있습니다 *.tsv.

*.tsv또한 스크립트는 특정 그룹에 들어가는 모든 파일 이름이 확장되는 목록의 연속 하위 목록으로 생성된다고 가정합니다 .

#!/bin/sh

set -- *.tsv

while read -r group first last; do
        collect=false

        for name do
                if ! "$collect"; then
                        [ "$name" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$name" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done <info.tsv

스크립트는 위치 매개변수 목록을 일치하는 이름 목록으로 설정합니다 *.tsv. 그런 다음 각 줄의 세 필드를 info.tsv변수 및 로 group읽어 first옵니다 last.

이런 방식 으로 읽은 각 행에 대해 info.tsv위치 매개변수 목록에서 그룹의 첫 번째 이름과 일치하는 이름을 검색합니다. 이 이름을 찾으면 collect위치 매개변수 목록에 명명된 파일의 데이터 수집을 목록의 현재 위치부터 시작하도록 스크립트 논리에 지시하는 플래그를 설정합니다. 이는 그룹의성에 해당하는 이름을 발견하면 종료됩니다.

true및 here 은 false단순한 문자열이 아닌 명령으로 사용됩니다. 변수에 저장된 값이 $collect실행되고 있으므로 if ! "$collect"스크립트가 두 개의 쉘 내장 명령 중 하나 true또는 를 실행한다는 의미입니다 false. 쉘에는 일부 다른 언어(예: Python)와는 달리 true 또는 false에 대한 특별한 키워드가 없습니다.

테스트:

$ ls
script
$ touch ABC{1234001..1234030}.tsv
$ for name in ABC*.tsv; do printf 'Name: %s\n' "$name" >"$name"; done
$ cat ABC1234015.tsv
Name: ABC1234015.tsv
$ cat >info.tsv <<END_DATA
group1 ABC1234001 ABC1234010
group2 ABC1234025 ABC1234030
END_DATA
$ ./script
$ cat group1.tsv
Name: ABC1234001.tsv
Name: ABC1234002.tsv
Name: ABC1234003.tsv
Name: ABC1234004.tsv
Name: ABC1234005.tsv
Name: ABC1234006.tsv
Name: ABC1234007.tsv
Name: ABC1234008.tsv
Name: ABC1234009.tsv
Name: ABC1234010.tsv
$ cat group2.tsv
Name: ABC1234025.tsv
Name: ABC1234026.tsv
Name: ABC1234027.tsv
Name: ABC1234028.tsv
Name: ABC1234029.tsv
Name: ABC1234030.tsv

이 답변에 대한 설명에서 언급했듯이 개인적인 용도로 이 스크립트를 개발하는 방법은 스크립트를 다음과 같이 두는 것입니다.

#!/bin/sh

while read -r group first last; do
        collect=false

        for name do
                filename=$( basename "$name" )

                if ! "$collect"; then
                        [ "$filename" = "$first.tsv" ] || continue
                        collect=true
                fi

                if "$collect"; then
                        cat -- "$name"
                        [ "$filename" = "$last.tsv" ] && break
                fi
        done >"$group.tsv"

done

상단의 명령 삭제 set(명령줄 인수로 대체됨)와 리디렉션 삭제 info.tsv(명령줄의 리디렉션으로 대체됨)에 유의하세요. 또한 filename명령줄에 제공된 경로 이름의 파일 이름 구성 요소를 보유할 변수 도 도입했습니다 .

그런 다음 스크립트를 다음과 같이 실행합니다.

$ ./script ABC*.tsv <info.tsv

ABC내가 이 방법으로 달성한 것은 입력 그룹 목록이 저장되는 위치나 이름이 무엇인지에 관계없이 파일 이름이 무엇인지(파일 이름 접미사가 있는 한 .tsv) 또는 저장 위치에 상관하지 않는 스크립트입니다. .

답변2

귀하의 접근 방식은 좋은 생각이지만 불행히도 중괄호 확장 내에서 변수가 확장되지 않기 때문에 작동하지 않습니다.

$ echo {1..5}
1 2 3 4 5
$ a=1
$ b=5
$ echo {$a..$b}
{1..5}

하지만 다음 을 사용하여 이 문제를 해결할 수 있습니다 eval.

sed 's/ABC//g' info.tsv | 
    while read -r group start end; do 
        files=( $(eval echo ABC{$start..$end}.tsv) )
        cat "${files[@]}" > "$group.tsv"; 
    done 

그러면 먼저 파일 ABC에서 모든 인스턴스가 제거되므로 info.tsv숫자만 얻을 수 있습니다. 이것은 우리에게 보여준 정확한 데이터 구조를 가정합니다. ABC그룹 이름에도 존재할 수 있으면 중단됩니다.

을 제거한 후 ABC결과는 , 및 while3개의 변수를 읽는 루프 로 파이프됩니다 . 그런 다음 중괄호 확장을 호출하기 전에 변수를 확장하는 전달되어 파일 이름 목록을 얻을 수 있습니다.$group$start$endeval

$ eval echo ABC{1..5}
ABC1 ABC2 ABC3 ABC4 ABC5

결과는 배열 eval에 저장되며 $files다음 항목에 입력으로 전달됩니다 cat.

cat "${files[@]}" > "$group.tsv";

답변3

내가 당신을 올바르게 이해했다면 여기에 옵션이 있습니다

$ while IFS= read -r i; do
    f=$(echo "$i" | cut -d' ' -f1)
    cat $(echo "$i" | cut -d' ' -f2- | sed -E 's/([0-9])\s+/\1.tsv /;s/([0-9])$/\1.tsv /') > "$f.txt"
  done < info.tsv

  • f=$(echo "$i" | cut -d' ' -f1)그룹의 이름을 검색합니다.
  • cat $(cut -d' ' -f2- | sed -E 's/([0-9])\s+|([0-9])$/\1.tsv /g')줄의 파일 목록을 연결합니다.

관련 정보