Tenga en cuenta que a partir de la versión 292 , xterm elimina los caracteres de control ASCII, excepto \b
, \r
, \t
, DEL
(0x7f) y \n
(convierte \n
a \r
s como otros terminales), y puede recuperarlos con el recurso allowPasteControls
. VTE (la biblioteca de emulador de terminal utilizada por gnome-terminal
, terminator
, xfce-terminal
...) también lo hace desde octubre de 2015
Entonces, en esos terminales, ^C
, ^[
, ^D
, ^Z
, ^\
, ^U
, ^W
ya no son un problema, pero DEL, \b
, \t
(muy peligroso con algunas configuraciones (incluida la predeterminada) de zsh
donde la terminación puede expandir las sustituciones de comandos), \r
y \n
siguen siendo.
xterm
también tiene un par de modos de pegado que pueden ayudar aquí.
-
el modo de pegado entre corchetes habilitado con la secuencia \e[?2004h
como se usa en algunos zsh o vim safe - pegar complementos .
zsh
( zle
) desde 5.1 y bash
( readline
) desde 4.4 ahora tienen soporte para eso integrado. Mientras que está habilitado de forma predeterminada en zsh
, debe habilitarlo manualmente con bind 'set enable-bracketed-paste on'
en bash
(o en la configuración readline
en ~/.inputrc
o /etc/inputrc
).
Este envuelve la selección entre \e[200~
y \e[201~
.
La mayoría de las otras terminales modernas, como las basadas en VTE ( gnome-terminal
, xfce-terminal
, terminator
...), rxvt
, konsole
, OS / X Terminal
ahora también admiten esa.
Sin embargo, en algunos de esos otros terminales (o versiones de los mismos) (o con allowPasteControls
en xterm
), es defectuoso que \e[201~
aparezca en la selección, y se tomará como corchete de cierre.
Esto podría solucionarse entre corchetes como \e\e[201~\e[200~201~
, pero no lo ha hecho ningún emulador de terminal todavía AFAIK (y significaría que la aplicación vería varias pastas).
^C
/ ^Z
/ ^\
también causaría que las señales se envíen al grupo de proceso de primer plano del terminal si ISIG
no se deshabilitó en la disciplina de línea tty.
-
El modo de pegar entre comillas habilitado con la secuencia \e[?2005h
(deshabilitado con \e[?2005l
).
Este prefigura cada carácter (en realidad byte) con un carácter ^V
.
^V
es el carácter predeterminado lnext
( literal next ) de la disciplina de línea tty en modo canónico, y también es reconocido como tal por vi
y otros editores y algunos editores de línea como readline o zsh
's zle.
Ese no tiene el mismo problema que el modo entre corchetes anterior, y tiene la ventaja de que funciona para el modo canónico del terminal (como cuando haces cat > file
) y algunas otras aplicaciones. pero tiene algunos inconvenientes:
- newline y CR terminan representándose como
^M
. Eso se puede evitar con otra secuencia de escape: \e[?2006h
, pero eso hace que las nuevas líneas se inserten como caracteres NUL en vim
y aparezcan como ^J
(a menos que hagas stty -echoctl
) en el modo canónico del terminal (aunque es solo un problema cosmético).
- Eso no funciona bien para los caracteres de varios bytes que no se insertan correctamente en
zle
o vim
, por ejemplo.
- algunas aplicaciones visuales no manejan
^V
como literal siguiente , por lo que es posible que aún tengas que desactivarlo selectivamente.
- no puede usarlo en
vim
porque ^V 1
, por ejemplo, no inserta 1
sino ^A
allí.
- No conozco ningún otro terminal que no sea compatible con
xterm
, pero no he realizado una encuesta exhaustiva.
-
también le permite definir su propio modo de pegado entre corchetes a través de la configuración. Por ejemplo, con:
XTerm*allowPasteControls: true
XTerm.VT100.translations: #override \
Ctrl Shift<KeyPress> Insert: \
insert-formatted("3[202~%S~%s", CLIPBOARD,PRIMARY,CUT_BUFFER0)'
insertaría CLIPBOARD
/ PRIMARY
/ CUT_BUFFER0
como ^[[202~<size-in-bytes>~<content>
en Shift + Ctrl + Insert . Entonces, la aplicación podría interpretar eso de manera confiable (aunque todavía tendría que deshabilitar ISIG
en la disciplina de línea tty).
Otro enfoque sería usar un envoltorio de pseudo-tty que inserte esos ^V
solo delante de los caracteres de control. Dicha envoltura debería ser capaz de detectar caracteres de control en pastas con cierta confiabilidad porque las pulsaciones reales del teclado solo enviarían un carácter a la vez o una secuencia de caracteres que comienzan con ESC, mientras que las pastas enviarían varios a la vez .
Aún tendrías el problema de las nuevas líneas mostradas como ^J
en el modo canónico terminal o ^@
en vim
, pero eso podría solucionarse con cierta cooperación por parte del shell
Una prueba de concepto:
Para ser utilizado por ejemplo como:
./safe-paste bash
Para iniciar un shell bash
bajo ese contenedor.
#!/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 =~ /.[ XTerm*allowPasteControls: true
XTerm.VT100.translations: #override \
Ctrl Shift<KeyPress> Insert: \
insert-formatted("3[202~%S~%s", CLIPBOARD,PRIMARY,CUT_BUFFER0)'
-77]/ || $buf =~ /^(?:[./safe-paste bash
-24-7]|3.*?[~a-zA-NP-Z])./) {
$buf =~ s/[#!/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 =~ /.[%pre%-77]/ || $buf =~ /^(?:[%pre%-24-7]|3.*?[~a-zA-NP-Z])./) {
$buf =~ s/[%pre%-77]/6$&/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)}
-77]/6$&/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)}
Un mejor enfoque probablemente sería usar un administrador de portapapeles donde puedes especificar el modo de pegado y eso marcaría las selecciones potencialmente peligrosas.