copia arquivos em paralelo lendo quais arquivos copiar de alguns outros arquivos

3

Estou trabalhando em um script de shell um pouco complicado pela primeira vez e abaixo está o que ele deve fazer:

  • Durante a inicialização, descobre qual é minha clientid , observando o arquivo host-mapping.txt . Se não conseguir encontrar clientid para o meu nome de host, preciso sair do script de shell com código de status diferente de zero e registrar a mensagem de erro.
  • Agora, depois que eu tiver o clientid válido, extrairei primary files do arquivo primary-mappings.txt e secondary files do arquivo secondary-mappings.txt para esse clientid válido. Se, por qualquer motivo, não conseguir encontrar arquivos primários ou secundários para esse clientid desse arquivo, então sairei do shell script e registrarei uma mensagem de erro.
  • Agora, quando eu tiver arquivos primários e secundários válidos para esse clientid , então, vou começar a copiar esses arquivos em paralelo usando gnu-parallel de local_server . Todos os arquivos primários irão para a pasta primary e todos os arquivos secundários irão para a pasta secondary . Se os arquivos não estiverem na pasta hold1 nos servidores remotos, eles deverão estar lá na pasta hold2 .
  • Agora, assim que todos os arquivos forem copiados, verificarei no final se todos os arquivos primários e secundários estão presentes para o clientid nessas duas pastas, mas se por algum motivo eu não encontrar esses arquivos, então eu deseja sair do shell script com uma mensagem que informa quais arquivos estão faltando.

Abaixo está meu script e ele faz o trabalho, mas eu gostaria de ver se há alguma maneira melhor ou mais eficiente de fazer as coisas acima, já que esta é a primeira vez que escrevo um script pouco complicado, então queria verificar isso. A partir de agora, não tenho mecanismo para sair do shell script, se não conseguir encontrar primary ou secondary arquivos para esse clientid e também não tenho mecanismo para sair do shell script se durante o fase de verificação, alguns arquivos estão faltando.

#!/bin/bash
path=/home/goldy/scripts
mapfiles=(primary-mappings.txt secondary-mappings.txt)
hostfile=host-mapping.txt
machines=(machine1769.abc.host.com proctek5461.def.host.com letyrs87541.pqr.host.com)
# folders on local box where to copy files
primary=/data01/primary
secondary=/data02/secondary
# folders on remote servers from where to copy files
export hold1=/data/snapshot/$1
export hold2=/data/snapshot/$2

date1=$(date +"%s")
# this will tell me what's my clientid given my current hostname
getProperty () {
   prop_value=$(hostname -f)
   prop_key='cat $path/$hostfile | grep "$prop_value" | cut -d'=' -f1'
   echo $(echo $prop_key | tr -dc '0-9')
}
# if I can't find clientid for my hostname, then I will log a message 
# and exit out of shell script with non zero status code
clientid=$(getProperty)
[ -z "$clientid" ] && { echo "cannot find clientid for $(hostname -f)"; exit 1; }

# now once I have valid clientid, then I will get primary and secondary mapping
# from the "host-mapping.txt" file
declare -a arr
mappingsByClientID () {
  id=$1 # 1 to 5
  file=$path/${mapfiles[$2]} # 0 to 1
  arr=($(sed -r "s/.*\b${id}=\[([^]\]+).*//; s/,/ /g" $file))
  echo "${arr[@]}"
}

# assign output of function to an array
pri=($(mappingsByClientID $clientid 0))
snd=($(mappingsByClientID $clientid 1))

echo "primary files: ${pri[@]}"
echo "secondary files: ${snd[@]}"

# figure out which machine you want to use to start copying files from
case $(hostname -f) in
    *abc.host.com)
        local_server=("${machines[0]}")
        ;;
    *def.host.com)
        local_server=("${machines[1]}")
        ;;
    *pqr.host.com)
        local_server=("${machines[2]}")
        ;;
    *) echo "unknown host: $(hostname -f), exiting." && exit 1 ;;
    # ?
esac
export local="$local_server"

# deleting files before we start copying
find "$primary" -maxdepth 1 -type f -exec rm -fv {} \;
find "$secondary" -maxdepth 1 -type f -exec rm -fv {} \;

do_copy() {
  el=$1
  primsec=$2
  (scp -C -o StrictHostKeyChecking=no goldy@"$local":"$hold1"/hello_monthly_"$el"_999_1.data "$primsec"/. > /dev/null 2>&1) || (scp -C -o StrictHostKeyChecking=no goldy@"$local":"$hold2"/hello_monthly_"$el"_999_1.data "$primsec"/. > /dev/null 2>&1)
}
export -f do_copy
# copy files in parallel
parallel -j "$3" do_copy {} $primary ::: ${pri[@]} &
parallel -j "$3" do_copy {} $secondary ::: ${snd[@]} &
wait
echo "all files copied"

