请注意,从版本 292 开始,xterm 删除了 ASCII 控制字符,除了\b
, \r
, \t
, DEL
(0x7f) 和\n
(它像其他终端一样转换\n
为\r
s),您可以将它们与allowPasteControls
资源一起带回。自 2015 年 10 月起gnome-terminal
, VTE terminator
( ...使用的终端仿真器库xfce-terminal
)也开始使用
因此,在那些终端中,^C
, ^[
, ^D
, ^Z
, ^\
, ^U
,^W
不再是问题,但 DEL, \b
, (对于完成可以扩展命令替换\t
的某些配置(包括默认配置)非常危险),并且仍然存在。zsh
\r
\n
xterm
还有一些粘贴模式可以在这里提供帮助。
在某些zsh 或 vim安全粘贴插件\e[?2004h
中使用的序列启用括号粘贴模式。
zsh
( zle
) 自 5.1 和bash
( readline
) 自 4.4 现在支持内置。虽然默认情况下在zsh
中启用它,但您需要使用bind 'set enable-bracketed-paste on'
in手动启用它bash
(或在 or 中的配置readline
中)。~/.inputrc
/etc/inputrc
这个包装了 和 之间的\e[200~
选择\e[201~
。
大多数其他现代终端,如基于 VTE 的终端 ( gnome-terminal
, xfce-terminal
, terminator
...), rxvt
, konsole
, OS/XTerminal
现在也支持该终端。
但是,在某些其他终端(或其版本)中(或带有allowPasteControls
in xterm
),存在缺陷,\e[201~
可能出现在选择中,并将被视为右括号。
这可以通过括号来解决\e\e[201~\e[200~201~
,但是AFAIK还没有由任何终端仿真器完成(这意味着应用程序会看到几个粘贴)。
^C
//如果没有在 tty 线路规程中禁用,^Z
也会^\
导致信号被发送到终端的前台进程组。ISIG
使用序列启用引用的粘贴模式\e[?2005h
(使用 禁用\e[?2005l
)。
这个在每个字符(实际上是字节)前面加上一个^V
字符。
^V
是规范模式下 tty 行规则的默认lnext
(文字 next)字符,并且也被vi
其他编辑器和一些行编辑器(如 readline 或zsh
's zle)识别为这样。
那个与上面的括号模式没有相同的问题,并且有利于终端规范模式(比如当你这样做时cat > file
)和其他一些应用程序,但有一些缺点:
- 换行符和 CR 最终呈现为
^M
. 这可以通过另一个转义序列来避免:\e[?2006h
,但这会导致换行符作为 NUL 字符插入vim
并在终端规范模式下显示为^J
(除非你这样做stty -echoctl
)(尽管这只是一个外观问题)。
zle
这对于没有正确插入的多字节字符不起作用vim
。
- 一些可视化应用程序不能
^V
像文字那样处理 next,因此您可能仍然需要有选择地关闭它。
- 您不能在其中使用它,
vim
例如^V 1
不插入1
但^A
在那里。
- 除了支持它之外,我不知道有任何其他终端
xterm
,但是我还没有进行过广泛的调查。
它还允许您通过配置定义自己的括号粘贴模式。例如,使用:
XTerm*allowPasteControls: true
XTerm.VT100.translations: #override \
Ctrl Shift<KeyPress> Insert: \
insert-formatted("\033[202~%S~%s", CLIPBOARD,PRIMARY,CUT_BUFFER0)'
它会插入CLIPBOARD
// 。PRIMARY
_ 然后应用程序可以可靠地解释它(尽管它仍然需要在 tty 行规则中禁用)。CUT_BUFFER0
^[[202~<size-in-bytes>~<content>
Shift+Ctrl+InsertISIG
另一种方法是使用伪 tty 包装器,^V
仅在控制字符前面插入这些包装器。这样的包装器应该能够以一定的可靠性检测粘贴中的控制字符,因为真正的键盘按键一次只会发送一个字符或以 ESC 开头的一系列字符,而粘贴一次会发送几个。
您仍然会遇到^J
终端规范模式或^@
in 中显示换行符的问题vim
,但这可以通过 shell 的一些合作来解决
概念证明:
例如用作:
./safe-paste bash
在该包装下启动一个bash
外壳。
#!/usr/bin/perl
use IO::Pty;
use IO::Stty;
my $pty = new IO::Pty;
my $pid = fork();
die "Cannot fork" if not defined $pid;
unless ($pid) {
$pty->make_slave_controlling_terminal();
my $slave = $pty->slave();
close $pty;
$slave->clone_winsize_from(\*STDIN);
open(STDIN,"<&". $slave->fileno())
or die "Couldn't reopen STDIN for reading, $!\n";
open(STDOUT,">&". $slave->fileno())
or die "Couldn't reopen STDOUT for writing, $!\n";
open(STDERR,">&". $slave->fileno())
or die "Couldn't reopen STDERR for writing, $!\n";
close $slave;
exec(@ARGV);
die "Cannot exec(@ARGV): $!";
}
$pty->close_slave();
$SIG{WINCH} = sub {
$pty->slave->clone_winsize_from(\*STDIN);
};
my $old = IO::Stty::stty(\*STDIN, '-g');
IO::Stty::stty(\*STDIN, 'raw', '-echo');
$tty = fileno($pty);
my ($rin,$ein) = ('','','');
vec($rin, 0, 1) = 1;
vec($rin, $tty, 1) = 1;
vec($ein, $tty, 1) = 1;
my ($to_stdout, $to_tty) = ('', '');
my $eof;
$SIG{CHLD} = sub {$eof = 1};
until ($eof && $to_stdout eq '' && $to_tty eq '') {
my ($rout,$wout,$eout,$timeleft);
my $win = '';
vec($win, 0, 1) = 1 if ($to_stdout ne "");
vec($win, $tty, 1) = 1 if ($to_tty ne "");
($nfound,$timeleft) = select($rout=$rin,$wout=$win,$eout=$ein,undef);
if ($nfound > 0) {
if (vec($eout, $tty, 1)) {
print STDERR "Exception on $tty\n";
}
if (vec($rout, 0, 1)) {
my $buf;
if (sysread(STDIN, $buf, 4096)) {
if ($buf =~ /.[\0-\037\177]/ || $buf =~ /^(?:[\0-\032\034-\037]|\033.*?[~a-zA-NP-Z])./) {
$buf =~ s/[\0-\037\177]/\026$&/g;
# TODO: add UTF-8 sanitizing
$buf =~ y/\r/\n/;
}
$to_tty .= $buf;
} else {
$eof = 1;
vec($rin, 0, 1) = 0;
}
}
if (vec($rout, $tty, 1)) {
my $buf;
if (sysread($pty, $buf, 4096)) {
$to_stdout .= $buf;
} else {
$eof = 1;
vec($rin, $tty, 1) = 0;
$to_tty = '';
}
}
if ($to_tty ne '' && vec($wout, $tty, 1)) {
my $written = syswrite($pty, $to_tty);
$to_tty = substr($to_tty, $written) if $written;
}
if ($to_stdout ne '' && vec(wout, 1, 1)) {
my $written = syswrite(STDOUT, $to_stdout);
$to_stdout = substr($to_stdout, $written) if $written;
}
}
}
END{IO::Stty::stty(\*STDIN, $old)}
更好的方法可能是使用剪贴板管理器,您可以在其中指定粘贴模式并标记潜在危险的选择。