如何在bash中打印由TAB分隔的字符串?


9

我正在尝试打印两个由TAB分隔的字符串。我努力了:

echo -e 'foo\tbar'
printf '%s\t%s\n' foo bar

他们两个都打印:

foo     bar

两者之间的空白实际上是5个空格(根据在Putty中用鼠标选择输出)。

我也尝试使用CTRL + V并在键入命令时按TAB键,结果相同。

强制将标签打印为标签的正确方法是什么,以便我可以选择输出并将其复制到带有标签的其他位置?

第二个问题是:bash为什么将制表符扩展为空格?

更新:显然,这是腻子的问题:https : //superuser.com/questions/656838/how-to-make-putty-display-tabs-within-a-file-instead-of-changing-them-to -空间



为什么只是不逃避呢? printf '%s\\t%s\n' foo bar
Valentin Bajrami

@steeldriver谢谢,这与我所需要的非常相似,但是最终没有解决方案……
Asu

1
@Valentin输出foo\tbar...
wjandrea

1
尽管如此,您已经知道自己的终端设备存在问题:Bash本身解释$'\t'为制表符。因此,您始终可以将这样的字符串连接起来-例如作为赋值:v='This is'$'\t''a test'printf '%s' "$v"
并按原样

Answers:


9

就像ilkkachu所说的那样,bash并不是问题,但是终端仿真器可以将制表符转换为输出的空格。

检查不同的终端,腻子,xterm和konsole会将制表符转换为空格,而urxvt和gnome-terminal则不会。因此,另一种解决方案是切换端子。


3
运行后,也可以由tty驱动程序完成此操作stty tab3
斯特凡Chazelas

14

两者之间的空白实际上是5个空格。

不,这不对。不在echo或的输出中printf

$ echo -e 'foo\tbar' | od -c
0000000   f   o   o  \t   b   a   r  \n
0000010

强制将标签打印为标签的正确方法是什么,以便我可以选择输出并将其复制到带有标签的其他位置?

这是一个不同的问题。它与外壳无关,而与终端仿真器有关,终端仿真器将选项卡转换为输出上的空格。许多(但不是全部)都这样做。

将带有制表符的输出重定向到文件,然后从那里复制文件,或者unexpand在输出上将空格转换为制表符,可能会更容易。(尽管它也不知道制表符以什么空白开头,并且在可能的情况下将所有空格转换为制表符。)当然,这取决于您需要对输出执行什么操作。


我的意思是,当我尝试选择输出时,它被视为5个空格。感谢您使用“ od -c”来验证命令输出的内容。
阿苏

1
@Asu我想他明白。他的解决方案是通过其他方式获取输出,因为不能保证终端仿真器在窗口中选择选项卡时会将其保留为选项卡。但是,我只是检查了一下,而腻子,xterm和konsole将制表符转换为空格,而urxvt和gnome-terminal却没有。因此,另一种解决方案是切换端子。
JoL

@JoL是的,这是我刚刚在一分钟前得出的结论,我认为如果有人愿意将其张贴为这样的话,这将是公认的答案……
Asu

1
@Asu,是的,我想过要手动解决此问题。这样做会很烦人,但是后来我承认我还没有意识到有些终端仿真器支持复制选项卡。更改为可以的方法当然是更好的解决方案!
ilkkachu

4

在中printf '%s\t%s\n' foo barprintf输出foo<TAB>bar<LF>

fobar是单宽度的图形的字符。

接收到这些字符后,终端将显示相应的标志符号,并将光标向右移动一列,除非光标已到达屏幕的右边缘(原始远程打字机中为纸张),在这种情况下,它可能会换行然后返回到屏幕的左边缘(自动换行),或者根据终端及其配置方式放弃该字符。

