この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では動作しません。
doただし、これを行うstty tab3
と、ttyデバイスの回線制御がタブ処理を引き継ぎ(カーソルが端末に送信される前の位置に関する独自の考えに基づいてTABをスペースに変換し)、タブを8列ごとに実装します。Linuxでのテストでは、CR、LF、BSの文字とマルチバイトUTF-8の文字(iutf8
オンになっている場合も同様)は適切に処理されているようですが、それで問題ありません。他のすべての非制御文字(ゼロ幅、倍幅文字を含む)の幅が1であることを前提としています。(明らかに)エスケープシーケンスを処理せず、適切に折り返されません...これはおそらく、タブ処理はできません。
いずれにせよ、tty icanon
エディターは、カーソルがどこにあるかを知る必要があり、上記のヒューリスティックを使用cat
します。を押すTabBackspaceと、ライン規律は、タブ文字を消去して表示するために送信するBS文字の数を知る必要があります。タブストップの場所を変更すると(などtabs 12
)、タブが適切に消去されないことがわかります。を押す前に全角文字を入力した場合も同様です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のソリューションを使用するのと同じように使用できます。
x
に呼び出す前に、スペースを他の半角文字(など)で置き換えたいexpand
場合も、最初に入力にあったスペースもカウントします。