Verificando a existência de arquivos em uma lista

2

Eu tenho um arquivo de texto "command" que emite um comando de download de arquivo de dados em cada     linha. Eu envio o arquivo de comando para o bash. No entanto, uma pequena porcentagem dos downloads falha. Aqui está o algoritmo que uso para descobrir o que está faltando:

  1. Após o download, volto ao arquivo de comando e verifico se cada arquivo de download existe.
  2. Se o download não existir, copio a linha de comando em um novo arquivo de comando.
  3. Fico com um novo arquivo de comando para os downloads restantes.

Aqui está o script bash que eu implementei o algoritmo com:

  1 #!/bin/bash
  2 while read line
  3 do
  4         for item in $line
  5         do
  6                 if [[ $item == *out_fname* ]]; then 
  7                         splitline=(${item//=/ })   
  8                         target_file=${splitline[1]}
  9                         if [ ! -f $target_file ]; then
 10                                 echo $line >> stillneed.txt
 11                         fi
 12                 fi
 13         done
 14 done < "$@"

Pergunta: Isso funciona bem, mas existe um algoritmo ou implementação melhor (talvez usando algo diferente de bash)? O que eu fiz foi apenas fazer o que um humano teria que fazer. Mas parece que o Unix sempre tem uma maneira melhor de fazer as coisas ...

    
por Pete 30.12.2011 / 05:03

4 respostas

1

Parece que você está procurando por 'out_fname =', não apenas 'out_fname'.

Eu faria isso em uma mistura de awk e shell ou em python. No awk / shell:

awk '{for(i=0;i<NF;i++) {if (index($i, 'out_fname=')) {split($i,A,/=/);print A[i]}}' "$@" |
    while read filename; do
        if [ ! -f $filename ]; then echo $filename; fi
    done > stillneed.txt

Em python:

import fileinput, os
stillneed = open("stillneed.txt", "w")
for line in fileinput.input():
    for filename in [l.split('=')[1] for l in line.split() if l.find('out_fname=')!=-1]:
        if not os.path.exists(filename):
            print >>stillneed, filename
    
por 30.12.2011 / 05:31
1

Não tenho certeza se isso vai ajudar, mas tenho uma função para repetir comandos até que eles retornem um sucesso:

retry () {
    local delay=1 n

    if ! [[ $1 = *[^0-9]* ]]; then
        # TODO allow delay=0 (prevents Ctrl-C)
        if (($1 > 0)); then
            delay=${1:1}
        fi
        shift
    fi

    # run command
    while ! "$@"; do
        echo "retrying in ${delay}s"
        for ((n=delay; n>0; n--)); do
            sleep 1 || return
        done
    done
}; export -f retry
    
por 30.12.2011 / 05:20
1

Em vez de verificar o que está faltando após a conclusão do script de download inicial, considere adicionar algumas verificações ao script de download mencionado. Eu não testei o seguinte, acabei de escrevê-lo no topo da minha cabeça:

cat files_to_download|while read file; 
do
    SUCCESS="False"
    while [[ $SUCCESS == "False" ]];
    do
        wget $file;
        if [[ $? -eq 0 ]];
        then
            SUCCESS="True"
        fi
   done
done
    
por 30.12.2011 / 12:43
1

Eu recomendaria echo ing uma cópia da linha quando o download falha, em vez de revisar os nomes dos arquivos analisando cada linha no arquivo depois:

[[ -f $1 ]] || { echo "$1 not found" >&2; exit 1; }
while read -r line; do
    $line || echo "$line" >> stillneed
done < "$1"

Isso seria mais eficiente e também significa que você não precisa se preocupar com nomes estranhos de arquivos no futuro (por exemplo, com espaços dentro).

Se você quiser melhorar seu método existente, poderá usar a expansão de parâmetro padrão:

for f; do
    while read -r line; do
        for item in $line; do
            [[ $item = out_fname=* ]] || continue
            [[ -f ${item#out_fname=} ]] || echo "$line"
            break # assuming one fname per line
        done
    done < "$f"
done > stillneed

.. mas considere o que acontece com: out_fname='foo bar.ext' . Além disso, lembre-se de que isso verifica cada linha após o evento, quando poderíamos ter verificado se o comando funcionava no momento em que o executamos.

Abrir stillneed uma vez para todo o loop é mais eficiente; Eu não fiz isso no primeiro snippet, já que provavelmente gostaríamos de ver a saída dos comandos de download. Aqui há apenas testes, nenhum comando externo é executado, portanto, faz sentido abrir o arquivo uma vez. (Observação usando > truncará o arquivo no início; usei for f para permitir mais de um arquivo de entrada como parâmetros posicionais: adicionar o mesmo acima deve ser fácil se você precisar dele.)

A única coisa que devo enfatizar é a citação: echo "$line" é muito diferente de echo $line . Em geral, cite expansões de parâmetros all (isso inclui variáveis), a menos que você tenha certeza de que deseja que a divisão de campos ocorra.

    
por 10.01.2012 / 10:09

Tags