<Tab><LF>是两个控制字符。<LF>(aka newline)是Unix文本中的行定界符,但对于终端,它仅输入一行(将光标向下移动一个位置)。因此内核中的终端驱动程序实际上会将其转换为<CR>(返回到屏幕的左边缘),<LF>(光标向下)(stty onlcr通常默认情况下为打开)。

<Tab> 告诉终端将光标移动到下一个制表位(默认情况下,大多数终端上的制表位相隔8个位置,但也可以配置为在任意位置设置),而不会用空格填充空白。

因此,如果将这些字符发送到终端,并且制表符每8列停住一次,而光标位于空行的开头,则会导致:

foo     bar

打印在屏幕上的那一行。如果在光标位于包含的行中的第三位置时发送了它们xxxxyyyyzzzz,则会导致:

xxfooyyybarz

在不支持制表的终端上,可以将终端驱动程序配置为将这些选项卡转换为空格序列。(stty tab3)。

在原始的远程打字机中,SPC字符会将光标向右移动,而退格键(\b)将其向左移动。现在,在现代终端中,SPC会向右移动并擦除(按您的期望写入空格字符)。因此,的项\b必须比ASCII更新。在大多数现代终端,它实际上是一个字符序列:<Esc>[C

还有更多的转义序列可将n字符向左,向右,向上,向下或屏幕上的任意位置移动。还有其他转义序列可擦除(填充空白)屏幕的部分行或区域等。

那些序列通常用于通过视觉应用程序,如vilynxmuttdialog其中文本在屏幕上的任意位置写入。

现在,所有X11终端仿真器和其他一些非X11终端仿真器(例如GNU)都screen使您可以选择屏幕区域以进行复制粘贴。在vi编辑器中选择一部分内容时,您不想复制用于生成该输出的所有转义序列。您想要选择在那里看到的文本。

例如,如果您运行:

printf 'abC\rAC\bB\t\e[C\b\bD\n'

它模拟了您在其中输入的编辑器会话abC,返回到开头,用替换abACCB,移至下一个制表位,然后在右侧再增加一列,在左侧再增加两列,然后输入D

你看:

ABC    D

也就是说ABC,有4列的间隙和D

如果用xterm或鼠标putty选择该选项ABC,它们将存储在选择项中,四个空格字符D,而不是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>就像<LF>有时在文本文件中(有时<CR>在MSDOS文本文件中,有时是<FF>用于分页符)中发现的少数几个控制字符之一一样。

因此,一些终端模拟器不选择可能的情况下将它们复制在复制粘贴的缓冲区来保存它们(这通常不是的情况下<CR>,也不<LF>虽然)。

例如,在基于VTE的终端中,例如gnome-terminal,您可能会看到,当printf 'a\tb\n'在空行上选择输出时,gnome-terminal实际上存储a\tb在X11选择中,而不是a7个空格和b

但是,对于输出printf 'a\t\bb\n',其存储a,6位和b,以及用于printf 'a\r\tb\n'a,7位和b

在其他情况下,终端将尝试复制实际输入,例如在运行后选择两行时printf 'a \nb\n',将保留不可见的尾随空间。或者,当选择两行时,如果右行换行导致两行不包含LF字符。

现在,如果要将输出存储printf到CLIPBOARD X11select中,最好的做法是直接执行以下操作:

printf 'foo\tbar\n' | xclip -sel c

请注意,将其粘贴到xterm其他终端或大多数其他终端时,xterm实际上将其替换为\n\r因为这是xterm您按下时发送的字符Enter(终端驱动程序可能会将其转换回\n)。


这是很有见地的,谢谢。我已经尝试过xclip解决方案,并且可以使用。但这并不能完全符合我的想法,需要X11。也许这会在某个时候派上用场,谢谢!
阿苏

@Asu X11是在诸如Unix之类的终端仿真器中xtermputty在Unix上处理复制粘贴选择的方法。其他终端仿真器可能具有它们自己的复制粘贴机制以及在其中存储任意内容的方式,例如GNU屏幕中的readbufregister命令。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.