나는 많은 파일을 가지고 있는데, 각 파일의 이름에는 특정 패턴이 포함되어 있습니다. 예를 들어 ABC1234001
일부 데이터 그룹(다중 열)에 대한 정보를 담고 있습니다. 또한 다음과 같은 테이블이 있습니다 info.tsv
.
group1 ABC1234001 ABC1234010
group2 ABC1234011 ABC1234018
group3 ABC1234019 ABC1234028
... ... ...
여기에는 다음이 포함됩니다.
- "그룹" 열에서 그룹을 지정하고,
- "첫 번째 파일" 열은 해당 그룹 정보를 포함하는 첫 번째 파일의 패턴(알파벳순)을 지정하고,
- "마지막 파일" 열은 해당 그룹 정보가 포함된 마지막 파일의 패턴(알파벳순)을 지정합니다.
그래서 내가 해야 할 일은 각 그룹의 파일을 하나의 파일로 병합하는 것입니다.
cat ABC123401{1..8}* >> group2.tsv
이 파일을 읽을 때 group2를 예로 들어 보겠습니다 info.tsv
. 이 예제에서는 모든 파일( ABC1234011.tsv
, ABC1234012.tsv
, ABC1234013.tsv
, ABC1234014.tsv
, ABC1234015.tsv
, ABC1234016.tsv
, )이 하나의 파일 로 연결됩니다 ABC1234017.tsv
.ABC1234018.tsv
group2.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
. 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
결과는 , 및 while
3개의 변수를 읽는 루프 로 파이프됩니다 . 그런 다음 중괄호 확장을 호출하기 전에 확장될 변수 에 전달하여 파일 이름 목록을 얻을 수 있습니다.$group
$start
$end
eval
$ 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')
이 줄의 파일 목록을 연결합니다.