substituição de comando: / dev / stdin: Permissão negada

5

Eu quero ler todo o stdin em variável. Eu não quero gerar novos processos.

Do manual do bash:

The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

Funciona principalmente, mas esse material /proc/fd é facilmente quebrado por sudo :

# same user. works
[root@okdistr ~]# echo aaa | bash -c 'echo $(</dev/stdin)'
aaa

# different user. fail
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(</dev/stdin)'
bash: /dev/stdin: Permission denied

# spawn new process. not want
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(cat)'
aaa

# try to read from fd: silently fails
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'

# works, but too complex
[root@okdistr ~]# echo aaa | sudo -u nobody bash -c 'a=; while true; do rc=0; read -N1024 b || rc=$?; a=$a$b; [ $rc = 0 ] || break; done; echo "$a"'
aaa

[root@okdistr ~]#

Por que $(<&0) falha?

    
por basin 01.07.2015 / 20:59

2 respostas

2

Primeiro, minha solução sugerida é apresentada abaixo. Depois disso, cada um dos dois erros que você observou são discutidos.

Solução sugerida

As variáveis de bash não podem conter um caractere NUL. Consequentemente, só é possível ler um arquivo inteiro em uma variável bash se o arquivo não contiver tais caracteres. Sujeito a esta limitação bash, o seguinte deve funcionar para você:

$ echo aaa | { read -rd "" v; echo "$v"; }
aaa

Com read -d '' var , o bash lerá stdin até que um caractere NUL seja encontrado. Como as variáveis bash não podem conter caracteres NUL, essa abordagem não limita você além das limitações inerentes do bash.

A opção -r para read impede que o bash interprete as seqüências de barra invertida. Além disso, se você deseja preservar espaços em branco iniciais e finais, adicione IFS= antes da instrução read .

"tente ler de fd: falha silenciosamente"

# echo aaa | sudo -u nobody bash -c 'echo $(<&0)'
#

A falha silenciosa acima ocorre mesmo sem sudo:

$ echo aaa | bash -c 'echo $(<&0)'
$

Até acontece sem criar o subshell:

$ echo aaa | echo $(<&0)
$

O problema é que &0 não é um nome de arquivo válido. Observe:

$ echo aaa | cat &0
[1] 22635
bash: 0: command not found

No bash, &0 só é significativo quando combinado com < ou > .

Vamos ver novamente a documentação do bash:

The command substitution $(cat file) can be replaced by the equivalent but faster $(< file).

Como cat &0 não funciona, não se deve esperar que $(< &0) funcione.

"usuário diferente. falha"

Isso pode parecer uma coisa razoável de se fazer:

# echo aaa | sudo -u nobody bash -c 'echo $(cat /dev/stdin)'
cat: /dev/stdin: Permission denied

Para ver por que ele falha, vamos examinar as permissões de /dev/stdin :

# echo aaa | sudo -u nobody bash -c 'ls -lH /dev/stdin'
prw------- 1 root root 0 Jul  1 15:42 /dev/stdin

O usuário não tem permissão para acessar esse arquivo. Isso é razoável: não se quer que ninguém mexa nos arquivos do root.

Um sistema operacional 'mais inteligente' pode saber que ninguém tem acesso a /dev/stdin neste caso particular mas, por questões de segurança, provavelmente é bom que o sistema operacional não tente inteligente em si.

    
por 02.07.2015 / 00:56
0

Você não precisa desses /dev links para fazer isso. Eu sei que sudo irá limpar as variáveis de ambiente, mas você ainda pode passar variáveis na forma de argumentos, já que você está chamando outra shell ...

sudo -u nobody bash -c '
    a=$1; echo "$a"
' -- "$(echo aaa)"

... ou ...

echo aaa | { 
    sudo -u nobody bash -c '
        a=$1; echo "$a"
    ' -- "$(</dev/fd/0)"
}

Ambas as impressões:

aaa

... para stdout ...

    
por 02.07.2015 / 12:10

Tags