Como induzir um comando a pensar que sua saída está indo para um terminal

38

Dado um comando que altera seu comportamento quando sua saída está indo para um terminal (por exemplo, produzir saída colorida), como essa saída pode ser redirecionada em um pipeline enquanto preserva o comportamento alterado? Deve haver uma utilidade para isso, da qual não estou ciente.

Alguns comandos, como grep --color=always, possuem sinalizadores de opção para forçar o comportamento, mas a questão é como solucionar os programas que dependem apenas do teste de seu descritor de arquivo de saída.

Se isso importa, meu shell está bashno Linux.

Amir
fonte
Você precisa saber como o comando testa seu descritor de arquivo de saída e, de alguma forma, retorna resultados consistentes com os resultados que seriam vistos em um terminal de saída.
Andrew Henle

Respostas:

21

Você pode obter o que precisa usando unbuffer.

unbufferé um tcl/ expectscript. Olhe a fonte, se quiser. Observe também a seção CAVEATS no man.

Observe também que ele não executa aliases como:

alias ls='ls --color=auto'

a menos que se adicione um truque, como observado por Stéphane Chazelas:

Se você fizer um alias unbuffer='unbuffer '(observe o espaço à direita), os aliases serão expandidos depois unbuffer.

Runium
fonte
Nota sobre aliases reconhecidos - ele também não funcionará com outras construções de shell, como builtins e funções.
Amir
4
Se você fizer um alias unbuffer='unbuffer '(observe o espaço à direita), os aliases serão expandidos depois unbuffer.
Stéphane Chazelas
unbufferisto é! sudo apt install expect- Isso não estava claro.
Loxaxs 17/03/19
22

Uma história de conjuntos de ferramentas

Você não é a primeira pessoa a querer essa ferramenta. As pessoas estão querendo essas ferramentas há 30 anos. E eles existem há quase tanto tempo também.

A primeira ferramenta para esse tipo de coisa foi o pacote "pty" de Daniel J. Bernstein, descrito por Rich Salz como uma "faca Ginsu", que Bernstein escreveu na virada da década de 1990 para enganar o nethack (sic!). A versão 4 do pacote "pty" foi publicada em 1992 para comp.sources.unix(volume 25 edições 127 a 135). Ainda é localizável na Internet. Paul Vixie descreveu na época:

O que posso dizer? Corta, corta, lava a louça, acompanha o cachorro. "Funciona", o que significa que, se você seguir as instruções, receberá um pacote de trabalho sem puxar os cabelos ou ranger de dentes ou outras atividades padrão de transporte.

Mais tarde, Bernstein atualizou isso, em algum momento antes de 1999-04-07, com um pacote "ptyget", que ele anunciou:

Eu montei um novo alocador de pseudo-tty, ptyget. Uma versão alfa está em ftp://koobera.math.uic.edu/pub/software/ptyget-0.50.tar.gz. Há uma lista de discussão ptyget; para participar, envie uma mensagem vazia para [email protected]. Eu projetei a interface do ptyget do zero. É muito mais modular que pty; a interface pty básica agora foi dividida em três partes:

  • ptyget: um pequeno programa de baixo nível - o único programa setuid do pacote - que aloca uma nova pseudo-tty e a passa para o programa de sua escolha
  • ptyspawn: outro pequeno programa que executa um processo filho em uma pseudo-tty, aguardando a saída e observando as paradas
  • ptyio: outro programa apenas um pouco maior que move os dados para frente e para trás

A velha faca Ginsu ptyagora está escrita ptybandage, o que é sinônimo ptyget ptyio -t ptyspawn; pty -d, para anexar programas de rede a pseudo-ttys, agora está escrito ptyrun, o que é sinônimo ptyget ptyio ptyspawn; e nobufé sinônimo de ptyget ptyio -r ptyspawn -23x. Dividi os recursos de gerenciamento de sessões em um pacote separado.

Esse pacote separado era o pacote "sess".

"ptyget" é, aliás, notável por exemplificar uma versão muito antiga e uma das poucas instâncias publicadas do sistema de compilação "redo" nunca publicado de Berstein. dependoné um precursor claro de redo-ifchange.

Uso

ptybandage

ptybandageé o que as pessoas geralmente querem em uma sessão de login. Seu principal caso de uso é criar programas sensíveis a que suas entradas, saídas ou erros padrão estejam conectados aos terminais, operem dessa maneira, mesmo que estejam de fato em pipelines de shell ou tenham seus descritores de arquivo padrão redirecionados para o arquivo.

É preciso um comando a ser executado (que tem de ser um comando externo apropriado, é claro) e executa-lo de tal maneira que ele acha que seu padrão de entrada, saída e erro estão ligados a um terminal, conectando aqueles através de ptybandage's entrada, saída e erro padrão original.

Ele lida com as nuances da execução sob as caixas de controle de tarefas, assegurando que o caractere STOP do terminal não apenas pare, ptybandagemas também interrompa a execução do programa conectado ao terminal interno.

ptyrun

ptyruné o que as pessoas geralmente querem nos servidores de rede TCP. Seu caso de uso principal são ambientes de execução remota que não configuraram terminais, executando programas que não operam como desejado quando não há terminal.

