다른 Linux 서버에서 원격으로 파일 해시 가져오기를 시도합니다.

다른 Linux 서버에서 원격으로 파일 해시 가져오기를 시도합니다.
while IFS= read -r line
    do
      LOCATION=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $1 }')
      USER=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $2 }')
      MD5=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $3 }')
      FILE=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $4 }')
      CHECK=$(md5sum "$FILE" | awk '{ print $1 }')
      FILENAME="${FILE##*/}"
      echo "$FILENAME"
      REMOTECHECK=$(ssh server md5sum filelocation/"${FILENAME}" < /dev/null | awk '{ print $1 }')
    
      if [[ "$CHECK" == "$MD5" ]]; then
    
        echo "Local File MD5: "
        echo "$CHECK"
        echo "Remote File MD5: "
        echo "$REMOTECHECK"
   fi
done < _path to file_

공백이 없는 파일 이름에는 스크립트가 제대로 작동하지만 파일 이름에 공백이 있으면 문제가 발생합니다.

파일명에 공백이 있을 때 출력됩니다.

md5sum: path_to_file/File: No such file or directory
md5sum: Name: No such file or directory
md5sum: With: No such file or directory
md5sum: Spaces.mp4: No such file or directory

내가 아는 한, 문제는 이 코드 줄에 있습니다.

      REMOTECHECK=$(ssh server md5sum filelocation/"${FILENAME}" < /dev/null | awk '{ print $1 }')

위 스크립트는 공백이 없는 파일 이름에 대해 작동하며, 문제는 공백이 있는 파일 이름에서만 발생합니다.

어떤 조언이라도 제공해 주시면 매우 도움이 될 것입니다.

답변1

ssh실행되지 않음주문하다원격 호스트에서는 원격 사용자의 로그인 쉘이 해석할 수 있도록 코드를 보냅니다. 따라서 원격 쉘이 주어진 인수 목록을 사용하여 주어진 명령을 실행하도록 하려면 다음을 수행해야 합니다.저것쉘 구문은 쉘이 이러한 매개변수를 사용하여 명령을 실행하도록 합니다.

쉘은 명령줄 해석기입니다. 주요 목적은 명령줄에서 주어진 명령을 실행하는 것입니다(명령줄은 다른 말로 하면쉘 구문의 코드) 당신이 그것을 제공합니다. 값 $FILENAME이 인 Korn과 같은 쉘에서 File Name With Spaces.mp4명령줄은 다음과 같습니다.

 ssh server md5sum filelocation/"${FILENAME}"

셸의 작업은 다음 매개변수를 사용하여 $PATH이름이 지정된 파일을 실행하는 것입니다.ssh/usr/bin/ssh

  • argv[0]:ssh
  • argv[1]:server
  • argv[2]:md5sum
  • argv[3]:filelocation/File Name With Spaces.mp4

쉘 언어 구문에서 공백은 명령 매개변수를 구분하고 $xxx매개변수 확장을 트리거하며 여기서 따옴표는 이 확장 중에 분할+글로브를 방지하기 위해 사용됩니다.

그런 다음 작업은 ssh받은 인수 목록에서 연결 server하고 나머지 인수를 공백으로 연결한 다음 결과를 원격 사용자의 로그인 셸에 전달하는 것입니다(그들은 변경된 기본 쉘을 사용할 수 있지만 chsh, , , ... ) 매개변수를 사용하여 실행합니다.zshtcshfishyashbashrc

  • argv[0]: 해당 쉘의 이름
  • argv[1]:-c
  • argv[2]:결과, 여기요:md5sum filelocation/File Name With Spaces.mp4

여기에서는 모든 쉘의 구문이 다르지만 명령줄은 대부분의 사람들이 동일하게 해석할 만큼 간단합니다. 즉, /path/to/md5sum다음 매개변수를 사용하여 명령을 실행합니다.

  • argv[0]:md5sum
  • argv[1]:filelocation/File
  • argv[2]:Name
  • argv[3]:With
  • argv[4]:Spaces.mp4

md5sum하나의 인수로 실행되는 명령 의 경우 filelocation/File Name With Spaces.mp4이러한 공백이 인수 구분 기호로 간주되지 않음을 원격 셸에 알려야 합니다. 이는 인용/이스케이프를 통해 수행됩니다. 그리고 인용 구문은 쉘마다 크게 다릅니다.

어쨌든, 공백만이 문제를 일으킬 수 있는 유일한 문자는 아닙니다. 원격 셸 구문의 특수 문자도 문제가 됩니다. 예를 들어 파일 이름이 $(reboot).mp4또는 이면 blah;rm -rf ~;blah.mp4더 큰 문제가 있습니다.

원격 셸이 Bourne과 유사하다는 것을 알고 있으면 다음을 수행할 수 있습니다.

#! /bin/zsh -
while IFS=, read -ru3 location user md5 file rest; do
  md5sum -- $file | read check rest
  filename=$file:t
  print -r -- $filename
  ssh -n server "md5sum filelocation/${(qq)filename}" | read remotecheck rest
  if [[ $md5 = $check ]]; then
    printf '%s File MD5: %s\n' Local "$check" Remote "$remotecheck"
  fi
done 3< $path_to_file

${(qq)file}다음으로 견적작은 따옴표는 Bourne과 같은 셸에서 내용을 인용하는 가장 안전한 방법입니다.. 따라서 귀하의 경우에는 File Name With Spaces.mp4로 전달됩니다 'File Name With Spaces.mp4'. 그렇다면 File Name With Quote's.mp4인용된 자체를 제외하고 'File Name With Quote'\''s.mp4'모든 것이 인용됩니다 .'...''\

