Redirecionando stdout para um arquivo no qual você não tem permissão de gravação

113

Ao tentar modificar um arquivo sem ter permissões de gravação, você recebe um erro:

> touch /tmp/foo && sudo chown root /tmp/foo
> echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Sudoing não ajuda, porque executa o comando como root, mas o shell lida com o redirecionamento de stdout e abre o arquivo da mesma maneira:

> sudo echo test > /tmp/foo
zsh: permission denied: /tmp/foo

Existe uma maneira fácil de redirecionar o stdout para um arquivo no qual você não tem permissão para gravar, além de abrir um shell como root e manipular o arquivo dessa maneira?

> sudo su
# echo test > /tmp/foo
Michael Mrozek
fonte
2
Responda a uma pergunta semelhante de StackOverflow stackoverflow.com/questions/82256/…
Cristian Ciupitu
Como você pode não ter permissão para um arquivo que você mesmo criou em tmp? é por causa de umask?
K961,
@ k961 Eu costumava chownmudar de proprietário; foi apenas um exemplo
Michael Mrozek

Respostas:

116

Sim, usando tee. Então echo test > /tmp/foose torna

echo test | sudo tee /tmp/foo

Você também pode acrescentar ( >>)

echo test | sudo tee -a /tmp/foo
Gert
fonte
27
Tee também produzirá para stdout; às vezes você não deseja que o conteúdo preencha a tela. Para corrigir isso, façaecho test | sudo tee /tmp/foo > /dev/null
Shawn J. Goff
Como você fará isso com o heredoc?
JohnDoea
26

Para substituir o conteúdo do arquivo pela saída de echo(como o >operador de redirecionamento de shell).

echo test | sudo dd of=/tmp/foo

Para gravar no arquivo (no início, embora você possa usar seekpara produzir diferentes deslocamentos) sem truncar (como o 1<>operador de shell Bourne):

echo test | sudo dd of=/tmp/foo conv=notrunc

Para acrescentar ao arquivo (como >>), com o GNU dd:

echo test | sudo dd of=/tmp/foo oflag=append conv=notrunc

Veja também GNU dd's conv=exclpara evitar derrotar um arquivo existente (como set -o noclobberem shells POSIX) e conv=nocreatpelo contrário (atualizar apenas um arquivo existente).

Chris Jepeway
fonte
esperto! isso alivia a necessidade de fazer echo test | sudo tee /tmp/foo >/dev/nullpara descartar a saída.
Adam Katz
2
Eu posso ter que voltar atrás; ddnão é confiável para isso, a menos que você esteja usando opções obscuras apenas do GNU iflag=fullblock oflag=fullblock, o que remove a elegância desta resposta. Eu vou ficar com tee.
Adam Katz
5
dd é confiável com o não obscuro bs = 1
umeboshi
2
@umeboshi Mas confiável apenas se você tiver experiência suficiente para saber exatamente o que está fazendo. Pois ddpode ser bastante perigoso (se não quer dizer: devastador) se apenas um pequeno erro foi cometido. Portanto, para novos usuários, prefiro recomendar o teemétodo para estar em um local seguro.
Syntaxerror
11
@AdamKatz, no caso de dd of=filesozinho (sem count/ skip...), é confiável. iflag=fullblocknão é necessário porque aqui ddescreve na saída o que leu na entrada. Não importa se não foram blocos completos.
Stéphane Chazelas
12

tee é provavelmente a melhor escolha, mas, dependendo da sua situação, algo como isto pode ser suficiente:

sudo sh -c 'echo test > /tmp/foo'
phunehehe
fonte
11
3 problemas: em evalvez de um simples sh -c; -i, que mudará seu diretório de trabalho; executando o comando inteiro como root, o que pode mudar seu comportamento ou introduzir um risco desnecessário. Por que isso foi aprovado?
4
você não fez isso, mas é enganoso, geralmente ruim e talvez até perigoso.
5

Enquanto eu concordo, essa | sudo teeé a maneira canônica, às vezes o sed (aqui assumindo o GNU sed) pode funcionar:

cat sudotest 
line 1

sudo sed -i '1iitest' sudotest && cat sudotest 
itest
line 1

sudo sed -i '$aatest' sudotest && cat sudotest 
itest
line 1
atest

-imodifica o arquivo no lugar. 1isignifica inserir antes da linha 1. $asignifica acrescentar após a última linha.

Ou copie para o xclipboard:

