Como resolver “permissão negada” ao usar o sudo com redirecionamento no Bash?

118

Ao usar o sudo para permitir edições em arquivos, recebo regularmente 'permissão negada'.

Por exemplo, meu mouse está nervoso e lento, por isso quero desativar a pesquisa:

sudo echo "options drm_kms_helper poll=N">/etc/modprobe.d/local.conf

Fico com uma senha solicitada e obtenho:

bash: /etc/modprobe.d/local.conf: Permission denied

Então, tentei fazer uma alteração temporária para desativar o poll usando:

sudo echo N> /sys/module/drm_kms_helper/parameters/poll

Mais uma vez o sistema respondeu com:

bash: /sys/module/drm_kms_helper/parameters/poll: Permission denied

Alguma idéia?

    
por Jack 19.12.2012 / 05:12

6 respostas

129

O redirecionamento de saída (via o operador > ) é feito pelo shell, não pelo echo . Você precisa fazer o login como root

sudo -i

Então você pode usar o redirecionamento

echo N> /sys/module/drm_kms_helper/parameters/poll

Caso contrário, você pode executar uma string bash com o sudo

sudo bash -c "echo N> /sys/module/drm_kms_helper/parameters/poll"
    
por shantanu 19.12.2012 / 05:22
71

O redirecionamento de saída é feito pelo shell do qual o comando foi chamado . Então, quebrando tudo em pedaços, aqui está o que está acontecendo *:

  • o shell invoca sudo echo "options drm_kms_helper poll=N" , que executa o comando sudo com a linha de comando echo "options drm_kms_helper poll=N"

  • sudo pede uma senha, abre o shell do superusuário e invoca echo "options drm_kms_helper poll=N" , que executa o comando echo passando "options drm_kms_helper poll=N"

  • echo, executando com root privileges, imprime a string para sua saída padrão.

  • O comando
  • echo termina, o shell do superusuário é encerrado, sudo termina

  • o shell do qual o comando foi chamado coleta a saída e tenta redirecioná-la para /etc/modprobe.d/local.conf , que é gravável somente pelo root. Obtém o erro "permissão negada".

Para as maneiras de corrigir isso, veja a resposta @shantanu.

(*) - enquanto a seqüência acima ajuda a entender porque o comando falha, na realidade as coisas acontecem um pouco fora de ordem: o shell original percebe o redirecionamento e tenta abrir o arquivo para escrita antes de invocar o sudo ... comando. Ao abrir o arquivo, o shell nem invoca o comando que deveria gravar no arquivo (graças a @PanosRontogiannis por apontar isto).

Aqui está um teste rápido:

$ touch ./onlyroot.txt
$ sudo chown root:root ./onlyroot.txt
$ sudo bash -c "whoami | tee who.txt" > onlyroot.txt
bash: onlyroot.txt: Permission denied

No teste acima, o whoami | tee who.txt iria criar um arquivo chamado who.txt contendo a palavra "root". No entanto, quando o redirecionamento de saída falha no shell de chamada, o arquivo "who.txt" também está ausente porque o comando não foi chamado.

    
por Sergey 19.12.2012 / 06:19
55

Adicionando a resposta de Shantanu:

... Ou você poderia usar um comando tee assim:

sudo tee /sys/module/drm_kms_helper/parameters/poll <<<10

ou se for a saída de um comando:

echo 10 | sudo tee /sys/module/drm_kms_helper/parameters/poll
    
por Untitled 19.12.2012 / 07:50
13

Uma abordagem que eu não vi mencionada aqui é simplesmente executar toda a linha de comando em seu próprio shell. O sudo manpage em si dá um exemplo dessa abordagem:

% bl0ck_qu0te%
$ sudo sh -c "cd /home ; du -s * | sort -rn > USAGE"
    
por kojiro 19.12.2012 / 14:53
3

Outra opção é usar um arquivo temporário. Isso é útil em um script bash.

temp=$(mktemp)
echo "Hello, world!" > $temp
sudo cp $temp /etc/wherever
    
por user545424 29.07.2014 / 17:51
3

sudo dd of=

Para anexar como você deseja:

echo inbytes | sudo dd of=outfile oflag=append conv=notrunc

ou para recriar o arquivo do zero:

echo inbytes | sudo dd of=outfile

Vantagens:

  • mais agradável que tee , pois não há /dev/null de redirecionamento
  • mais agradável que sh , já que nenhum subshell explícito (mas um implícito para o redirecionamento)
  • dd tem muitas opções poderosas, por ex. status=progress para ver o progresso da transferência

Funciona porque o sudo encaminha o stdin para o comando.