# this is for verification to see all files got copied or not
# in primary and secondary folder
set -- "$primary" "$secondary"
typeset -n array
for array in pri snd; do
    for num in "${array[@]}"; do
        name="hello_monthly_${num}_999_1.data"
        if [ ! -f "$1/$name" ]; then
            { echo "$name" not found in "$1" >&2 && exit 1; }
        fi
    done
    shift
done

date2=$(date +"%s")
diff=$(($date2-$date1))
echo "Total Time Taken - $(($diff / 3600)) hours and $(((diff/60) % 60)) minutes and $(($diff % 60)) seconds elapsed."

Abaixo está o meu arquivo host-mapping.txt e ele terá muito mais entradas. Aqui valor é um nome de host válido e a chave será string "k" seguida por algum número e esse número deve estar lá nos arquivos de mapeamento.

k1=machineA.abc.com
k2=machineB.abc.com
k3=machineC.def.com
k4=machineD.pqr.com
k5=machineO.abc.com

E abaixo estão meus arquivos de mapeamento de amostra:

primary_mappings.txt

{1=[343, 0, 686, 1372, 882, 196], 2=[687, 1, 1373, 883, 197, 736, 1030, 1569], 3=[1374, 2, 884, 737, 198, 1570], 4=[1375, 1032, 1424, 3, 885, 1228], 5=[1033, 1425, 4, 200, 886]}

secondary_mappings.txt

{1=[1152, 816, 1488, 336, 1008], 2=[1153, 0, 817, 337, 1489, 1009, 1297], 3=[1, 1154, 1490, 338], 4=[1155, 2, 339, 1491, 819, 1299, 1635], 5=[820, 1492, 340, 3, 1156]}

Por exemplo: clientid 1 tem 343, 0, 686, 1372, 882, 196 arquivos primários e 1152, 816, 1488, 336, 1008 arquivos secundários. Similarmente para outros clientids também.

    
por user1950349 17.04.2018 / 22:49

1 resposta

3

Eu tenho algumas sugestões, então pensei em escrever uma resposta.

  1. Existe uma inconsistência em relação a primary_mappings.txt e secondary_mappings.txt , que são chamados de primary-mappings.txt e secondary-mappings.txt no script. Você deveria, claro renomeie os arquivos (ou altere o nome deles para o script).

  2. Eu criaria duas funções para manipular o log para STDERR e sair do programa. Isto tem a vantagem de uma melhor legibilidade, é menos propenso a erros e permite que você saia de dentro das chamadas de função (ele responde Eu não tenho mecanismo para sair do shell script ):

    trap "exit 1" TERM
    export TOP_PID=$$
    log_error () {
       echo "$1">&2;
    }
    log_error_and_exit () {
       echo "$1">&2;
       kill -s TERM $TOP_PID
    }
    
  3. Você parece querer registrar erros no STDERR, mas às vezes você escreve no STDOUT. Supondo que você simplesmente esqueceu um par de >&2 , é fácil padronizar isso usando nossas funções recém-criadas:

    a.

    [ -z "$clientid" ] && { echo "cannot find clientid for $(hostname -f)"; exit 1; }
    

    torna-se:

    [ -z "$clientid" ] && { log_error_and_exit "Cannot find ClientID for $(hostname -f)"; }
    

    b.

    *) echo "unknown host: $(hostname -f), exiting." && exit 1 ;;
    

    torna-se:

    *) log_error_and_exit "Unknown host: $(hostname -f), exiting." ;;
    
  4. Parece que você esqueceu de fazer isso:

    If for whatever reason, I cannot find either primary or secondary files for that clientid from that file, then I will exit from shell script and log an error message.

    mappingsByClientID () {
      id=$1 # 1 to 5
      file=$path/${mapfiles[$2]} # 0 to 1
      if [[ $(< $file) != *" $1="* ]]; then
          log_error_and_exit "ClientID $1 out of range for $file";
      fi
      arr=($(sed -r "s/.*\b${id}=\[([^]\]+).*//; s/,/ /g" $file))
      echo "${arr[@]}"
    }
    
  5. Você atualmente não faz isso:

    if for whatever reason, I cannot find those files then I want to exit from the shell script with message that tells me what files are missing.

    Como exit , assim que um arquivo for encontrado ausente, você nunca receberá mais de um arquivo informado. A solução é criar uma variável para acompanhar um status de erro e verificar no final da verificação:

    # this is for verification to see all files got copied or not
    # in primary and secondary folder
    set -- "$primary" "$secondary"
    typeset -n array
    errors=false
    for array in pri snd; do
        for num in "${array[@]}"; do
            name="hello_monthly_${num}_999_1.data"
            if [ ! -f "$1/$name" ]; then
                {  log_error "$name not found in $1" && errors=true; }
            fi
        done
        shift
    done
    if [ "$errors" = true ]; then
        exit 1
    fi
    
por 22.04.2018 / 05:29