somecommand | xclip
sudo gedit sudotest
move cursor to desired place, click middle mouse button to insert, save
Usuário desconhecido
fonte
11
Isto é redigido como um enigma chinês antigo. Não consigo entender como isso conseguiu tantos votos positivos. ( hrmph )
syntaxerror
11
@syntaxerror: Senhor, desculpe, não sou um falante nativo de inglês. Se você especificar o que não está claro para você, eu poderia tentar melhorar minha resposta. Comparado com 65 votos positivos para Gert, 4 também não parece ser tantos votos positivos, não acha?
usuário desconhecido
Bem, eu ficaria bastante satisfeito com 4 :-D Seu inglês não é nada ruim. É apenas redigido em uma espécie de nerdspeak técnico conciso. Devo adivinhar que você é um programador. Os codificadores entre si se entenderão perfeitamente dessa maneira, mas um não codificador deve pensar que está redigido como ... como dito.
syntaxerror 31/01
Observe que sed -i, na verdade, não modifica o arquivo no local - ele cria um arquivo temporário e o renomeia ao sair. Então você não vai ser capaz de fazer algo assim tail -f ...no arquivo original e ver a saída usando sed -i ...enquanto o gasoduto está sendo executado
Andrew Henle
@AndrewHenle: Sim, já que o tamanho pode ser aumentado ou diminuído, e já que esse é provavelmente o caso da maioria das invocações de sed e você não pode nem - afaik - escrever no mesmo local em SSDs, é apenas uma operação pseudo 'no lugar' . Como um falante não inglês nativo, posso pedir uma breve expressão, que provavelmente não é mal interpretada? Apenas -i creates a new file of same nameou há algo mais compacto? Eu acho que gosto in place, porque explica o i. A página de manual do gnu-sed também o coloca no lugar e a bandeira longa é --in-place.
usuário desconhecido
2

Tenho pensado no fundo de minha mente por um problema semelhante e descobri as seguintes soluções:

  • sudo uncatOnde uncatestá um programa que lê a entrada padrão e a grava no arquivo nomeado na linha de comando, mas ainda não o escrevi uncat.

  • sudocata variante do sudoeditque eu não escrevi ainda que faz um limpador sudo catou sudo uncat.

  • ou esse pequeno truque de usar sudoeditcom um EDITOR que é um script de shell

    #!/bin/sh
    # uncat
    cat > "$1"
    

    que pode ser invocado como um |sudo ./uncat fileou mais, | EDITOR=./uncat sudoeditmas que tem efeitos colaterais interessantes.

hildred
fonte
catleva uma lista de arquivos para con gato inata, portanto CCT deve dar uma lista de arquivos para un con gato inata para. Teria que usar magia para decidir quanto colocar em cada arquivo. Nome alternativo incluem dog, to-file, redirect.
ctrl-alt-Delor
Não consigo pensar em nenhuma razão para querer uncatquando tiver tee.
Wildcard
11
Bem, teetem a desvantagem trivial de gravar seu stdin em seu stdout - o que é trivialmente mitigado redirecionando o stdout para /dev/null. Outras alternativas incluem dd of=/tmp/foo(mencionadas em outra resposta), que grava informações de status no stderr, e cp /dev/stdin /tmp/foo.
Scott
1

Use esponja do pacote moreutils. Tem a vantagem de não gravar no stdout.

echo test | sudo sponge /tmp/foo

Use a opção -a para anexar a um arquivo em vez de substituí-lo.

Hontvári Levente
fonte
11
Eu não tenho certeza do que vantagem não escrever para stdout presentes, mas você pode simplesmente redirecionar a saída para /dev/nullse fosse um problema
Michael Mrozek
11
Claro que posso redirecionar para / dev / null, mas o comando é mais fácil de ler e digitar sem o redirecionamento. A vantagem de não escrever no stdout é que meu terminal não está cheio de lixo.
Hontvári Levente
11
Eu não instalaria um pacote para poupar um redirecionamento, mas uso regularmente a esponja, por isso já está lá.
Hontvári Levente
0

O erro vem da ordem em que o shell faz as coisas.

O redirecionamento é tratado antes da execução do shell sudoe, portanto, é feito com as permissões do usuário em que você está trabalhando no momento. Como você não tem permissões de gravação para criar / truncar o destino do redirecionamento, você recebe um permission deniederro do shell.

A solução é garantir que o arquivo de saída seja criado sob a identidade que lhe é fornecida sudo, por exemplo, com tee:

$ generate_output | sudo tee target_file
Kusalananda
fonte