여러 다른 파일에서 복사할 파일을 읽어 병렬로 파일을 복사합니다.

여러 다른 파일에서 복사할 파일을 읽어 병렬로 파일을 복사합니다.

나는 처음으로 다소 복잡한 쉘 스크립트를 작성하고 있는데, 그 스크립트가 수행해야 할 작업은 다음과 같습니다.

  • clientid시작하는 동안 파일을 보고 내가 가진 것이 무엇인지 알아냈습니다 host-mapping.txt. 내 호스트 이름을 찾을 수 없으면 clientid0이 아닌 상태 코드로 쉘 스크립트를 종료하고 오류 메시지를 기록해야 합니다.
  • 이제 유효한 파일이 있으면 파일 에서 추출 하고 파일 에서 유효한 파일을 추출 clientid하겠습니다 . 어떤 이유로든 파일의 기본 또는 보조 파일을 찾을 수 없으면 쉘 스크립트를 종료하고 오류 메시지를 기록합니다.primary filesprimary-mappings.txtsecondary filessecondary-mappings.txtclientidclientid
  • 이제 유효한 기본 및 보조 파일이 있으면 clientidfrom을 사용하여 이러한 파일을 병렬로 복사하기 시작합니다. 모든 기본 파일은 폴더로 이동하고 모든 보조 파일은 폴더로 이동합니다. 파일이 원격 서버의 폴더에 없으면 해당 폴더에 있어야 합니다.gnu-parallellocal_serverprimarysecondaryhold1hold2
  • 이제 모든 파일이 복사되면 마지막에 모든 기본 파일과 보조 파일이 이 두 폴더에 있는지 확인하겠습니다. clientid그러나 어떤 이유로 파일을 찾을 수 없으면 메시지와 함께 쉘 스크립트를 종료하고 싶습니다. 어떤 파일이 누락되었는지 알려줍니다.

아래는 내 스크립트입니다. 이 스크립트는 해당 작업을 수행하지만 위의 작업을 수행하는 더 좋고 효율적인 방법이 있는지 확인하고 싶었습니다. 작고 복잡한 스크립트를 작성하는 것은 이번이 처음이므로 확인해 보고 싶었습니다. 지금까지는 스크립트 primary나 파일을 찾을 수 없는 경우 secondary셸 스크립트를 종료할 수 있는 메커니즘이 없으며 clientid, 확인 단계에서 일부 파일이 누락된 경우 셸 스크립트를 종료할 수 있는 메커니즘도 없습니다.

#!/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}=\[([^]\]+).*/\1/; 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."

아래는 내 host-mapping.txt파일입니다. 그 안에 더 많은 항목이 있을 것입니다. 여기의 값은 유효한 호스트 이름이고 키는 문자열 "k"와 그 뒤에 매핑 파일에 있어야 하는 숫자가 됩니다.

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

다음은 샘플 매핑 파일입니다.

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]}

보조_매핑.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]}

예: 1 기본 파일과 보조 파일이 clientid있습니다 . 다른 모든 사람에게도 마찬가지 입니다.343, 0, 686, 1372, 882, 1961152, 816, 1488, 336, 1008clientids

답변1

몇 가지 제안 사항이 있어서 답변을 작성해야겠다고 생각했습니다.

  1. primary_mappings.txt및 에 관한 불일치가 있으며 스크립트에서는 및 secondary_mappings.txt이라고 합니다 primary-mappings.txt . secondary-mappings.txt물론 이 파일의 이름을 바꿔야 합니다(또는 스크립트 이름을 변경해야 합니다).

  2. STDERR에 대한 로깅과 프로그램 종료를 처리하는 두 가지 함수를 만들겠습니다. 이는 더 읽기 쉽고 오류가 덜 발생한다는 장점이 있으며 함수 호출을 종료할 수 있게 해줍니다.쉘 스크립트를 종료할 수 있는 메커니즘이 없습니다.):

    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. STDERR에 오류를 기록하고 싶은 것 같지만 때로는 STDOUT에 기록하는 경우도 있습니다. 방금 뭔가를 잊어버렸다고 가정하면 >&2새로 생성된 함수를 사용하여 이를 쉽게 정규화할 수 있습니다.

    ㅏ.

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

    이 되다:

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

    비.

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

    이 되다:

    *) log_error_and_exit "Unknown host: $(hostname -f), exiting." ;;
    
  4. 이 작업을 잊어버린 것 같습니다.

    어떤 이유로든 해당 클라이언트 ID에 대한 기본 또는 보조 파일을 찾을 수 없으면 쉘 스크립트를 종료하고 오류 메시지를 기록합니다.

    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}=\[([^]\]+).*/\1/; s/,/ /g" $file))
      echo "${arr[@]}"
    }
    
  5. 현재는 다음 작업을 수행하지 않습니다.

    어떤 이유로 파일을 찾을 수 없으면 어떤 파일이 누락되었는지 알려주는 메시지와 함께 쉘 스크립트를 종료하고 싶습니다.

    exit파일이 누락된 것으로 발견되면 여러 파일에 대한 보고서를 받을 수 없기 때문입니다 ! 해결 방법은 오류 상태를 추적하고 유효성 검사가 끝날 때 확인하는 변수를 만드는 것입니다.

    # 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
    

관련 정보