한 줄에서 탭 문자 '\t'의 길이를 결정합니다.

한 줄에서 탭 문자 '\t'의 길이를 결정합니다.

텍스트 처리 필드에서 탭 문자의 길이가 8자(기본 길이) 이하인지 알 수 있는 방법이 있습니까?

예를 들어 탭 구분 기호가 있는 샘플 파일이 있고 필드 내용이 탭 1개 미만(7 이하)이고 그 뒤에 탭이 있는 경우 탭은 "탭"이 됩니다. 테이블 크기 – 길이 필드 크기 ' '.

한 줄에 있는 탭 문자의 전체 길이를 얻는 방법이 있습니까? 나는 탭 수(즉, 10개의 탭이 10을 반환해서는 안 됨)가 아니라 해당 탭의 문자 길이를 찾고 있습니다.

다음 입력 데이터의 경우(탭으로 구분된 필드와 단 하나의 탭):

field0  field00 field000        last-field
fld1    fld11   fld001  last-fld
fd2     fld3    last-fld

각 줄의 탭 문자 길이를 계산하고 싶습니다.

11
9
9

답변1

TAB문자는 터미널로 전송될 때 터미널의 커서가 다음 탭 정지로 이동하도록 하는 제어 문자입니다. 기본적으로 대부분의 터미널에서 탭 정지는 8열 간격으로 떨어져 있지만 이는 구성 가능합니다.

불규칙한 간격으로 탭 정지를 설정할 수도 있습니다.

$ tabs 3 9 11; printf '\tx\ty\tz\n'
  x     y z

터미널만이 오른쪽 TAB의 몇 열이 커서를 움직일지 알고 있습니다.

탭을 전송하기 전후에 터미널에서 커서 위치를 쿼리하여 이 정보를 얻을 수 있습니다.

주어진 행에 대해 수동으로 계산을 수행하고 해당 행이 화면의 첫 번째 열에 인쇄된다고 가정하려면 다음이 필요합니다.

  • 탭 정지 위치 파악²
  • 알다각 문자의 표시 너비
  • 화면 너비를 알아보세요
  • 추가 제어 문자를 처리할지 결정합니다(예: \r(커서를 첫 번째 열로 이동) 또는 \b커서를 뒤로 이동...)

탭 정지가 8개 열마다 하나씩 있고 행이 화면에 맞으며 터미널이 올바르게 표시할 수 없는 다른 제어 문자나 문자(또는 비문자)가 없다고 가정하면 단순화할 수 있습니다.

GNU의 경우 wc행이 다음 위치에 저장되어 있는 경우 $line:

width=$(printf %s "$line" | wc -L)
width_without_tabs=$(printf %s "$line" | tr -d '\t' | wc -L)
width_of_tabs=$((width - width_without_tabs))

wc -L입력 시 가장 넓은 줄의 너비를 제공합니다. wcwidth(3)결정된 문자 너비를 사용하고 8열마다 탭이 정지된다고 가정하여 이를 수행합니다.

GNU가 아닌 시스템의 경우 동일한 가정을 사용하면 다음을 참조하세요.@Kusalananda의 접근 방식. 탭 정지를 지정할 수 있기 때문에 훨씬 더 좋지만, expand입력에 멀티바이트 문자나 너비가 0(예: 결합 문자) 또는 이중 너비 문자가 포함된 경우 불행하게도 현재(적어도) GNU에서는 작동하지 않습니다.


1 그러나 이렇게 하면 stty tab3tty 장치 라인 규칙이 탭 처리(터미널로 보내기 전에 커서가 있을 수 있는 자체 아이디어를 기반으로 탭을 공백으로 변환)를 대신하고 8개 열마다 탭 지정을 구현한다는 점에 유의하세요. 기호가 멈춥니다. Linux에서 테스트한 결과 CR, LF 및 BS 문자는 물론 멀티바이트 UTF-8 문자( iutf8켜져 있는 경우)도 올바르게 처리하는 것으로 보이지만 그게 전부입니다. 다른 모든 비제어 문자(너비 0, 이중 너비 문자 포함)에 대해 너비를 1로 가정하고 (분명히) 이스케이프 시퀀스를 처리하지 않으며 올바르게 래핑되지 않습니다. 다음 터미널: 탭 처리를 수행할 수 없습니다.

어떤 경우든 tty 줄 규칙은 커서가 어디에 있는지 알아야 하며 위의 경험적 방법을 사용해야 합니다. 왜냐하면 icanon줄 편집기를 사용할 때(예를 들어 자체 줄 편집기를 구현하지 않는 응용 프로그램에 텍스트를 입력할 때 cat), 을 누르면 TabBackspace회선 규율은 보낼 BS 문자 수를 알아야 합니다.삭제표시에 사용되는 탭 문자입니다. 탭 정지 위치(예: )를 변경하면 tabs 12탭이 제대로 제거되지 않는 것을 확인할 수 있습니다. 를 누르기 전에 2바이트 문자를 입력해도 마찬가지입니다 TabBackspace.


² 이렇게 하려면 탭 문자를 보내고 각 문자 뒤의 커서 위치를 쿼리할 수 있습니다. 그것은 다음과 같습니다:

tabs=$(
  saved_settings=$(stty -g)
  stty -icanon min 1 time 0 -echo
  gawk -vRS=R -F';' -vORS= < /dev/tty '
    function out(s) {print s > "/dev/tty"; fflush("/dev/tty")}
    BEGIN{out("\r\t\33[6n")}
    $NF <= prev {out("\r"); exit}
    {print sep ($NF - 1); sep=","; prev = $NF; out("\t\33[6n")}'
  stty "$saved_settings"
)

expand -t "$tabs"그런 다음 @Kusalananda의 솔루션처럼 사용할 수 있습니다.

답변2

$ expand file | awk '{ print gsub(/ /, " ") }'
11
9
9

POSIX expand유틸리티는 탭을 공백으로 확장합니다. 이 awk스크립트는 각 줄의 모든 공백을 바꾸는 데 필요한 대체 횟수를 계산하고 출력합니다.

입력 파일에 이미 존재하는 공백을 계산하지 않으려면 다음을 수행하십시오.

$ tr ' ' '@' <file | expand | awk '{ print gsub(/ /, " ") }'

@입력 데이터에 문자가 존재하지 않는다고 보장되는 곳은 어디 입니까?

탭당 일반 8칸 대신 10칸을 원하는 경우:

$ tr ' ' '@' <file | expand -t 10 | awk '{ print gsub(/ /, " ") }'
9 
15
13

답변3

그리고 perl:

perl -F/\\t/ -lpe '$c = 0; $F[-1] eq "" or pop @F; $_ = (map { $c += 8 - (length) % 8 } @F)[-1]' file

또는:

perl -MList::Util=reduce -lpe \
    '@F = split /\t/, $_, -1; pop @F if $F[-1] ne ""; $_ = reduce { $a + $b } map { 8 - (length) % 8 } @F' file

탭 문자의 길이를 다르게 하려면 위의 8을 다른 값으로 변경할 수 있습니다.

답변4

또한 사용되지만 expandbash 인수 조작을 통해 공백 수를 계산합니다.

$ line=$'field0\tfield00\tfield000\tlast-field'
$ tabs2spaces=$(expand <<<"$line")
$ only_spaces=${tabs2spaces//[^ ]/}    # remove all non-space characters
$ echo "${#only_spaces}"
11

관련 정보