awk 스크립트의 각 줄에 대해 FIELDWIDTHS를 변경할 수 있나요?

awk 스크립트의 각 줄에 대해 FIELDWIDTHS를 변경할 수 있나요?

제가 받은 파일에는 데이터 세트를 나타내는 행이 포함되어 있습니다. 각 줄에는 여러 데이터 필드를 나타내는 문자가 구분 없이 포함되어 있습니다. 각 데이터 필드를 추출하려면 줄을 끊어야 합니다.

ABCD075BCD156300544E0001000900125349544520494

->

ABCD 075BCD 15630 0544 E00010009 00 12 5349544520494

각 필드의 문자 수를 알고 있으므로 FIELDWIDTHS를 사용하여 awk에서 이 작업을 수행할 수 있습니다. 이 접근 방식은 모든 데이터 세트가 동일할 때 효과적이지만 그렇지 않습니다. 문제를 더욱 복잡하게 만드는 것은 필드 중 하나를 읽어야만 그것이 어떤 유형의 데이터 세트인지 알 수 있다는 것입니다.

그래서 데이터 유형을 읽으려면 FIELDWIDTHS 세트를 적용해야 할 것 같습니다. 그런 다음 다른 FIELDWIDTHS 세트를 적용하고 getline을 사용하여 동일한 행을 다시 읽어 데이터를 추출합니다. 하지만 변경된 필드 너비가 적용되지 않기 때문에 이는 작동하지 않습니다.

BEGIN {
  FIELDWIDTHS = "30 2";                  # set FIELDWIDTHS to read data type
}

{
  print $2;                              # print data type
  FIELDWIDTHS = "5 5 5 5 5 5 5 5 5 5 5"; # change fieldwidths to read data
  getline NF;                            # reread current line to use new fieldwidths
  print $2;                              # print data field
  FIELDWIDTHS = "30 2";                  # change fieldwidths to read next line
}

END {
}

어떤 조언이라도 대단히 감사하겠습니다.

답변1

무엇이든:

awk 'NR%2   { fieldwidths="4 6 5 4 9 2 2 13" } # update fieldwidths on odd line numbers
    !(NR%2) { fieldwidths="4 5 4 2 3 9 7 11" } # update fieldwidths on even line numbers
    # condition { fieldwidths="# # #  ..." }   # whatever other condition you want...

{ fields=split(fieldwidths, fldwd); startPos=1;
  for(i=1; i<=fields; i++) {
      printf "%s", (i==1?"": OFS) substr($0, startPos, fldwd[i])
      startPos+=fldwd[i]
  }
  print ""
}' infile

답변2

나는 다음과 같은 것(FIELDWIDTHS에 GNU awk를 사용하는 것)이 당신이 원하는 것이라고 생각합니다:

BEGIN {
    type2fw[10] = "7 3 6 8 9"
    type2fw[12] = "5 5 5 5 5 5 5 5 5 5 5"
    type2fw[53] = "1 1 1 17 29 31"
    ....
}
{
    FIELDWIDTHS = type2fw[substr($0,31,2)]
    $0 = $0
    do whatever you like with the fields
}

하지만 이는 필드 분할을 두 번 수행하기 때문에(레코드를 읽을 때 한 번, $0=$0을 수행할 때 두 번째) 약간 비효율적입니다. 유형이 변경될 때만 다시 분할하여 효율성을 향상시킬 수 있습니다.

BEGIN {
    type2fw[10] = "7 3 6 8 9"
    type2fw[12] = "5 5 5 5 5 5 5 5 5 5 5"
    type2fw[53] = "1 1 1 17 29 31"
    ....
}
{ type = substr($0,31,2) }
type != prev {
    FIELDWIDTHS = type2fw[type]
    $0 = $0
    prev = type
}
{
    do whatever you like with the fields
}

각 유형에 대해 FIELDWIDTHS를 한 번만 변경하면 되도록 먼저 31번째/32번째 문자 유형 필드(예: )별로 sort -k1.31,1.32 file | awk '...'입력을 정렬 할 수 있습니다.

입력 및 예상 출력의 간결하고 테스트 가능한 여러 줄/유형의 예를 보지 않고는 이보다 더 구체적일 수 없으며 이는 잘못된 접근 방식일 수도 있으며 match($0,/(foo)(bar)(etc)/,a)또는 다른 것을 사용하는 것이 더 나을 것입니다.

답변3

gnu awk를 사용하면 $0 = $0. 예를 들어,

echo '1 abcdefghij
2   abcdefghij' |
awk '
/^1/{ FIELDWIDTHS = "1 1 5 5"; $0 = $0; print $3; next }
/^2/{ FIELDWIDTHS = "1 3 3 3"; $0 = $0; print $3; next }
'

또는 처리된 행을 접두사 문자로 표시하는 등 하나의 필드 형식을 처리하는 awk를 통해 데이터를 전송한 #다음 결과를 두 번째 awk로 파이프하는 Unix와 유사한 솔루션을 고려할 수 있습니다. 예를 들어,

awk -v FIELDWIDTHS="1 1 5 5" '
/^1/{ print "#" $3; next }
    { print }
' |
awk -v FIELDWIDTHS="1 3 3 3" '
/^2/{ print $3; next }
/^#/{ print substr($0,2); next }
    { print }
'

답변4

또 다른 사용 방법GNU sed/e명령에 대한 수정자는 여기에 표시됩니다 .s///

일반적인 아이디어는 현재 입력 레코드에 대해 31-32자 이름을 가진 파일에 공백으로 구분된 필드 너비 목록을 저장하는 것입니다. 이것PK 취소현재 레코드와 연관된 필드 너비를 보유하는 파일 이름을 함수에 제공하십시오. 그런 다음 이러한 너비를 기준으로 현재 레코드를 분할하는 sed 코드를 생성합니다.

#--- edit this function to add the fieldwidths corresponding to
#--- the 2 characters in the 31st/32nd
#--- positions of the input record
_init_() {
  [ -s "$1" ] && return
  case $1 in
    */12) echo '4 6 5 4 9 2 2 13' ;;
    */96) echo '5 5 5 6 7 2 2 13' ;;
  esac > "$1"
}

_unpk_() {
  _init_ "$1"
< "$1" tr -s ' \t' '[\n*]' |
sed -Ee '
  1i\
$!d;H;z;x
  s|.*|s/\\n.{&}/\&\\n/|
  s|$|;s/\\n/ /|
  $a\
s/^.|.$//g
'
}

export -f _init_ _unpk_
tmpdir=$(mktemp -d)

sed -Ee "w $tmpdir/h
  s:.{30}(..).*:_unpk_ '$tmpdir/\\1' | sed -Ef - '$tmpdir/h':e
" file

관련 정보