줄에있는 탭 ‘\ t’의 길이를 결정하십시오. 탭 수를 찾지 않고

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

예를 들어, 탭 구분 기호가있는 샘플 파일이 있고 필드의 내용이 하나보다 적은 탭 (≤7)에 맞으면 그 뒤에 탭이 있으면 해당 탭은 ‘탭 크기 – 필드 크기’ ‘ 길이.

줄에서 총 탭 길이를 얻는 방법이 있습니까? 탭 수를 찾지 않고 (즉, 10 개의 탭이 10을 반환해서는 안 됨) 해당 탭의 문자 길이입니다.

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

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

각 줄의 탭 길이를 계산할 것으로 예상되므로

11
9
9


답변

TAB문자는 terminal¹에 보낼 때 다음 탭 정지 터미널의 커서 이동을하게 제어 문자입니다. 기본적으로 대부분의 터미널에서 탭 스톱은 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 에서 작동하지 않습니다 (적어도).


¹ 그러나 stty tab3tty 디바이스 라인 규칙은 탭 처리를 인수하고 (터미널로 보내기 전에 커서가있을 수있는 위치에 대한 자체 아이디어에 따라 TAB을 공백으로 변환 함) 8 열마다 탭 중지를 구현합니다. Linux에서 테스트하면 CR, LF 및 BS 문자뿐만 아니라 멀티 바이트 UTF-8 문자 (제공된 iutf8경우도 있음)를 올바르게 처리하는 것 같습니다 . 제어되지 않는 다른 모든 문자 (제로 너비, 두 배 너비 문자 포함)는 너비가 1이라고 가정하고 (분명히) 이스케이프 시퀀스를 처리하지 않으며 올바르게 줄 바꿈하지 않습니다 … 아마도 터미널을위한 것입니다. 탭 처리를 수행 할 수 없습니다.

어떤 경우에는, 청각 장애 제어 규칙은 커서가하고 사용하는 경우 때문에, 위의 그 추론을 사용하여 위치를 알 필요하지 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의 솔루션을 사용하는 것처럼 사용할 수 있습니다 .


답변

$ 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

답변

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

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


답변

또한 expandbash 매개 변수 조작을 사용하여 공백 수를 계산합니다.

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