Pergunta

Estou tentando automatizar o download de alguns arquivos de texto do AZ/OS PDS, usando o Python e o FTPLIB.

Como os arquivos do host são EBCDIC, não posso simplesmente usar ftp.retrbinary ().

Ftp.retrlines (), quando usado com o Open (arquivo, w) .WriteLines como seu retorno de chamada, é claro que não fornece EOLs.

Então, para iniciantes, eu criei esse código que "parece bom para mim", mas como sou um python Noob, alguém pode sugerir uma abordagem melhor? Obviamente, para manter essa pergunta simples, essa não é a coisa final, sinos e shistles.

Muito Obrigado.

#!python.exe
from ftplib import FTP

class xfile (file):
    def writelineswitheol(self, sequence):
        for s in sequence:
            self.write(s+"\r\n")

sess = FTP("zos.server.to.be", "myid", "mypassword")
sess.sendcmd("site sbd=(IBM-1047,ISO8859-1)")
sess.cwd("'FOO.BAR.PDS'")
a = sess.nlst("RTB*")
for i in a:
    sess.retrlines("RETR "+i, xfile(i, 'w').writelineswitheol)
sess.quit()

ATUALIZAÇÃO: Python 3.0, a plataforma é Mingw no Windows XP.

O Z/OS PDSS possui uma estrutura de registro fixo, em vez de depender de terminações on -line como separadores de registros. No entanto, o servidor Z/OS FTP, ao transmitir no modo de texto, fornece as terminações de registro, que thurlines () desligam.

Atualização de fechamento:

Aqui está minha solução revisada, que será a base para o desenvolvimento contínuo (removendo senhas internas, por exemplo):

import ftplib
import os
from sys import exc_info

sess = ftplib.FTP("undisclosed.server.com", "userid", "password")
sess.sendcmd("site sbd=(IBM-1047,ISO8859-1)")
for dir in ["ASM", "ASML", "ASMM", "C", "CPP", "DLLA", "DLLC", "DLMC", "GEN", "HDR", "MAC"]:
    sess.cwd("'ZLTALM.PREP.%s'" % dir)
    try:
        filelist = sess.nlst()
    except ftplib.error_perm as x:
        if (x.args[0][:3] != '550'):
            raise
    else:
        try:
            os.mkdir(dir)
        except:
            continue
        for hostfile in filelist:
            lines = []
            sess.retrlines("RETR "+hostfile, lines.append)
            pcfile = open("%s/%s"% (dir,hostfile), 'w')
            for line in lines:
                pcfile.write(line+"\n")
            pcfile.close()
        print ("Done: " + dir)
sess.quit()

Meus agradecimentos a John e Vinay

Foi útil?

Solução

Acabei de encontrar essa pergunta enquanto eu estava tentando descobrir como baixar recursivamente conjuntos de dados do z/OS. Eu uso um script python simples há anos para baixar arquivos EBCDIC do mainframe. Ele efetivamente faz isso:

def writeline(line):
    file.write(line + "\n")

file = open(filename, "w")
ftp.retrlines("retr " + filename, writeline)

Outras dicas

Você deve ser capaz de baixar o arquivo como um binário (usando retrbinary) e use o codecs Módulo para converter do EBCDIC para qualquer codificação de saída que desejar. Você deve conhecer a página de código EBCDIC específica que está sendo usada no sistema z/OS (por exemplo, CP500). Se os arquivos forem pequenos, você pode até fazer algo como (para uma conversão para o UTF-8):

file = open(ebcdic_filename, "rb")
data = file.read()
converted = data.decode("cp500").encode("utf8")
file = open(utf8_filename, "wb")
file.write(converted)
file.close()

Atualizar: Se você precisar usar retrlines Para obter as linhas e suas linhas estão voltando na codificação correta, sua abordagem não funcionará, porque o retorno de chamada é chamado uma vez para cada linha. Então, no retorno de chamada, sequence será a linha, e o seu loop para escrever caracteres individuais na linha para a saída, cada um em sua própria linha. Então você provavelmente quer fazer self.write(sequence + "\r\n") ao invés de for ciclo. Ainda não parece especialmente certo para a subclasse file Apenas para adicionar esse método de utilidade - provavelmente precisa estar em uma classe diferente em seu bells-and-whistles versão.

Seu método WriteLineswitheol aplende ' r n' em vez de ' n' e, em seguida, grava o resultado em um arquivo aberto no modo de texto. O efeito, independentemente da plataforma em que você está executando, será um ' r' indesejado. Basta anexar ' n' e você terá o final da linha apropriado.

O manuseio de erro adequado não deve ser relegado a uma versão "sinos e assobios". Você deve configurar seu retorno de chamada para que seu arquivo open () esteja em uma tentativa/exceto e mantenha uma referência ao identificador de arquivo de saída, sua chamada de gravação está em uma tentativa/exceto e você tem um método de callback_obj.close () que que Você usa quando o retorna do retrato () (em uma tentativa/exceto) - para que você obtém erros de manuseio de erros por exemplo, "não pode (abrir | gravar para | fechar o arquivo x porque y" e Você economiza que pensar quando seus arquivos serão fechados implicitamente e se você corre o risco de ficar sem alças de arquivo.

Python 3.x ftplib.ftp.retrlines () deve fornecer objetos STR que são de fato strings unicode, e você precisará codificá -los antes de escrevê -los - a menos que a codificação padrão seja latin1, o que seria bastante incomum para uma janela caixa. Você deve ter arquivos de teste com (1) todos os 256 bytes possíveis (2) todos os bytes que são válidos na página de código EBCDIC esperada.

Algumas observações de "saneamento"

  1. Você deve considerar a atualização do seu Python de 3.0 (uma versão "Prova de conceito") para 3.1.

  2. Para facilitar uma melhor compreensão do seu código, use "i" como identificador apenas como um índice de sequência e somente se você adquiriu irrespecutamente o hábito da Fortran 3 ou mais décadas atrás :-)

  3. Dois dos problemas descobertos até agora (anexando o terminador de linha a cada caractere, terminador de linha errado) teria aparecido na primeira vez que você o testou.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top