Bourne과 유사한 원격 셸이 보장되지 않으면 다음을 참조하세요.원격 사용자의 로그인 셸을 모르고 SSH를 통해 임의의 간단한 명령을 어떻게 실행할 수 있습니까?더 많은 선택을 위해.

md5sum여기에서 특정 사용 사례의 경우 로컬 및 원격 체크섬을 비교하기 위해 또 다른 옵션은 확인 모드( 와 함께 )를 사용하는 것입니다 -c.

#! /bin/zsh -
while IFS=, read -ru3 location user md5 file rest; do
  (cd -P -- $file:h && md5sum -- $file:t) |
    ssh -n server 'cd ./filelocation && md5sum -c'
done 3< $path_to_file

이번에는 파일 이름이 로컬로 작성되고 md5sum원격 파일의 표준 입력에서 읽혀지므로 원격 쉘에서 이를 참조할 필요가 없습니다. 이 명령줄은 대부분의 쉘에서 이해됩니다 cd ./filelocation && md5sum -c(접두사는 ./비대화식으로 또는 ssh를 통해 호출될 때 rc 파일을 읽거나 읽을 수 있는 csh/tcsh/bash에서 /의 효과를 피하기 위한 것입니다).$cdpath$CDPATH

답변2

문제는 이 줄과 두 쉘이 이를 해석하는 방식에 있습니다.

REMOTECHECK=$(ssh server md5sum filelocation/"${FILENAME}" < /dev/null | awk '{ print $1 }')

파일 이름이 "happy Monday"라고 가정하고 ssh명령을 구체적으로 살펴 보겠습니다.

변수 값을 평가한 후 로컬 셸에서 다음을 확인합니다.

ssh server md5sum 'filelocation/happy monday' < /dev/null

특히, 따옴표가 제거되고 쉘은 내용을 단일 단어로 처리합니다 filelocation/happy monday.

이제 결과는 ssh원격 셸(무엇이든)에 전달된 명령줄 인수에 의해 실행됩니다. 따옴표가 제거되었으므로 다음은 원격으로 실행된다는 점을 기억하세요.

md5sum 파일 위치/해피 먼데이

현재 md5sum두 개의 파일을 찾고 filelocation/happy있습니다 monday.

따옴표 누락을 방지하려면 전체 명령을 다른 세트로 묶어야 합니다.

ssh server "md5sum 'filelocation/happy monday'"

이것을 원래 코드에 다시 적용하십시오.

REMOTECHECK=$(ssh -n server "md5sum 'filelocation/$FILENAME'" | awk '{ print $1 }')

답변3

가장 간단한 접근 방식은 원격 시스템이 다음과 같이 POSIX sh 구문을 지원하는 셸을 사용하고 있다는 것을 알고 있다고 가정하는 것입니다.

#!/bin/sh

while IFS= read -r line
do
    LOCATION=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $1 }')
    USER=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $2 }')
    HASH=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $3 }')
    FILE=$(echo "$line" | awk 'BEGIN { FS = "," } ; { print $4 }')
    CHECK=$(b2sum "$FILE" | awk '{ print $1 }')
    FILENAME="${FILE##*/}"
    REMOTECHECK=$(printf '%s\0' "$FILE" |
             ssh castro xargs -0 -I{} b2sum "remotefile/{}" |
             cut -d' ' -f1)
    echo "Local File hash: "
    echo "$CHECK"
    echo "Remote File hash: "
    echo "$REMOTECHECK"
done

참고할 몇 가지 사항이 있습니다.

먼저 xargs -I원격 시스템에서 단일 특정 경로 이름을 지정하는 데 사용합니다. 이는 원격 시스템이 경로 이름을 원격 측에 전달하고 올바르게 인용되도록 하는 가장 쉬운 방법입니다. 파일 이름에 따옴표가 포함된 경우 이를 인용하려고 하면 흥미로운 예외 사례가 발생할 수 있습니다. git rev-parse --sq-quote로컬에서 Git을 사용할 수 있는 경우 이러한 방법을 사용하여 이러한 문제를 해결할 수 있지만 이 방법이 더 간단하고 강력합니다. 우리의 xargs사용법은 엄격하게 이식 가능하지는 않지만(일부 시스템에서는 {}별도의 매개 변수가 필요하기 때문에) Linux와 대부분의 다른 일반적인 시스템에서는 이 동작을 구현합니다.

둘째, 원격 시스템에서 수행해야 하는 처리량을 최소화하여 이에 대해 너무 많은 가정을 할 필요가 없습니다. 보장할 수는 없지만 원격 측에서 POSIX sh를 사용하지 않는 경우에도 이 구문이 작동할 수 있습니다. 물론 추가 출력을 제공하지 않는 원격 측에 의존하지만 이는 사실상 불가피합니다.

셋째, 우리는 MD5를 사용하지 않습니다. CERT CC를 인용하자면: "소프트웨어 개발자는...MD5 알고리즘을 어떤 방식으로든 사용하지 않아야 합니다. 내 시스템에 MD5와 충돌하는 파일이 있기 때문에 빠른 검사로는 적합하지 않습니다." 여기서는 MD5보다 더 안전하고 빠른 BLAKE2b를 사용했거나 b2sumSHA-256을 사용할 수 있습니다( sha256sum또는 사용할 수 없는 경우).shasum -a 256

관련 정보