다른 디렉토리의 여러 파일을 연결하고 싶습니다.
디렉터리 1: Chr1
이 예에서는 4개의 파일을 포함합니다.
ABC.1
DEF.1
GHI.1
JKL.1
카탈로그 2:Chr2
ABC.2
DEF.2
GHI.2
JKL.2
22개의 디렉토리가 있습니다. 각 파일에는 20개의 열과 헤더가 있습니다. 헤더는 모든 파일에 동일합니다.
모든 것을 하나의 파일(모든 디렉터리의 모든 파일을 연결하는 전역 출력 파일)로 연결하고 싶습니다.
나는 이것을 시도했지만 작동하지 않습니다.
cat */Chr{1..22}/*.{1..22} > */final_file
파일이 없기 때문에 "해당 파일이나 디렉터리가 없습니다"라고 표시됩니다. 예를 들어 *.1~21은 chr22 디렉터리에 있는 파일입니다.
어떤 생각이 있나요? 미리 감사드립니다.
답변1
그냥 zsh
쉘을 사용하세요:
cat -- */Chr<1-22>/*.<1-22>(n) > final_file
에서는 10진수 범위와 일치하는 glob 연산자이며 glob 한정자는 glob 확장이 숫자로 정렬되도록 하는 옵션을 전환 zsh
합니다 .<x-y>
n
numericglobsort
다른 셸에서는 다음을 수행할 수 있습니다.
zsh -c 'cat -- */Chr<1-22>/*.<1-22>(n) > final_file'
첫 번째 파일을 제외한 모든 헤더를 건너뛰고 GNU 또는 비지박스 구현 tail
(Linux를 커널로 사용하는 시스템에서 가장 일반적임)을 가정하려면 다음을 수행할 수 있습니다.
(){
cat < $1; shift; (($#)) && tail -qn +2 -- "$@"
} */Chr<1-22>/*.<1-22>(n) > final_file
답변2
접근 방식의 문제점은 반복되는 와일드카드가 "동기적으로"(="확장") 해석되지 않고 명령줄에서 발생할 때마다 다시 해석되고 독립적이라는 것입니다. 따라서 작동하려면 중첩된 셸 루프를 사용해야 합니다.
다음 쉘 스크립트를 시도해 볼 수 있습니다. 기능을 사용한다는 점에 유의하십시오 bash
(귀하의 질문은 사용 중인 쉘을 다루지 않습니다)
#!/bin/bash
hdr=0 # initialize variable to keep track of whether the header is already printed
# loop over directories
for d in Chr*
do
# extract trailing number from dir name by removing 'Chr' part (bash feature!)
n="${d#Chr}"
# loop over all files
for f in "$d/"*".$n"
do
if (( hdr == 0 )) # if header wasn't printed yet, output entire file
then
cat "$f" > final_file
hdr=1
else # otherwise, output file content starting with line 2
tail -n +2 "$f" >> final_file
fi
done
done
스크립트 이름을 concatenate.sh
실행 파일로 지정하고 모든 하위 디렉터리가 있는 디렉터리에서 실행할 수 있습니다 Chr{1..22}
. final_file
이 디렉토리에도 생성 됩니다 .
너무 멀리 테스트할 수는 없지만 아무 것도 깨져서는 안 됩니다...
답변3
모든 하위 디렉터리의 모든 파일을 캡처하려면 Chr.*
다음을 사용할 수 있습니다.
cat Chr*/* >final_file
해당 디렉토리 이름의 접미사와 일치하도록 각 하위 디렉토리의 파일 세트를 제한해야 하는 경우(따라서 Chr1
일치하는 파일만 고려 *.1
) 루프가 필요합니다.
shopt nullglob # This is bash-specific
for i in {1..22}
do
cat Chr$i/*.$i
done >final_file
이 옵션은 shopt nullglob
일치할 수 없는 와일드카드 문자를 리터럴 별표로 남겨 두는 대신 와일드카드 문자를 제거하도록 쉘에 지시합니다.
대안으로, 연결된 파일에서 첫 번째 헤더 행을 제외한 모든 것을 생략하려는 것처럼 보이므로 이 확장 루프가 이를 처리할 수 있습니다.
first=yes
for i in {1..22}
do
for f in Chr$i/*.$i
do
[[ -n "$first" ]] && head -n1 "$f" && first=
cat "$f"
done
done >final_file
또는 헤더 행이 첫 번째 파일의 첫 번째 행으로 존재하고 이후에 발견되는 모든 곳에서 제거할 수 있는 경우 다음과 같은 구조를 사용하여 제거할 수 있습니다.
for i in {1..22}
do
cat Chr$i/*.$i
done |
awk '$0 != header { print } header == "" { header = $0 }' >final_file