Costumo usar tee /proc/self/fd/2
para mostrar algo para stdout e stderr, capturar stdout, enquanto ainda mantenho toda a saída para stderr.
Por exemplo, eu tenho um do.sh
:
STD_OUT_STR=$(CMD ARGS ... 2>&1 | tee /proc/self/fd/2)
# handle STD_OUT_STR ...
...
Isso funciona muito bem.
por exemplo,
./do.sh
que funciona basicamente assim:
bash -c 'echo hi | tee /proc/self/fd/2'
saídas
hi
hi
No entanto, um dia eu o executo sudo -u A_USER ./do.sh
, ele falha.
sudo -u A_USER ./do.sh'
que basicamente executa:
sudo -u A_USER bash -c 'echo hi | tee /proc/self/fd/2'
saídas
tee: /proc/self/fd/2: Permission denied
hi
Até agora, tenho que seguir a solução alternativa:
sudo -u A_USER bash -c 'echo hi | tee >(cat >&2)'
Entendo que isso é por motivos de segurança: não compartilhe o pty do usuário atual com outros usuários.
Gostaria de saber se existe alguma opção de sudo -u A_USER
deixar o processo acessar seu pty?
EDIT: Preciso colocar o tee /proc/self/fd/2
ou mais em um script digamos do.sh, ou seja, não posso colocar tee /proc/self/fd/2
fora do do.sh, então sudo -u A_USER bash -c '...' | tee ...
não será útil para mim.
Ou:
como não há nada específico de bash lá, funcionaria em sistemas diferentes de Linux ou Cygwin.
Enquanto na maioria dos sistemas, abrir
/dev/fd/x
é como fazerdup(x)
, como>&x
emsh
, no Linux ou Cygwin,/dev/fd/x
, assim como/proc/self/fd/x
são apenas links simbólicos "mágicos" para os arquivos abertos nos descritores de arquivo correspondentes, portanto, abri-los não gera o mesmo comportamento de umdup()
de forma alguma.fd 2 pode muito bem estar aberto em um arquivo que você não tem permissão para abrir para gravação como no seu caso, ou não pode ser aberto como um soquete (como é normalmente o caso de processos iniciados por onde stderr é geralmente um
systemd
soquete parajournald
).Mesmo que seja um arquivo normal para o qual você tem permissão de gravação, está fazendo
tee /proc/self/fd/2
outee /dev/fd/2
está errado, pois abre o arquivo correspondente desde o início e o trunca. Eventee -a /proc/self/fd/2
está errado (embora na prática seja um pouco melhor), pois, embora haja truncamento e faça com que as gravações sejam feitas no final do arquivo, isso só é válido se stderr também for aberto no modo de acréscimo .Por exemplo, após:
A
hi
saída foi sobre atest
escrita portee
.No Linux/Cygwin, usar
/dev/fd/x
//proc/self/fd/x
(ou/dev/stdout
,/dev/stderr
) só funciona de forma aceitável quando o fd correspondente está aberto em alguns tipos de arquivos não pesquisáveis, como pipes ou dispositivos tty, mas mesmo assim você pode ter problemas de permissão como aqui.O seu
tee >(cat >&2)
é transformado pelo seu shell em algumtee /proc/self/fd/somefd
lugarsomefd
aberto em um pipe e, como o pipe foi criado pelo usuário que escreve nele, não há problema de permissão, mas ainda é errado fazer isso no bash shell especificamente como o bash faz não espere por essecat
processo.Depois:
Você descobrirá que às vezes
file
contém:Porque
bash
voltou antescat
conseguiu escrevertest
eecho
emite seuhi
primeiro.Aqui, você pode usar
zsh
em vez debash
qual pode fazer o teeing para vários descritores de arquivo em vez de apenas arquivos como comtee
:Onde quando um fd (aqui 1/stdout) é redirecionado mais de uma vez para saída, ele é redirecionado para um canal para um processo interno que faz o tee.
Funcionaria sem a ressalva do bash acima, mas esses
tee
ecat
seriam desnecessários, pois o zsh possui o recurso embutido.Se
zsh
fosse o shell de chamada, você poderia ter aquele tee interno feito pelo usuário de chamada em vez de USER_A com:Qual no Linux/Cygwin seria melhor do que
em si melhor do que
(mesmo que eles também contornem o problema de permissão) pelos motivos mencionados acima.
Com
sh
(onde você não pode usar a substituição do processo) +tee
, uma solução para transformar stderr em um canal para contornar o problema de permissão é fazer isso manualmente com:Onde dentro do grupo de comando interno, stderr se torna um canal para
cat
, compartilhado porcmd
, embora você também possa deixarcmd
stderr sozinho com:O que seria um equivalente mais próximo do seu,
cmd | tee >(cat >&2)
embora aquicat
seja esperado adequadamente.Apenas corra
tee
como você mesmo.Considere este comando:
Ele envia linhas de texto para stdout.
Não há necessidade de executar
tee
como esse outro usuário. Aceite as linhas e processe-as como você mesmo. Invoquesudo
novamente se realmente precisar dessas permissões posteriormente no pipeline.Se você atingir um ponto crítico, seu script sempre poderá gravar apenas no sistema de arquivos e, em seguida, você
cat
outail -f
o arquivo de texto em outro processo.O produtor de texto sempre pode usar stdbuf para ajustar o buffer de linha e exibir os resultados prontamente, se desejado.