web-dev-qa-db-fra.com

Scp peut-il créer un répertoire s'il n'existe pas?

Je veux utiliser scp pour télécharger des fichiers mais parfois le répertoire cible peut ne pas exister.

Est-il possible de créer le dossier automatiquement? Si c'est le cas, comment? Sinon, quelle autre façon puis-je essayer?

100
AGamePlayer

C'est l'une des nombreuses choses que rsync peut faire.

Si vous utilisez une version de rsync publiée au cours des dernières années, ¹ sa syntaxe de commande de base est similaire à scp: ²

$ rsync -r local-dir remote-machine:path

Cela copiera local-source et son contenu à $HOME/path/local-dir sur la machine distante, créant les répertoires nécessaires.³

rsync a quelques restrictions ici qui peuvent affecter si cela fonctionnera dans votre situation particulière. Il ne créera pas plusieurs niveaux de répertoires distants manquants, par exemple; il ne créera qu'un seul niveau manquant sur la télécommande. Vous pouvez facilement contourner ce problème en précédant la commande rsync avec quelque chose comme ceci:

$ ssh remote-Host 'mkdir -p foo/bar/qux'

Cela va créer le $HOME/foo/bar/qux arbre s'il n'existe pas. Il ne se plaindra pas ou ne fera rien de mal si cela existe existe déjà.

rsync a parfois d'autres comportements surprenants. Fondamentalement, vous lui demandez de comprendre ce que vous vouliez copier, et ses suppositions peuvent ne pas correspondre à vos hypothèses. Essayez-le et voyez. S'il ne se comporte pas comme prévu et que vous ne voyez pas pourquoi, publiez plus de détails sur vos configurations de répertoire local et distant et donnez la commande que vous avez essayée.


Notes de bas de page :

  1. Avant rsync 2.6.0 (1er janvier 2004), il fallait le -e ssh flag pour qu'il se comporte comme scp car il est par défaut obsolète protocole RSH .

  2. scp et rsync partagent certains indicateurs, mais il n'y a qu'un peu de chevauchement.

  3. Lorsque vous utilisez SSH comme protocole de transfert, rsync utilise les mêmes valeurs par défaut. Ainsi, tout comme scp, il supposera qu'il existe un utilisateur portant le même nom que votre utilisateur local sur la machine distante par défaut.

76
Warren Young

Si vous copiez un groupe de fichiers, pas vraiment. Si vous copiez un répertoire et tout le contenu en dessous, oui. Étant donné cette commande:

$ scp -pr /source/directory user@Host:the/target/directory

Si directory n'existe pas dans ~/the/target sur Host, il sera créé. Si, toutefois, ~the/target n'existe pas, vous aurez probablement des problèmes - je ne me souviens pas des résultats exacts de ce cas, mais préparez-vous à l'échec de scp.

39
John

Pour autant que je sache, scp lui-même ne peut pas faire cela, non. Cependant, vous pouvez simplement ssh sur la machine cible, créer le répertoire, puis copier. Quelque chose comme:

ssh user@Host "mkdir -p /target/path/" &&
    scp /path/to/source user@Host:/target/path/

Notez que si vous copiez des répertoires entiers, ce qui précède n'est pas nécessaire. Par exemple, pour copier le répertoire ~/foo à l'hôte distant, vous pouvez utiliser le -r (récursif):

scp ~/foo/ user@Host:~/

Cela va créer le répertoire cible ~/foo sur l'hôte distant. Cependant, il ne peut pas créer le répertoire parent. scp ~/foo user@Host:~/bar/foo échouera sauf si le répertoire cible bar existe. Dans tous les cas, le -r L'indicateur n'aidera pas à créer un répertoire cible si vous copiez des fichiers individuels.

17
terdon

J'utilise la fonction suivante avant de transférer btrfs des instantanés sur ssh:

check_remote_dir() {
        printf "\ntesting remote directory: '$1' "

        if ssh -p $PORT $ROOT@$REMOTE "[ ! -d $1 ]"; then
                printf "\nCreating: $1 on $ROOT@$REMOTE\n"
                ssh -p $PORT $ROOT@$REMOTE "mkdir -p $1"
        else
                printf "[OK]\n"
        fi
}

Appelez simplement la fonction dans votre script avec:

check_remote_dir /my/remote/path

4
Stuart Cardall

Oui, vous pouvez. Selon la page de manuel de scp:

man scp

.....

-r Copie récursivement des répertoires entiers. Notez que scp suit les liens symboliques rencontrés lors de la traversée de l'arbre.

....

3
YoMismo

Lorsqu'il est limité à scp ou sftp sans ssh disponible (par rssh), il est possible de créer des répertoires en copiant une arborescence de répertoires vide avec rsync puis en copiant des fichiers.

Voilà comment je mets la clé publique ssh sur l'hôte distant lorsque ssh-copy-id ne fonctionne pas et que le répertoire .ssh n'existe pas:

