Calculando o tamanho de um diretório usando Python?
182
Antes de reinventar essa roda específica, alguém tem uma boa rotina para calcular o tamanho de um diretório usando Python? Seria muito bom se a rotina formatasse bem o tamanho em Mb / Gb etc.
NÃO seria muito bom. Você deve ter uma função para calcular o tamanho e uma função bastante independente (que pode ser usada também com tamanhos de memória, por exemplo) para "formatar o tamanho perfeitamente em Mb / Gb etc".
John Machin
17
Sim, eu sei, mas isso economiza fazendo duas perguntas.
Gary Willoughby
1
O treecomando nos sistemas * nix faz tudo isso de graça. tree -h -d --du /path/to/dir.
Me
@mehdu -sh /path/to/dir/*
mrgloom
Respostas:
252
Isso percorre todos os subdiretórios; somando tamanhos de arquivo:
import os
def get_size(start_path ='.'):
total_size =0for dirpath, dirnames, filenames in os.walk(start_path):for f in filenames:
fp = os.path.join(dirpath, f)# skip if it is symbolic linkifnot os.path.islink(fp):
total_size += os.path.getsize(fp)return total_size
print(get_size(),'bytes')
E um oneliner para se divertir usando os.listdir ( não inclui subdiretórios ):
import os
sum(os.path.getsize(f)for f in os.listdir('.')if os.path.isfile(f))
Atualizado
Para usar os.path.getsize , isso é mais claro do que usar o método os.stat (). St_size.
Obrigado a ghostdog74 por apontar isso!
os.stat - st_size Dá o tamanho em bytes. Também pode ser usado para obter o tamanho do arquivo e outras informações relacionadas ao arquivo.
import os
nbytes = sum(d.stat().st_size for d in os.scandir('.')if d.is_file())
Atualização 2018
Se você usa o Python 3.4 ou anterior, pode considerar usar o walkmétodo mais eficiente fornecido pelo scandirpacote de terceiros . No Python 3.5 e posterior, este pacote foi incorporado à biblioteca padrão e os.walkrecebeu o aumento correspondente no desempenho.
Atualização 2019
Recentemente, tenho usado pathlibcada vez mais, eis uma pathlibsolução:
from pathlib importPath
root_directory =Path('.')
sum(f.stat().st_size for f in root_directory.glob('**/*')if f.is_file())
+1 mas o oneliner não retorna um resultado válido porque não é recursiva
luc
2
Sim, é apenas para o caso do diretório plano.
monkut
35
Para diversão real, você pode criar um tamanho recursivo em uma linha: sum (os.path.getsize (os.path.join (caminho do caminho, nome do arquivo)) para o caminho do caminho, nomes de arquivos, nomes de arquivos em os.walk (PATH) para nomes de arquivos em nomes de arquivos)
Driax
2
Mas você deve usar st_sizese não quiser seguir os links simbólicos, como deve usar lstat.
asmeurer
3
Aviso! isso não é o mesmo que 'du -sb'. Veja a resposta de Samuel Lampa! Seu código ignora o tamanho da pasta usada para armazenar o FAT.
Yauhen Yakimovich
43
Algumas das abordagens sugeridas até o momento implementam uma recursão, outras empregam um shell ou não produzirão resultados bem formatados. Quando o seu código é único para plataformas Linux, você pode obter a formatação como de costume, incluindo a recursão, como uma linha. Exceto pela printúltima linha, ele funcionará para as versões atuais de python2e python3:
du.py
-----#!/usr/bin/python3import subprocess
def du(path):"""disk usage in human readable format (e.g. '2,1GB')"""return subprocess.check_output(['du','-sh', path]).split()[0].decode('utf-8')if __name__ =="__main__":print(du('.'))
é simples, eficiente e funcionará para arquivos e diretórios multiníveis:
Python, sendo multi-plataforma na natureza, provavelmente deve coíbe de este
Jonathan
11
Obrigado por estas observações. Eu adicionei algumas ressalvas sobre a dependência da plataforma à resposta. No entanto, grande parte do código Python é um script único. Esse código não deve vir com limitações funcionais, passagens longas e propensas a erros ou resultados incomuns em casos extremos, apenas por uma portabilidade além de qualquer necessidade . É, como sempre, um trade-off, e é a responsabilidade do desenvolvedor para escolher com sabedoria;)
flaschbier
9
Nitpick: não Linux, mas Unix / POSIX específica :)
Sr. Tubarão
3
Provavelmente é aconselhável adicionar a opção '-x' ao comando du para limitar a pesquisa ao sistema de arquivos. Em outras palavras, use ['du', '-shx', path].
Keith Hanlan
24
Aqui está uma função recursiva (que recursivamente resume o tamanho de todas as subpastas e seus respectivos arquivos) que retorna exatamente os mesmos bytes que ao executar "du -sb". no linux (onde "." significa "a pasta atual"):
Essa função também calcula o tamanho do link simbólico - se você quiser pular os links simbólicos, verifique se não é isso: if os.path.isfile (itempath) e os.path.islink (itempath) e elif os.path.isdir ( itempath) e os.path.islink (itempath).
airween 23/08/15
17
Tamanho da pasta recursiva do Python 3.5 usando os.scandir
def folder_size(path='.'):
total =0for entry in os.scandir(path):if entry.is_file():
total += entry.stat().st_size
elif entry.is_dir():
total += folder_size(entry.path)return total
Método one-liner do Python 3, se não estiver preocupado com a recursividade sum([entry.stat().st_size for entry in os.scandir(file)]). Observe que a saída está em bytes, / 1024 para obter KB e / (1024 * 1024) para obter MB.
weiji14
4
@ weiji14 Perca os suportes, ou seja sum(entry.stat().st_size for entry in os.scandir(file)),. Não há razão para fazer uma lista, porque também sumleva iteradores.
Vedran Šego
8
A resposta monknut é boa, mas falha no link simbólico quebrado, portanto você também deve verificar se esse caminho realmente existe
if os.path.exists(fp):
total_size += os.stat(fp).st_size
A resposta aceita não leva em consideração os links físicos ou físicos e contaria esses arquivos duas vezes. Você gostaria de acompanhar quais inodes viu e não adicionar o tamanho desses arquivos.
import os
def get_size(start_path='.'):
total_size =0
seen ={}for dirpath, dirnames, filenames in os.walk(start_path):for f in filenames:
fp = os.path.join(dirpath, f)try:
stat = os.stat(fp)exceptOSError:continuetry:
seen[stat.st_ino]exceptKeyError:
seen[stat.st_ino]=Trueelse:continue
total_size += stat.st_size
return total_size
print get_size()
A resposta de Chris é boa, mas poderia ser mais idiomática usando um conjunto para verificar os diretórios vistos, o que também evita o uso de uma exceção para o fluxo de controle:
def directory_size(path):
total_size =0
seen = set()for dirpath, dirnames, filenames in os.walk(path):for f in filenames:
fp = os.path.join(dirpath, f)try:
stat = os.stat(fp)exceptOSError:continueif stat.st_ino in seen:continue
seen.add(stat.st_ino)
total_size += stat.st_size
return total_size # size in bytes
A resposta de Chris também não leva em consideração os links simbólicos nem o tamanho dos diretórios. Eu editei sua resposta de acordo, a saída da função fixa agora é idêntica a df -sb.
Creshal
7
uma linha recursiva:
def getFolderSize(p):from functools import partial
prepend = partial(os.path.join, p)return sum([(os.path.getsize(f)if os.path.isfile(f)else getFolderSize(f))for f in map(prepend, os.listdir(p))])
Não é um forro embora. No entanto, calcula recursivamente o tamanho da pasta (mesmo que a pasta tenha várias pastas dentro) em bytes e fornece o valor correto.
Venkatesh
Fui para este tão fácil de usar e trabalhou primeira vez no Windows
hum3
5
Para a segunda parte da pergunta
def human(size):
B ="B"
KB ="KB"
MB ="MB"
GB ="GB"
TB ="TB"
UNITS =[B, KB, MB, GB, TB]
HUMANFMT ="%f %s"
HUMANRADIX =1024.for u in UNITS[:-1]:if size < HUMANRADIX :return HUMANFMT %(size, u)
size /= HUMANRADIX
return HUMANFMT %(size, UNITS[-1])
Um pouco tarde para a festa, mas em uma linha desde que você tenha glob2 e humanizar instalado. Observe que no Python 3, o padrão iglobpossui um modo recursivo. Como modificar o código do Python 3 é deixado como um exercício trivial para o leitor.
>>>import os
>>>from humanize import naturalsize
>>>from glob2 import iglob
>>> naturalsize(sum(os.path.getsize(x)for x in iglob('/var/**'))))'546.2 MB'
A partir do Python 3.5, o built-in globsuporta recursão. Você pode usar:glob.glob('/var/**', recursive=True)
adzenith
3
O script a seguir imprime o tamanho do diretório de todos os subdiretórios do diretório especificado. Ele também tenta se beneficiar (se possível) do armazenamento em cache das chamadas de funções recursivas. Se um argumento for omitido, o script funcionará no diretório atual. A saída é classificada pelo tamanho do diretório, do maior para o menor. Para que você possa adaptá-lo às suas necessidades.
from __future__ import print_function
import os
import sys
import operator
def null_decorator(ob):return ob
if sys.version_info >=(3,2,0):import functools
my_cache_decorator = functools.lru_cache(maxsize=4096)else:
my_cache_decorator = null_decorator
start_dir = os.path.normpath(os.path.abspath(sys.argv[1]))if len(sys.argv)>1else'.'@my_cache_decoratordef get_dir_size(start_path ='.'):
total_size =0if'scandir'in dir(os):# using fast 'os.scandir' method (new in version 3.5)for entry in os.scandir(start_path):if entry.is_dir(follow_symlinks =False):
total_size += get_dir_size(entry.path)elif entry.is_file(follow_symlinks =False):
total_size += entry.stat().st_size
else:# using slow, but compatible 'os.listdir' methodfor entry in os.listdir(start_path):
full_path = os.path.abspath(os.path.join(start_path, entry))if os.path.isdir(full_path):
total_size += get_dir_size(full_path)elif os.path.isfile(full_path):
total_size += os.path.getsize(full_path)return total_size
def get_dir_size_walk(start_path ='.'):
total_size =0for dirpath, dirnames, filenames in os.walk(start_path):for f in filenames:
fp = os.path.join(dirpath, f)
total_size += os.path.getsize(fp)return total_size
def bytes2human(n, format='%(value).0f%(symbol)s', symbols='customary'):"""
(c) http://code.activestate.com/recipes/578019/
Convert n bytes into a human readable string based on format.
symbols can be either "customary", "customary_ext", "iec" or "iec_ext",
see: http://goo.gl/kTQMs
>>> bytes2human(0)
'0.0 B'
>>> bytes2human(0.9)
'0.0 B'
>>> bytes2human(1)
'1.0 B'
>>> bytes2human(1.9)
'1.0 B'
>>> bytes2human(1024)
'1.0 K'
>>> bytes2human(1048576)
'1.0 M'
>>> bytes2human(1099511627776127398123789121)
'909.5 Y'
>>> bytes2human(9856, symbols="customary")
'9.6 K'
>>> bytes2human(9856, symbols="customary_ext")
'9.6 kilo'
>>> bytes2human(9856, symbols="iec")
'9.6 Ki'
>>> bytes2human(9856, symbols="iec_ext")
'9.6 kibi'
>>> bytes2human(10000, "%(value).1f %(symbol)s/sec")
'9.8 K/sec'
>>> # precision can be adjusted by playing with %f operator
>>> bytes2human(10000, format="%(value).5f %(symbol)s")
'9.76562 K'
"""
SYMBOLS ={'customary':('B','K','M','G','T','P','E','Z','Y'),'customary_ext':('byte','kilo','mega','giga','tera','peta','exa','zetta','iotta'),'iec':('Bi','Ki','Mi','Gi','Ti','Pi','Ei','Zi','Yi'),'iec_ext':('byte','kibi','mebi','gibi','tebi','pebi','exbi','zebi','yobi'),}
n = int(n)if n <0:raiseValueError("n < 0")
symbols = SYMBOLS[symbols]
prefix ={}for i, s in enumerate(symbols[1:]):
prefix[s]=1<<(i+1)*10for symbol in reversed(symbols[1:]):if n >= prefix[symbol]:
value = float(n)/ prefix[symbol]return format % locals()return format % dict(symbol=symbols[0], value=n)################################################################## main ()###############################################################if __name__ =='__main__':
dir_tree ={}### version, that uses 'slow' [os.walk method]#get_size = get_dir_size_walk### this recursive version can benefit from caching the function calls (functools.lru_cache)
get_size = get_dir_size
for root, dirs, files in os.walk(start_dir):for d in dirs:
dir_path = os.path.join(root, d)if os.path.isdir(dir_path):
dir_tree[dir_path]= get_size(dir_path)for d, size in sorted(dir_tree.items(), key=operator.itemgetter(1), reverse=True):print('%s\t%s'%(bytes2human(size, format='%(value).2f%(symbol)s'), d))print('-'*80)if sys.version_info >=(3,2,0):print(get_dir_size.cache_info())
Seu script funciona bem, mas você precisa mover a função null_decorator acima da linha 'if sys.version_info> = ...'. Caso contrário, você receberá uma exceção 'null_decorator' não definida. Funciona muito bem depois disso.
precisa saber é o seguinte
@ user2233949, obrigado! Eu modifiquei o código correspondentemente.
Estou usando python 2.7.13 com scandir e aqui está minha função recursiva de uma linha para obter o tamanho total de uma pasta:
from scandir import scandir
def getTotFldrSize(path):return sum([s.stat(follow_symlinks=False).st_size for s in scandir(path)if s.is_file(follow_symlinks=False)])+ \
+ sum([getTotFldrSize(s.path)for s in scandir(path)if s.is_dir(follow_symlinks=False)])>>>print getTotFldrSize('.')1203245680
Quando o tamanho dos subdiretórios é calculado, ele deve atualizar o tamanho da pasta do pai e isso continuará até atingir o pai raiz.
A função a seguir calcula o tamanho da pasta e todas as suas subpastas.
import os
def folder_size(path):
parent ={}# path to parent path mapper
folder_size ={}# storing the size of directories
folder = os.path.realpath(path)for root, _, filenames in os.walk(folder):if root == folder:
parent[root]=-1# the root folder will not have any parent
folder_size[root]=0.0# intializing the size to 0elif root notin parent:
immediate_parent_path = os.path.dirname(root)# extract the immediate parent of the subdirectory
parent[root]= immediate_parent_path # store the parent of the subdirectory
folder_size[root]=0.0# initialize the size to 0
total_size =0for filename in filenames:
filepath = os.path.join(root, filename)
total_size += os.stat(filepath).st_size # computing the size of the files under the directory
folder_size[root]= total_size # store the updated size
temp_path = root # for subdirectories, we need to update the size of the parent till the root parentwhile parent[temp_path]!=-1:
folder_size[parent[temp_path]]+= total_size
temp_path = parent[temp_path]return folder_size[folder]/1000000.0
Esse script informa qual arquivo é o maior no CWD e também indica em qual pasta o arquivo está. Este script funciona para mim no shell win8 e python 3.3.3
import os
folder=os.cwd()
number=0
string=""for root, dirs, files in os.walk(folder):for file in files:
pathname=os.path.join(root,file)## print (pathname)## print (os.path.getsize(pathname)/1024/1024)if number < os.path.getsize(pathname):
number = os.path.getsize(pathname)
string=pathname
## print ()print(string)print()print(number)print("Number in bytes")
Estou um pouco atrasado (e novo) aqui, mas optei por usar o módulo de subprocesso e a linha de comando 'du' com o Linux para recuperar um valor preciso para o tamanho da pasta em MB. Eu tive que usar if e elif para a pasta raiz porque, caso contrário, o subprocesso gera um erro devido ao valor diferente de zero retornado.
import subprocess
import os
## get folder size#def get_size(self, path):if os.path.exists(path)and path !='/':
cmd = str(subprocess.check_output(['sudo','du','-s', path])).\
replace('b\'','').replace('\'','').split('\\t')[0]return float(cmd)/1000000elif os.path.exists(path)and path =='/':
cmd = str(subprocess.getoutput(['sudo du -s /'])). \
replace('b\'','').replace('\'','').split('\n')
val = cmd[len(cmd)-1].replace('/','').replace(' ','')return float(val)/1000000else:raiseValueError
def humanized_size(num, suffix='B', si=False):if si:
units =['','K','M','G','T','P','E','Z']
last_unit ='Y'
div =1000.0else:
units =['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']
last_unit ='Yi'
div =1024.0for unit in units:if abs(num)< div:return"%3.1f%s%s"%(num, unit, suffix)
num /= div
return"%.1f%s%s"%(num, last_unit, suffix)
Tamanho do arquivo / pasta recursiva do Python 3.6+ usando os.scandir. Tão poderoso quanto na resposta de @blakev, mas mais curto e no estilo python do EAFP .
import os
def size(path,*, follow_symlinks=False):try:with os.scandir(path)as it:return sum(size(entry, follow_symlinks=follow_symlinks)for entry in it)exceptNotADirectoryError:return os.stat(path, follow_symlinks=follow_symlinks).st_size
def recursive_dir_size(path):
size =0for x in os.listdir(path):ifnot os.path.isdir(os.path.join(path,x)):
size += os.stat(os.path.join(path,x)).st_size
else:
size += recursive_dir_size(os.path.join(path,x))return size
Eu escrevi esta função que me fornece o tamanho total exato de um diretório, tentei outro para soluções de loop com os.walk, mas não sei por que o resultado final sempre foi menor que o tamanho real (no ubuntu 18 env). Eu devo ter feito algo errado, mas quem se importa escreveu este funciona perfeitamente bem.
tree
comando nos sistemas * nix faz tudo isso de graça.tree -h -d --du /path/to/dir
.du -sh /path/to/dir/*
Respostas:
Isso percorre todos os subdiretórios; somando tamanhos de arquivo:
E um oneliner para se divertir usando os.listdir ( não inclui subdiretórios ):
Referência:
Atualizado Para usar os.path.getsize , isso é mais claro do que usar o método os.stat (). St_size.
Obrigado a ghostdog74 por apontar isso!
os.stat - st_size Dá o tamanho em bytes. Também pode ser usado para obter o tamanho do arquivo e outras informações relacionadas ao arquivo.
Atualização 2018
Se você usa o Python 3.4 ou anterior, pode considerar usar o
walk
método mais eficiente fornecido peloscandir
pacote de terceiros . No Python 3.5 e posterior, este pacote foi incorporado à biblioteca padrão eos.walk
recebeu o aumento correspondente no desempenho.Atualização 2019
Recentemente, tenho usado
pathlib
cada vez mais, eis umapathlib
solução:fonte
st_size
se não quiser seguir os links simbólicos, como deve usarlstat
.Algumas das abordagens sugeridas até o momento implementam uma recursão, outras empregam um shell ou não produzirão resultados bem formatados. Quando o seu código é único para plataformas Linux, você pode obter a formatação como de costume, incluindo a recursão, como uma linha. Exceto pela
print
última linha, ele funcionará para as versões atuais depython2
epython3
:é simples, eficiente e funcionará para arquivos e diretórios multiníveis:
fonte
Aqui está uma função recursiva (que recursivamente resume o tamanho de todas as subpastas e seus respectivos arquivos) que retorna exatamente os mesmos bytes que ao executar "du -sb". no linux (onde "." significa "a pasta atual"):
fonte
Tamanho da pasta recursiva do Python 3.5 usando
os.scandir
fonte
sum([entry.stat().st_size for entry in os.scandir(file)])
. Observe que a saída está em bytes, / 1024 para obter KB e / (1024 * 1024) para obter MB.sum(entry.stat().st_size for entry in os.scandir(file))
,. Não há razão para fazer uma lista, porque tambémsum
leva iteradores.A resposta monknut é boa, mas falha no link simbólico quebrado, portanto você também deve verificar se esse caminho realmente existe
fonte
lstat
.A resposta aceita não leva em consideração os links físicos ou físicos e contaria esses arquivos duas vezes. Você gostaria de acompanhar quais inodes viu e não adicionar o tamanho desses arquivos.
fonte
os.lstat
(em vez deos.stat
), o que evita seguintes links simbólicos: docs.python.org/2/library/os.html#os.lstatA resposta de Chris é boa, mas poderia ser mais idiomática usando um conjunto para verificar os diretórios vistos, o que também evita o uso de uma exceção para o fluxo de controle:
fonte
df -sb
.uma linha recursiva:
fonte
Para a segunda parte da pergunta
fonte
Usando
pathlib
eu vim esta linha para obter o tamanho de uma pasta:E é isso que eu criei para uma saída bem formatada:
Uso:
Também me deparei com essa pergunta , que possui estratégias mais compactas e provavelmente com melhor desempenho para imprimir tamanhos de arquivo.
fonte
Você pode fazer algo assim:
neste caso, não testei o resultado antes de devolvê-lo, se você quiser, pode verificá-lo com commands.getstatusoutput.
fonte
os.walk
para verificar o tamanho da subpasta recursivamente?Uma linha que você diz ... Aqui está uma linha:
Embora eu provavelmente o dividisse e ele não realiza verificações.
Para converter para kb, consulte Biblioteca reutilizável para obter uma versão legível para humanos do tamanho do arquivo? e trabalhe
fonte
Um pouco tarde para a festa, mas em uma linha desde que você tenha glob2 e humanizar instalado. Observe que no Python 3, o padrão
iglob
possui um modo recursivo. Como modificar o código do Python 3 é deixado como um exercício trivial para o leitor.fonte
glob
suporta recursão. Você pode usar:glob.glob('/var/**', recursive=True)
O script a seguir imprime o tamanho do diretório de todos os subdiretórios do diretório especificado. Ele também tenta se beneficiar (se possível) do armazenamento em cache das chamadas de funções recursivas. Se um argumento for omitido, o script funcionará no diretório atual. A saída é classificada pelo tamanho do diretório, do maior para o menor. Para que você possa adaptá-lo às suas necessidades.
PS: usei a receita 578019 para mostrar o tamanho do diretório em formato compatível com humanos ( http://code.activestate.com/recipes/578019/ )
Saída de amostra:
EDIT: moveu null_decorator acima, como user2233949 recomendado
fonte
use library sh : o módulo
du
faz isso:se você deseja passar o asterix, use
glob
como descrito aqui .para converter os valores em legíveis humanas, use humanize :
fonte
para obter o tamanho de um arquivo, existe os.path.getsize ()
é relatado em bytes.
fonte
Pelo que vale a pena ... o comando tree faz tudo isso de graça:
Eu amo Python, mas, de longe, a solução mais simples para o problema não requer código novo.
fonte
É útil:
fonte
Estou usando python 2.7.13 com scandir e aqui está minha função recursiva de uma linha para obter o tamanho total de uma pasta:
https://pypi.python.org/pypi/scandir
fonte
Quando o tamanho dos subdiretórios é calculado, ele deve atualizar o tamanho da pasta do pai e isso continuará até atingir o pai raiz.
A função a seguir calcula o tamanho da pasta e todas as suas subpastas.
fonte
Se você estiver no sistema operacional Windows, pode:
instale o módulo pywin32 iniciando:
pip install pywin32
e depois codificando o seguinte:
fonte
Aqui está um liner que faz isso recursivamente (opção recursiva disponível no Python 3.5):
fonte
para python3.5 +
fonte
Esse script informa qual arquivo é o maior no CWD e também indica em qual pasta o arquivo está. Este script funciona para mim no shell win8 e python 3.3.3
fonte
É certo que isso é meio hackeado e só funciona no Unix / Linux.
Corresponde
du -sb .
porque, na verdade, é um wrapper do Python bash que executa odu -sb .
comando.fonte
Estou um pouco atrasado (e novo) aqui, mas optei por usar o módulo de subprocesso e a linha de comando 'du' com o Linux para recuperar um valor preciso para o tamanho da pasta em MB. Eu tive que usar if e elif para a pasta raiz porque, caso contrário, o subprocesso gera um erro devido ao valor diferente de zero retornado.
fonte
Obter tamanho do diretório
Propriedades da solução:
du
quest.st_blocks
para o espaço em disco usado, portanto, funciona apenas em sistemas do tipo UnixO código:
Exemplo de uso:
Tamanho do arquivo legível por humanos
Propriedades da solução:
O código:
Exemplo de uso:
fonte
Uma solução que funciona no Python 3.6 usando pathlib.
fonte
Tamanho do arquivo / pasta recursiva do Python 3.6+ usando
os.scandir
. Tão poderoso quanto na resposta de @blakev, mas mais curto e no estilo python do EAFP .fonte
Eu escrevi esta função que me fornece o tamanho total exato de um diretório, tentei outro para soluções de loop com os.walk, mas não sei por que o resultado final sempre foi menor que o tamanho real (no ubuntu 18 env). Eu devo ter feito algo errado, mas quem se importa escreveu este funciona perfeitamente bem.
fonte