我正在尝试打印两个由制表符分隔的字符串。我努力了:
echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar
两者都打印:
foo bar
两者之间的空白实际上是 5 个空格(根据在 Putty 中用鼠标选择输出)。
我也尝试过使用 CTRL+V 并在键入命令时按 TAB 键,结果相同。
强制将选项卡打印为选项卡的正确方法是什么,以便我可以选择输出并将其复制到带有选项卡的其他位置?
第二个问题:为什么 bash 将制表符扩展为空格?
更新:显然,这是 Putty 的问题: https://superuser.com/questions/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-chang-them-to-spaces
答案1
两者之间的空白实际上是 5 个空格。
不,这不对。不在echo
or的输出中printf
。
$ echo -e 'foo\tbar' | od -c
0000000 f o o \t b a r \n
0000010
强制将选项卡打印为选项卡的正确方法是什么,以便我可以选择输出并将其复制到带有选项卡的其他位置?
这是一个不同的问题。这与 shell 无关,而是与终端模拟器有关,终端模拟器将输出中的制表符转换为空格。许多人(但不是全部)都这样做。
将带有制表符的输出重定向到文件并从那里复制它,或者unexpand
在输出上使用将空格转换为制表符可能会更容易。 (尽管它也不知道空白是制表符开始的,并且如果可能的话,会将其全部转换为制表符。)这当然取决于您需要对输出执行什么操作。
答案2
就像 ilkkachu 所说,这不是 bash 的问题,而是终端模拟器的问题,终端模拟器会将制表符转换为输出中的空格。
检查不同的终端,putty、xterm 和 konsole 将制表符转换为空格,而 urxvt 和 gnome-terminal 则不会。所以,另一个解决办法就是切换终端。
答案3
中printf '%s\t%s\n' foo bar
,printf
输出foo<TAB>bar<LF>
。
f
、o
、b
、a
和r
是单角图形字符。
收到这些字符后,终端将显示相应的字形并将光标向右移动一列,除非它已经到达屏幕的右边缘(原始电传打字机中的纸张),在这种情况下,它可能会输入一行并返回到屏幕的左边缘(换行)或只是丢弃该字符,具体取决于终端及其配置方式。
<Tab>
和<LF>
两个控制人物。<LF>
(又名换行符)是 Unix 文本中的行分隔符,但对于终端,它只是换行(将光标向下移动一个位置)。所以内核中的终端驱动程序实际上会将其翻译为<CR>
(返回到屏幕的左边缘),<LF>
(光标向下)(stty onlcr
通常默认情况下处于打开状态)。
<Tab>
告诉终端将光标移动到下一个制表位(在大多数终端上,默认情况下相距 8 个位置,但也可以配置为在任何位置设置),而不用空格填充间隙。
因此,如果将这些字符发送到每 8 列制表符停止的终端,同时光标位于空行的开头,则将导致:
foo bar
打印在屏幕上的那一行。如果在光标位于包含 的行中的第三个位置时发送它们xxxxyyyyzzzz
,将导致:
xxfooyyybarz
在不支持制表的终端上,可以将终端驱动程序配置为将这些制表符转换为空格序列。 ( stty tab3
)。
原始电传打字机中的 SPC 字符会将光标向右移动,而退格键 ( \b
) 会将光标向左移动。现在在现代终端中,SPC 向右移动并擦除(如您所期望的那样写入空格字符)。所以 的垂饰\b
必须是比 ASCII 更新的东西。在大多数现代终端上,它实际上是一个字符序列:<Esc>
, [
, C
。
有更多的转义序列可以将n
字符向左、向右、向上、向下或屏幕上的任何位置移动。还有其他转义序列可以擦除(填充空白)部分线条或屏幕区域等。
vi
这些序列通常由诸如、lynx
、之类的视觉应用程序使用mutt
,dialog
其中文本被写入屏幕上的任意位置。
现在,所有 X11 终端模拟器和其他一些非 X11 终端模拟器(例如 GNU)都screen
允许您选择屏幕区域进行复制粘贴。当您选择在编辑器中看到的部分内容时vi
,您不想复制用于生成该输出的所有转义序列。您想要选择在那里看到的文本。
例如,如果您运行:
printf 'abC\rAC\bB\t\e[C\b\bD\n'
它模拟一个编辑器会话,您输入abC
,返回到开头,替换ab
为AC
, C
,B
移动到下一个制表位,然后再向右一列,然后向左两列,然后输入D
。
你看:
ABC D
即 、ABC
4 列间隙 和D
。
如果您用鼠标选择xterm
或putty
,它们将在选择中存储ABC
4 个空格字符 和D
, not abC<CR>AC<BS>B<Tab><Esc>[C<BS><BS>D
。
最终选择的内容是printf
终端驱动程序和终端仿真器已发送但经过后处理的内容。
对于其他类型的转换,请参阅将<U+0065><U+0301>
(e
后跟组合重音符号) 更改为<U+00E9>
(é
预合成形式) xterm
。
或者echo abc
最终ABC
由终端驱动程序转换为,然后在stty olcuc
.
现在,<Tab>
, like<LF>
是实际上有时在文本文件中找到的少数控制字符之一(也在<CR>
MSDOS 文本文件中,有时<FF>
用于分页符)。
因此,一些终端模拟器确实选择在可能的情况下在复制粘贴缓冲区中复制它们以保留它们(但通常情况并非<CR>
如此<LF>
)。
例如,在基于 VTE 的终端(如 )中,您可能会看到,当您在空行上gnome-terminal
选择 的输出时,实际上存储在 X11 选择中,而不是、 7 个空格和。printf 'a\tb\n'
gnome-terminal
a\tb
a
b
但对于 的输出printf 'a\t\bb\n'
,它存储a
, 6 个空格 和b
,对于printf 'a\r\tb\n'
, a
,它存储 7 个空格 和b
。
在其他情况下,终端将尝试复制实际输入,例如当您在运行后选择两行时,printf 'a \nb\n'
将保留不可见的尾随空格。或者,当选择不包含 LF 字符的两行时,如果这两行是在右边距处换行的结果。
现在,如果您想将 的输出存储printf
到 CLIPBOARD X11
select 中,最好是直接执行此操作,如下所示:
printf 'foo\tbar\n' | xclip -sel c
请注意,当您将其粘贴到xterm
或大多数其他终端中时,xterm
实际上会将其替换\n
为,因为这是您按下时发送的\r
字符(终端驱动程序可能会将其转换回)。xterm
Enter\n