테이블이 있어요
M - A A -
- A G - -
M - - - G
나는 다음을 실행하고 싶다:첫 번째 행의 열에 "-"가 포함되어 있으면 해당 열 인쇄를 건너뜁니다.
예상 출력은 다음과 같습니다.
M A A
- G -
M - -
나는 성공하지 못한 채 이와 같은 것을 시도했습니다.
awk 'NR==1 && $i!="-" {print $i}'
명령을 수정하는 방법을 아는 사람이 있습니까?
답변1
의 변종에드 모튼의 답변는 필드 번호별로 첫 번째 행에 없는 필드를 기억한 다음 새 레코드를 인쇄하기 전에 -
배열에 저장된 인덱스를 기반으로 입력의 각 레코드를 다시 구성합니다 .out
FNR == 1 {
for (i = 1; i <= NF; ++i)
if ($i != "-") out[++nf] = i
}
{
for (i = 1; i <= nf; ++i)
a[i] = $(out[i])
$0 = ""
for (i = 1; i <= nf; ++i)
$i = a[i]
print
}
여기서는 가독성을 위해 약간의 효율성을 희생하고 두 번째 블록의 단일 루프에서 필수 필드를 인쇄하는 대신 별도의 루프에서 레코드를 재구성했습니다.
시험:
$ awk -f script.awk file
M A A
- G -
M - -
탭을 출력 필드 구분 기호로 사용하여 실행합니다.
$ awk -v OFS='\t' -f script.awk file
M A A
- G -
M - -
입력 데이터가 탭으로 구분되어 있는지 여부에 따라 코드 한 줄이 약간 너무 깁니다.
$ cut -f "$(awk -v OFS=',' '{ nf=split($0,a); $0=""; for (i=1; i<=nf; ++i) if (a[i]!="-") $(++NF)=i; print; exit }' file)" file
M A A
- G -
M - -
awk
이는 출력하는 데 사용됩니다.필드 번호-
쉼표로 구분된 목록으로 첫 번째 줄 에 없습니다 . 그런 다음 해당 목록은 목록으로 전달되어 cut -f
실제로 파일의 데이터를 출력합니다. 파일 이름(여기서 축약됨 file
)은 명령줄에 두 번 제공됩니다. 한 번은 에 대해, awk
또 한 번은 에 대해 다시 제공됩니다 cut
.
답변2
$ cat tst.awk
NR == 1 {
for (i=1; i<=NF; i++) {
if ($i != "-") {
f[++numOutFlds] = i
}
}
}
{
for (i=1; i<=numOutFlds; i++) {
printf "%s%s", $(f[i]), (i<numOutFlds ? OFS : ORS)
}
}
$ awk -f tst.awk file
M A A
- G -
M - -
답변3
awk
입력 레코드(행) 및 파일에 대해 암시적으로 루프를 수행하지만 명시적으로 수행해야 하는 필드에 대해서는 루프를 수행하지 않습니다. 귀하의 경우 첫 번째 행(헤더 행)의 필드를 반복하여 포함할 열을 결정한 다음 반복해야 합니다.모든행(제목 및 비제목)에 해당 행에 필요한 열을 포함합니다.
다음 헤더 필드를 찾고 있는지 확실하지 않습니다.동일한(문자열) "-" 또는 다음과 같이 전달할 수도 있습니다.(하위)문자열. 또한 필드 구분 기호로 여러 개의 공백이 아닌 단일 탭이 있다고 가정합니다. 이는 더 지루하며 게시물과 시각적으로 구별되지 않습니다.
awk -F"\t" 'NR==1{for(i=1;i<=NF;i++)s[i]=$i!="-"} {x="";for(i=1;i<=NF;i++)if(s[i])x=x FS $i;print substr(x,2)}'
# for _matches_ "-" instead of _equals_ "-" change $i!="-" to $i!~/-/
# note if a nonheader line has more fields than the header did,
# all extra fields are nonselected (as if their header field was/matched -)
# or (re)use the flags for both what to include _and_ when to terminate the line
awk -F"\t" 'NR==1{t=RS;for(i=NF;i;i--)if(s[i]=($i!="-"?t:""))t=FS} {for(i=1;i<=NF;i++)if(s[i])printf "%s%s",$i,s[i]}'
# some people may consider this too clever
답변4
를 사용하여 이 작업을 수행할 수 있습니다 sed
. 비록 코드가 확장 정규식 모드에서 GNU sed를 사용하고 있지만 이것은 단지 치료법일 뿐입니다 backslashitis
.
방법은 첫 번째 행부터 지도를 생성하는 것입니다. 유지될 필드는 x에 매핑되고 다른 필드는 대시에 매핑됩니다. 이 지도를 화물칸에 보관하세요.
그런 다음 모든 라인에 대해 이 지도를 추가하고 BOL에 마커를 배치합니다.
루프에서 \n-이 표시되고 마커가 다음 필드로 이동하면 현재 줄의 선행 필드가 계속 제거됩니다.
이 마커가 현재 줄과 예약된 공간 사이의 개행 문자와 충돌하면 루프가 종료됩니다(G 명령으로 인해).
$ sed -Ee '
1{
h
y/-/\n/
s/\S+/x/g;s/[[:blank:]]+//g
y/\n/-/
x
}
G;s/^/\n/
:a
s/\n(\S+\s*)(.*\n)x/\1\n\2/
s/\n(\S+\s*)(.*\n)-/\n\2/
/\n\n/!ba
s/\s+$//
' file
결과
M A A
- G -
M - -