Ele não espera estar sendo executado sob um shell de controle de tarefas e, se o comando que está sendo executado recebe um sinal de parada, é simplesmente reiniciado.

Conjuntos de ferramentas disponíveis

Dru Nelson publica as versões "pty" 4 e "ptyget".

Paul Jarc publica uma versão fixa do ptyget, que tenta lidar com o dispositivo pseudo-terminal específico do sistema operacional ioctls no original que os sistemas operacionais na verdade não fornecem mais.

O pacote fonte nosh vem com scripts ptybandangee práticas ptyrun, que usam a execlineferramenta de Laurent Bercot e os comandos de gerenciamento pseudo-terminal do próprio pacote nosh. A partir da versão 1.23 do nosh, eles estão disponíveis pré-empacotados no pacote nosh-terminal-extras. (As versões anteriores os forneciam apenas para pessoas que criavam a partir da fonte.)

Alguns exemplos usam

Jurjgen Oskam usando ptybandageno AIX para alimentar a entrada de um documento aqui para um programa que é explicitamente aberto e lê seu terminal de controle para um prompt de senha:

$ ptybandage dsmadmc << EOF> uit.txt
joskam
senha
sessão de consulta
processo de consulta
Sair
EOF

Andy Bradford usando ptyrunno OpenBSD sob daemontools e ucspi-tcp para tornar o bgplgshprograma de controle de roteador interativo acessível via rede, enquanto pensa que está falando com um terminal:

#! / bin / sh
exec 2> & 1
exec envuidgid rviews tcpserver -vDRHl0 0 23 ptyrun / usr / bin / bgplgsh

Leitura adicional

JdeBP
fonte
Obrigado pela lição da história, mas as ferramentas que você sugere não estão disponíveis em uma distribuição moderna do Linux, portanto, não são úteis.
Amir
4
Há vários links e uma seção inteira da resposta dedicada a onde as ferramentas definitivamente estão disponíveis.
JdeBP
1
Qual a sua opinião expect?
CMCDragonkai 23/01
20

Você pode usar o socat para iniciar seu processo com um pty conectado e fazer com que o socat conecte a outra extremidade do pty a um arquivo. Qual AFAIU é exatamente o que você pediu:

socat EXEC:"my-command",pty GOPEN:mylog.log

Esse método fará com que o isattychamado my-commandretorne truee um processo que depende apenas dele será enganado para gerar códigos de controle. Observe que alguns processos (principalmente grep) também verificam o valor da TERMvariável de ambiente; portanto, você pode precisar configurá-lo para algo razoável, como"xterm"

Guss
fonte
13

Também há uma boa solução postada aqui no Superusuário por KarlC :

Compile uma pequena biblioteca compartilhada:

echo "int isatty(int fd) { return 1; }" | gcc -O2 -fpic -shared -ldl -o isatty.so -xc -

Em seguida, diga ao seu comando para carregar essa isatty(3)substituição dinamicamente:

LD_PRELOAD=./isatty.so mycommand

Isso pode não funcionar para todos os comandos existentes, pode até quebrar alguns de maneiras inesperadas, mas provavelmente funcionaria na maioria dos casos.

Amir
fonte
2
Para usuários de MacOS, você pode obter o mesmo comportamento usando:DYLD_INSERT_LIBRARIES=./isatty.so DYLD_FORCE_FLAT_NAMESPACE=y mycommand
Christopher Shroba 13/17
12

Que tal usar script(1)?

Por exemplo:

script -q -c 'ls -G' out_file

Salvará a lssaída out_filecom os códigos de cores preservados.

sagi
fonte
Isso não funciona aqui. Como as cores são preservadas? Existe uma ferramenta que eu devo usar para produzir out_filecom suas cores?
Kira
1
@Kira para visualizar arquivos com seqüências de escape de cores ANSI, eu uso less -R. Nesse caso, porém, eu queria que a saída continuasse no pipeline, que acabou no meu terminal. Usando catpara ilustração, era algo como script -q -c 'ls -G' /dev/null | cat, que suprime typescriptcompletamente o arquivo, deixando apenas a saída do programa.
Amir
Para evitar a criação de um arquivo, basta usar um traço ( -) como scriptarquivo de saída, por exemple:script -q -c 'ls -G' -
Franklin Piat
0

Baseado na resposta de @ Amir , aqui está um script que gera e inclui a biblioteca em tempo de execução:

#!/bin/bash
set -euo pipefail

function clean_up {
  trap - EXIT # Restore default handler to avoid recursion
  [[ -e "${isatty_so:-}" ]] && rm "$isatty_so"
}
# shellcheck disable=2154 ## err is referenced but not assigned
trap 'err=$?; clean_up; exit $err' EXIT HUP INT TERM

isatty_so=$(mktemp --tmpdir "$(basename "$0")".XXXXX.isatty.so)
echo "int isatty(int fd) { return 1; }" \
  | gcc -O2 -fpic -shared -ldl -o "$isatty_so" -xc -
# Allow user to SH=/bin/zsh faketty mycommand
"${SH:-$SHELL}" -c 'eval $@' - LD_PRELOAD="$isatty_so" "$@"
Tom Hale
fonte