別の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

md5sum1つの引数で実行されるコマンドでは、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ここで特定のユースケースでは、ローカルとリモートのチェックサムを比較するためのもう1つのオプションは、確認モード(と一緒に)を使用することです-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

問題は、この行と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ファイルの場所/ハッピーマンデー

現在md5sum2つのファイルを探して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

関連情報