rsync -e 'ssh -p22' -av -f"+ */" -f"- *" ~/.ssh [email protected]:~/
scp -P22 ~/.ssh/id_rsa.pub [email protected]:~/.ssh/authorized_keys

Il est possible de combiner ces commandes en un seul rsync en ajoutant un filtre d'inclusion supplémentaire au milieu, si le nom du fichier cible est le même.

1
user3132194

Vous pouvez chaîner la commande avec la commande mkdir puis faire référence au dossier que vous avez créé:

mkdir ~/new-folder/ && scp -P 22 <remote/url>:~/new-folder/
0
Ducky

Que diriez-vous de "smart" scp + ssh mkdir?

Il s'agit d'un script pour créer une hiérarchie de répertoires cible ne seule fois par répertoire (pas un ssh mkdir -p Aveugle à chaque fois).

Raisonnement

Je copie des enregistrements animés (fichiers vidéo) au fur et à mesure qu'ils sont écrits sur le disque local (événementiel).

  • rsync n'a pas de sens car il est inutile de faire des comparaisons de répertoires complètes - je transfère un fichier par événement. Si je restreins rsync à un seul sous-répertoire ou fichier, il ne pourra pas créer la hiérarchie complète du répertoire cible.
  • scp fonctionnera très bien mais il ne sait pas si le répertoire cible existe, et je ne veux pas exécuter un ssh mkdir -p pour chaque fichier.

Solution

  1. Créez chaque répertoire cible via ssh la première fois que vous en avez besoin (première fois que nous sommes sur le point de le copier).
  2. Mettez en cache le nom du répertoire cible dans un tableau afin de ne pas ssh mkdir Chaque fois que nous le reverrons. Effacez ce tableau quotidiennement afin qu'il ne devienne pas énorme.
  3. scp le fichier.

Exemple de code:

#!/bin/bash

TARGETHOST="some.hostname"
TARGETPATH="/data/backups/MOTION/"

ensure_dir() {
    if [ "$TODAY" != "$(date +%D)" ] ; then
        CREATED=()  # reset array daily and first time so it won't get huge
        TODAY=$(date +%D)
    fi  
    if [[ ! " ${CREATED[@]} " =~ " ${1} " ]]; then
        # create dir
        echo "Creating remote directory: $1"
        ssh $TARGETHOST "mkdir -p ${TARGETPATH}$1"
        CREATED+=("$1")
    fi  
}

# This line is specific to my use case, watching a directory for new files:
inotifywait -mr --format '%w %f' -e close_write --exclude '.jpg' /data/MOTION/ | while read filedir filename ; do
    RELDIR=$(echo $filedir | sed 's|/data/MOTION/||')
    ensure_dir "$RELDIR"
    echo "Copying '$filename' from '${filedir}' to '${RELDIR}'"
    scp -r -p "${filedir}${filename}" "${TARGETHOST}:${TARGETPATH}${RELDIR}${filename}"
done

Sortie typique, illustrant le comportement "intelligent" de mkdir:

Creating remote directory: 2019-12-28/movie-C6/
Copying 'test1.mp4' from '/data/MOTION/2019-12-28/movie-C6/' to '2019-12-28/movie-C6/'
Creating remote directory: 2019-12-28/movie-C4/
Copying '095700.mp4' from '/data/MOTION/2019-12-28/movie-C4/' to '2019-12-28/movie-C4/'
Copying '095841.mp4' from '/data/MOTION/2019-12-28/movie-C4/' to '2019-12-28/movie-C4/'
Copying '100410.mp4' from '/data/MOTION/2019-12-28/movie-C4/' to '2019-12-28/movie-C4/'
Creating remote directory: 2019-12-28/movie-C2/
Copying '102106.mp4' from '/data/MOTION/2019-12-28/movie-C2/' to '2019-12-28/movie-C2/'
Creating remote directory: 2019-12-28/movie-C5/
Copying '102318.mp4' from '/data/MOTION/2019-12-28/movie-C5/' to '2019-12-28/movie-C5/'
Copying '102326.mp4' from '/data/MOTION/2019-12-28/movie-C2/' to '2019-12-28/movie-C2/'
Creating remote directory: 2019-12-28/movie-C1/
Copying '102450.mp4' from '/data/MOTION/2019-12-28/movie-C1/' to '2019-12-28/movie-C1/'
Copying '102744.mp4' from '/data/MOTION/2019-12-28/movie-C2/' to '2019-12-28/movie-C2/'
Copying '103008.mp4' from '/data/MOTION/2019-12-28/movie-C2/' to '2019-12-28/movie-C2/'
Copying '103945.mp4' from '/data/MOTION/2019-12-28/movie-C6/' to '2019-12-28/movie-C6/'
Copying '104252.mp4' from '/data/MOTION/2019-12-28/movie-C6/' to '2019-12-28/movie-C6/'
Copying '104824.mp4' from '/data/MOTION/2019-12-28/movie-C6/' to '2019-12-28/movie-C6/'
0
Akom