Eu quero escrever um CGI, que deve ler um número especificado de bytes de STDIN. Minha ideia é fazer assim:
dd bs=$CONTENT_LENGTH count=1
Mas eu queria saber se o tamanho do bloco é limitado por qualquer outra coisa além da RAM.
$ dd bs=1000000000000
dd: memory exhausted by input buffer of size 1000000000000 bytes (931 GiB)
A página de manual do coreutils do GNU não especifica nenhum limite.
As especificações POSIX para
dd
não especificam um máximo explicitamente, mas existem alguns limites:size_t
, pois esse é o tipo do número de bytes a serem lidos fornecido àread
função ;read
também é especificado para ter um limite deSSIZE_MAX
;read
apenas transfere até 2.147.479.552 bytes de qualquer maneira.Em uma plataforma de 64 bits,
size_t
tem 64 bits de comprimento; além disso, não é assinado, portantodd
, falhará quando forem fornecidos valores maiores que 2 64 – 1:No Linux em x86 de 64 bits,
SSIZE_MAX
é 0x7ffffffffffffffL (executeecho SSIZE_MAX | gcc -include limits.h -E -
para verificar), e esse é o limite de entrada:Depois de encontrar um valor que seja aceito, o próximo limite é a quantidade de memória que pode ser alocada, pois
dd
precisa alocar um buffer antes de poder ler nele.Depois de encontrar um valor que pode ser alocado, você atingirá o
read
limite (no Linux e outros sistemas com limites semelhantes), a menos que você use GNUdd
e especifiqueiflag=fullblock
:(
dd
copiado pouco menos de 2 31 bytes, ou seja , o limite do Linux mencionado acima, nem metade do que eu pedi).Conforme explicado no link de perguntas e respostas acima, você precisará
fullblock
copiar de forma confiável todos os dados de entrada em qualquer caso, para qualquer valorbs
maior que 1.Independentemente do seu valor máximo, aí você tem um problema maior; da especificação POSIX:
(enfase adicionada)
Como escrevi no passado ,
dd
é uma ferramenta extremamente estúpida: no seu caso, basicamente se resume abs
é apenas o argumentodd
usado para realizar aread(2)
syscall, masread(2)
é permitido realizar uma "short read", ou seja, retornar menos bytes do que o solicitado. De fato, é o que ele faz se tiver alguns bytes disponíveis agora, mesmo que não sejam tudo o que você pediu; isso é típico se o arquivo de entrada for um tty, um pipe ou um socket (então você está particularmente em risco com seu CGI...). Apenas tente:Aqui eu digitei
asd
e apertei enter;dd
lê-lo (executando um singleread(STDIN_FILENO, buf, 1000)
e escrevê-lo; ele fez umread
conforme solicitado, então ele sai. Não parece que ele copiou 1000 bytes.Em última análise, o "padrão" simples
dd
é uma ferramenta muito estúpida para a maioria das necessidades; você pode disputar para fazer o que você precisa:bs=1
e usandocount
para o número de bytes; isso é garantido para copiar o número de bytes que você precisa (se disponível antes do EOF), mas é bastante ineficiente, pois executa uma syscall por byte;fullblock
bandeira; isso garante quedd
acumule um bloco de entrada completo antes de escrevê-lo. Observe, no entanto, que isso não é padrão (GNU dd tem isso, IDK sobre outros).Por fim, se você estiver optando por uma extensão não POSIX, minha sugestão é apenas usar
head -c
: ele fará a coisa certa com buffer sensato e sem limites de tamanho específicos, garantindo correção e bom desempenho.O máximo depende do sistema (incluindo suas políticas de alocação) e da memória disponível no momento.
Em vez de tentar ler tudo de uma vez (você poderia esgotar a memória, desacelerar as coisas por causa da troca, você teria que adicionar verificações para ver se realmente funcionava ...), você poderia ler blocos de tamanho razoável com
dd
.Digamos que você queira ler esses bytes e colocá-los em um arquivo. No bash, você pode executar algo assim (o total de bytes está em $ total):