재현 가능한 예는 다음과 같습니다.
file01.txt
line to skip line to skip line to skip line to keep file 01 heading 1 in the form: 2017243 01 2017243 01 data 1 file 01 heading 2 in the form: 2017243 02 2017243 02 data 2 file 01 heading 3 in the form: 2017243 03 2017243 03 data 3 file 01
file02.txt
line to skip line to skip line to skip line to keep file 02 heading 1 in the form: 2017243 01 2017243 01 data 1 file 02 heading 2 in the form: 2017243 02 2017243 02 data 2 file 02 heading 3 in the form: 2017243 03 2017243 03 data 3 file 02
file03.txt
line to skip line to skip line to skip line to keep file 03 heading 1 in the form: 2017243 01 2017243 01 data 1 file 03 heading 2 in the form: 2017243 02 2017243 02 data 2 file 03 heading 3 in the form: 2017243 03 2017243 03 data 3 file 03
원하는 출력
line to keep file 01 line to keep file 02 line to keep file 03 heading 1 in the form: 2017243 01 2017243 01 data 1 file 01 data 1 file 02 data 1 file 03 heading 2 in the form: 2017243 02 2017243 02 data 2 file 01 data 2 file 02 data 2 file 03 heading 3 in the form: 2017243 03 2017243 03 data 3 file 01 data 3 file 02 data 3 file 03
지금까지 나는 다음을 통해 각 입력 파일에서 네 번째 줄을 추출하는 매우 간단한 작업을 수행했습니다.
awk 'FNR == 4' *.txt >> out_row4
하지만 나머지 파일 처리 작업이 막혀서 제대로 작동하는 최종 솔루션을 생각해낼 수 없었습니다...
처리할 파일 및 줄 수가 매우 많기 때문에 솔루션을 매우 일반적인 방식으로 유지해야 합니다(파일당 5900줄 이상).
참조용 일반 패턴:
- 항상 각 파일의 처음 3줄을 건너뛰세요.
- 각 파일의 4번째 줄을 유지하세요
- 제목 1, 2, 3(...등등)은 다른 파일에서 정확히 동일합니다(따라서 원하는 출력 파일에서 한 번만 보고하면 됩니다).
- 모든 파일에는 동일한 수의 줄이 포함되어 있습니다.
- 파일에 알려진 구조적 형식이 없으며 일반 텍스트 파일입니다.
추출하고 재배열하는 일반적인 패턴은 다음과 같습니다.
heading n in the form: 2017243 n 2017243 n
data n file ...
어떤 팁이 있나요?
답변1
애플리케이션DSU 관용어, 필수 POSIX 도구 awk, 정렬 및 잘라내기의 모든 버전을 사용합니다.
$ cat tst.sh
#!/usr/bin/env bash
awk -v OFS='\t' '
FNR == 1 { fileNr++ }
FNR >= 4 { print FNR-3, fileNr, $0 }
' "${@:--}" |
sort -n -k1,1 -k2,2 |
awk '($1 % 2) || ($2 == 1)' |
cut -f 3-
$ ./tst.sh file01.txt file02.txt file03.txt
line to keep file 01
line to keep file 02
line to keep file 03
heading 1 in the form: 2017243 01 2017243 01
data 1 file 01
data 1 file 02
data 1 file 03
heading 2 in the form: 2017243 02 2017243 02
data 2 file 01
data 2 file 02
data 2 file 03
heading 3 in the form: 2017243 03 2017243 03
data 3 file 01
data 3 file 02
data 3 file 03
모든 입력을 한 번에 처리해야 하는 위의 유일한 도구는 sort
요구 페이지 매김 등을 사용하여 대량의 입력을 처리하도록 설계되었으므로 입력 파일 수는 중요하지 않습니다(ARG_MAX를 초과하지 않는 한). 물론) 또는 얼마나 큰지.
또는 awk를 사용하고 입력 파일이 충분하지 않아 "열린 파일이 너무 많습니다" 오류가 발생한다고 가정합니다.
$ cat tst.awk
BEGIN {
while ( ! eof ) {
for ( fileNr=1; fileNr<ARGC; fileNr++ ) {
if ( (getline vals[fileNr] < ARGV[fileNr]) <= 0 ) {
eof = 1
}
}
if ( !eof && (++lineNr >= 4) ) {
if ( lineNr % 2 ) {
print vals[1]
}
else {
for ( fileNr=1; fileNr<ARGC; fileNr++ ) {
print vals[fileNr]
}
}
}
}
exit
}
$ awk -f tst.awk file01.txt file02.txt file03.txt
line to keep file 01
line to keep file 02
line to keep file 03
heading 1 in the form: 2017243 01 2017243 01
data 1 file 01
data 1 file 02
data 1 file 03
heading 2 in the form: 2017243 02 2017243 02
data 2 file 01
data 2 file 02
data 2 file 03
heading 3 in the form: 2017243 03 2017243 03
data 3 file 01
data 3 file 02
data 3 file 03
나는 getline
대부분의 입력 파일을 한 번에 메모리로 읽는 것을 피하기 위해 위에서 주의를 기울였습니다.http://awk.freeshell.org/AllAboutGetline사용 시기/방법에 대한 자세한 내용을 확인하세요.
답변2
위에서 제공한 패턴을 세 개의 파일에 저장했습니다. 이렇게 하면 awk 필터링을 완료하는 데 필요한 출력을 얻을 수 있습니다.
for i in {4..15}; do awk "FNR == $i" *.txt | sort -u; done
답변3
awk 이외의 것을 사용해도 괜찮다면:
for f in $(ls *.txt) ; do awk 'FNR >=4' $f | egrep "." -n ; done | sort -n | uniq | cut -d: -f2-
성공할 것이다
설명하다:
- for 루프는 각 파일에서 처음 3줄을 제거하고(awk 사용) 개수를 계산합니다(egrep -n 및 grep 기준으로 모든 문자 사용).
- 그런 다음 출력은 줄 번호별로 정렬됩니다.
- 그러면 중복된 헤더 행이 제거됩니다.
- 마지막으로 줄 번호가 제거됩니다
고쳐 쓰다:
awk는 이미 전체 파일을 반복했으며 출력에 줄 번호를 추가할 수도 있기 때문에 egrep 사용을 제거했습니다(파일을 두 번 읽는 것을 방지).
for f in $(ls *.txt) ; do awk 'FNR >=4 {printf("%s#%s\n", FNR-3, $0)}' $f ; done | sort -n | uniq | cut -d# -f2-