Por que sudo cat dá uma permissão negada, mas sudo vim funciona bem?


86

Estou tentando automatizar a adição de uma fonte de repositório no arquivo pacman.conf do meu arch, mas usando o echocomando em meu script de shell. No entanto, ele falha assim: -

sudo echo "[archlinuxfr]" >> /etc/pacman.conf
sudo echo "Server = http://repo.archlinux.fr/\$arch" >> /etc/pacman.conf
sudo echo " " >> /etc/pacman.conf

-bash: /etc/pacman.conf: Permission denied

Se eu fizer alterações em /etc/pacman.conf manualmente usando o vim,

sudo vim /etc/pacman.conf

e sair do vim com :wq, tudo funciona bem e meu pacman.conf foi atualizado manualmente sem reclamações de "permissão negada".

Porque isto é assim? E como faço sudo echopara trabalhar? (aliás, eu tentei usar sudo cattambém, mas falhou com permissão negada também)


Respostas:


51

O problema é que o redirecionamento está sendo processado pelo shell original, não pelo sudo. As conchas não são capazes de ler mentes e não sabem que esse particular >>se destina a ele sudoe não a ele.

Você precisa:

  1. cite o redirecionamento (para que seja passado para sudo)
  2. e use sudo -s(para que sudouse um shell para processar o redirecionamento entre aspas.)

1
Então, fazer isso em duas etapas como esta (1) sudo -s(2) echo "# test" >> /etc/pacman.conf funciona. Mas é possível fazer isso em uma única linha?
Calvin Cheng

15
sudo -s 'echo "# test" >>/etc/pacman.conf'é o que eu estava tentando transmitir a você.
geekosaur

2
Na verdade, tentei fazer isso depois de ler sua resposta acima, mas recebi um erro estranho como este: - sudo -s 'echo "# test" >> /etc/pacman.conf' /bin/bash: echo "# test" >> /etc/pacman.conf: No such file or directorypor isso tentei posteriormente o processo manual de 2 etapas.
Calvin Cheng

16
Ah ... uma última tentativa desta maneira funcionou -echo 'echo "# test" >> /etc/pacman.conf' | sudo -s
Calvin Cheng

1
como / onde posso encontrar informações sobre a sudodesativação da configuração -s? visudo?
Calvin Cheng

127

Como @geekosaur explicou, o shell faz o redirecionamento antes de executar o comando. Quando você digita:

sudo foo >/some/file

Seu processo shell atual faz uma cópia de si mesmo que primeiro tenta abrir /some/filepara gravação, depois torna o descritor de arquivo sua saída padrão e só então é executado sudo.

Se você tiver permissão (configurações de sudoer geralmente impedem a execução de shells), você pode fazer algo assim:

sudo bash -c 'foo >/some/file'

Mas acho que uma boa solução em geral é usar em | sudo teevez de >e em | sudo tee -avez de >>. Isso é especialmente útil se o redirecionamento for o único motivo de que preciso sudo; afinal, a execução desnecessária de processos como root é exatamente o que sudofoi criado para evitar. E executar echocomo root é simplesmente idiota.

echo '[archlinuxfr]' | sudo tee -a /etc/pacman.conf >/dev/null
echo 'Server = http://repo.archlinux.fr/$arch' | sudo tee -a /etc/pacman.conf >/dev/null
echo ' ' | sudo tee -a /etc/pacman.conf >/dev/null

Eu adicionei > /dev/nullno final, porque teeenvia sua saída para ambos o arquivo chamado e sua própria saída padrão, e eu não preciso vê-lo no meu terminal. (O teecomando atua como um conector "T" em um pipeline físico, que é de onde vem seu nome.) E eu mudei para aspas simples ( '... ') em vez de duplas ( "... ") para que tudo seja literal e eu não precisou colocar uma barra invertida na frente do $dentro $arch. (Sem as aspas ou barra invertida, $archseria substituído pelo valor do parâmetro do shell arch, que provavelmente não existe, caso em que o $arché substituído por nada e simplesmente desaparece.)

Então isso cuida de gravar arquivos como root usando sudo. Agora, para uma longa digressão sobre as maneiras de produzir texto contendo nova linha em um script de shell. :)

Para BLUF, como eles dizem, minha solução preferida seria apenas alimentar um here-document no sudo teecomando acima ; então não há necessidade de catou echoou printfou quaisquer outros comandos. As aspas simples foram movidas para a introdução da sentinela <<'EOF', mas têm o mesmo efeito lá: o corpo é tratado como texto literal, portanto, $arché deixado sozinho:

sudo tee -a /etc/pacman.conf >/dev/null <<'EOF'
[archlinuxfr]
Server = http://repo.archlinux.fr/$arch

EOF

Mas enquanto eu faria isso, existem alternativas. Aqui estão alguns:

Você pode ficar com um echopor linha, mas agrupar todos eles em um subshell, então você só precisa anexar ao arquivo uma vez:

(echo '[archlinuxfr]'
 echo 'Server = http://repo.archlinux.fr/$arch'
 echo ' ') | sudo tee -a /etc/pacman.conf >/dev/null

Se você adicionar -eao echo(e estiver usando um shell que suporte essa extensão não POSIX), poderá incorporar novas linhas diretamente na string usando \n:

# NON-POSIX - NOT RECOMMENDED
echo -e '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Mas, como dito acima, esse não é um comportamento especificado pelo POSIX; seu shell pode apenas ecoar um literal -eseguido por uma string com um monte de literais \n. A maneira POSIX de fazer isso é usar em printfvez de echo; ele trata automaticamente seu argumento como o echo -efaz, mas não acrescenta automaticamente uma nova linha no final, então você também precisa colocar um extra \n:

printf '[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n \n' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Com qualquer uma dessas soluções, o que o comando obtém como uma string de argumento contém a sequência de dois caracteres \n, e cabe ao próprio programa de comando (o código dentro de printfou echo) traduzir isso em uma nova linha. Em muitos shells modernos, você tem a opção de usar aspas ANSI $'... ', o que traduzirá as sequências \nem novas linhas literais antes que o programa de comando veja a string. Isso significa que essas strings funcionam com qualquer comando, incluindo o plain old -e-less echo:

echo $'[archlinuxfr]\nServer = http://repo.archlinux.fr/$arch\n ' | 
  sudo tee -a /etc/pacman.conf >/dev/null

Mas, embora mais portáteis do que echo -e, as aspas ANSI ainda são uma extensão não POSIX.

E, novamente, embora essas sejam todas opções, prefiro a tee <<EOFsolução direta acima.


Interessante! Você poderia adicionar uma observação explicando o motivo de também fragmentar o arquivo em / dev / null?
knite

sudo bash -c 'foo >/some/file'funciona para mim no osx :-)
kayochin

Prefiro esta resposta porque funciona com texto multilinha. cat <<EOF | sudo tee -a /some/file > /dev/null ...
ltvan

Essa é efetivamente minha última resposta, exceto que você adicionou um catprocesso estranho . :)
Mark Reed

17

http://www.innovationsts.com/blog/?p=2758

Como as instruções acima não são tão claras, estou usando as instruções dessa postagem do blog. Com exemplos, fica mais fácil ver o que você precisa fazer.

$ sudo cat /root/example.txt | gzip> /root/example.gz
-bash: /root/example.gz: Permissão negada

Observe que é o segundo comando (o comando gzip) no pipeline que causa o erro. É aí que entra a nossa técnica de usar o bash com a opção -c.

$ sudo bash -c 'cat /root/example.txt | gzip> /root/example.gz '
$ sudo ls /root/example.gz
/root/example.gz

Podemos ver na saída do comando ls que a criação do arquivo compactado foi bem-sucedida.

O segundo método é semelhante ao primeiro no sentido de que estamos passando uma string de comando para o bash, mas estamos fazendo isso em um pipeline via sudo.

$ sudo rm /root/example.gz
$ echo "cat /root/example.txt | gzip> /root/example.gz" | sudo bash
$ sudo ls /root/example.gz
/root/example.gz


1
Para um comando tão simples, pode ser um pouco melhor usar sh em vez de bash. Por exemplo, sudo sh -c 'cat /root/example.txt | gzip> /root/example.gz '. Mas por outro lado, boas explicações, +1.
bryce


1

PASSO 1 criar uma função em um arquivo bash ( write_pacman.sh)

#!/bin/bash

function write_pacman {
 tee -a /etc/pacman.conf > /dev/null << 'EOF'
  [archlinuxfr]
  Server = http://repo.archlinux.fr/\$arch
EOF
}

'EOF'não interpretará $archvariável.

Arquivo bash de origem STE2

$ source write_pacman.sh

PASSO 3 execute a função

$ write_pacman

para que serve a cotação no EOF?
bilabila

0

anexar arquivos (sudo cat):

cat <origin-file> | sudo tee -a <target-file>

anexar echo ao arquivo (sudo echo):

echo <origin> | sudo tee -a <target-file>

(EXTRA) desconsiderar a saída:

echo >origin> | sudo tee -a <target-file> >/dev/null
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.