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

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

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).


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'

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

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.


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.