120 votes

ssh s'affranchit de la boucle while dans bash

J'utilise ce code bash pour télécharger des fichiers vers un serveur distant, pour des fichiers normaux cela fonctionne bien :

for i in `find devel/ -newer $UPLOAD_FILE`
do
    echo "Upload:" $i
    if [ -d $i ]
    then
        echo "Creating directory" $i
        ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i"
        continue
    fi
    if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i
    then
        echo "$i OK"
    else
        echo "$i NOK"
        rm ${UPLOAD_FILE}_tmp
    fi
done

Le seul problème est que pour les fichiers dont le nom contient un espace, la boucle for échoue, j'ai donc remplacé la première ligne par celle-ci :

find devel/ -newer $UPLOAD_FILE | while read i
do
    echo "Upload:" $i
    if [ -d $i ]
    then
        echo "Creating directory" $i
        ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i"
        continue
    fi
    if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i
    then
        echo "$i OK"
    else
        echo "$i NOK"
        rm ${UPLOAD_FILE}_tmp
    fi
done

Pour une raison étrange, la commande ssh sort de la boucle while, ce qui fait que le premier répertoire manquant est créé sans problème, mais que tous les fichiers/répertoires manquants suivants sont ignorés.

Je suppose que cela a quelque chose à voir avec le fait que ssh écrit quelque chose sur stdout, ce qui perturbe la commande "read". En commentant la commande ssh, la boucle fonctionne comme elle le devrait.

Quelqu'un sait-il pourquoi cela se produit et comment on peut empêcher ssh de casser la boucle while ?

290voto

choroba Points 56333

Le problème est que ssh lit à partir de l'entrée standard, il mange donc toutes les lignes restantes. Vous pouvez simplement connecter son entrée standard à nulle part :

ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" < /dev/null

Vous pouvez également utiliser ssh -n au lieu de la redirection.

16voto

Charles Duffy Points 34134

Une autre approche consiste à boucler sur une DF autre que stdin :

while IFS= read -u 3 -r -d '' filename; do
  if [[ -d $filename ]]; then
    printf -v cmd_str 'cd %q; mkdir -p %q' "$REMOTE_PATH" "$filename"
    ssh "$USER@$SERVER" "$cmd_str"
  else
    printf -v remote_path_str '%q@%q:%q/%q' "$USER" "$SERVER" "$REMOTE_PATH" "$filename"
    scp -Cp "$filename" "$remote_path_str"
  fi
done 3< <(find devel/ -newer "$UPLOAD_FILE" -print0)

En -u 3 y 3< sont critiques ici, en utilisant FD 3 plutôt que FD 0 (stdin) par défaut.

L'approche présentée ici -- en utilisant -print0 , une personne dégagée IFS est également moins bogué que le code original et la réponse existante, qui ne peut pas gérer correctement les noms de fichiers intéressants. (La réponse de Glenn Jackman est proche, mais même elle ne peut pas gérer les noms de fichiers avec des nouvelles lignes ou des noms de fichiers avec des espaces blancs à la fin).

L'utilisation de printf %q est essentiel pour générer des commandes qui ne peuvent pas être utilisées pour attaquer la machine distante. Considérons ce qui se passerait avec un fichier nommé devel/$(rm -rf /)/hello avec un code qui n'avait pas cette paranoïa.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X