Comment concaténer deux chaînes pour créer un chemin complet

93

J'essaye d'écrire un script bash. Dans ce script, je souhaite que l'utilisateur saisisse le chemin d'un répertoire. Ensuite, je veux ajouter des chaînes à la fin de cette chaîne et créer un chemin vers certains sous-répertoires. Par exemple, supposons que l'utilisateur entre une chaîne comme celle-ci:

/home/user1/MyFolder

Maintenant, je veux créer 2 sous-répertoires dans ce répertoire et y copier quelques fichiers.

/home/user1/MyFolder/subFold1
/home/user1/MyFolder/subFold2

Comment puis-je faire ceci?

Hakim
la source
1
Qu'est-ce que tu as essayé jusque-là? Est-ce également une partie de votre question sur l'obtention des commentaires de l'utilisateur et l'autre sur la création du chemin? Ou juste le chemin?
Levon

Réponses:

135

La norme POSIX exige que plusieurs /soient traités comme un seul /dans un nom de fichier. Ainsi //dir///subdir////fileest le même que /dir/subdir/file.

En tant que tel, concaténer deux chaînes pour créer un chemin complet est un simple comme:

full_path="$part1/$part2"
Dunes
la source
11
Sauf si $ part1 peut être une chaîne vide.
Tuure Laurinolli
1
@TuureLaurinolli Je ne comprends pas d'où tu viens. La concaténation ci-dessus résulterait toujours en un chemin valide. Le chemin n'existe peut-être pas, mais il est toujours valide. par exemple. "" + "/" + "Documents"donne "/Documents".
Dunes
17
Si les parties elles-mêmes sont des chemins relatifs et que part1 peut être vide, le résultat peut passer du chemin relatif au chemin absolu.
Tuure Laurinolli
2
@TuureLaurinolli Ensuite, vous pouvez simplement ajouter ./à l'avant. Mais peut-être que l'utilisateur souhaite concater des chemins absolus. Sinon, ils pourraient simplement s'ajouter ./à l'avant pour forcer le chemin à être relatif. Notez également que "$path1/./$path2"c'est la même chose que "$path1/$path2".
yyny
41
#!/bin/bash

read -p "Enter a directory: " BASEPATH

SUBFOLD1=${BASEPATH%%/}/subFold1
SUBFOLD2=${BASEPATH%%/}/subFold2

echo "I will create $SUBFOLD1 and $SUBFOLD2"

# mkdir -p $SUBFOLD1
# mkdir -p $SUBFOLD2

Et si vous souhaitez utiliser readline pour obtenir la complétion et tout cela, ajoutez un -eà l'appel à read:

read -e -p "Enter a directory: " BASEPATH
Sean Bright
la source
1
Malheureusement, cela ne fonctionne pas lorsque BASEPATH est vide. Ce dont j'ai besoin, c'est quelque chose comme ça qui ajoute seulement un / quand il ne se termine pas déjà par une barre oblique ET n'est pas vide. Ainsi, quand il se termine sur un caractère de nom de fichier légal.
Carlo Wood
17

La simple concaténation de la partie de votre chemin n'accomplira-t-elle pas ce que vous voulez?

$ base="/home/user1/MyFolder/"
$ subdir="subFold1"
$ new_path=$base$subdir
$ echo $new_path
/home/user1/MyFolder/subFold1

Vous pouvez ensuite créer les dossiers / répertoires selon vos besoins.

Une convention est de terminer les chemins de répertoire par /(par exemple /home/) parce que les chemins commençant par / pourraient être confondus avec le répertoire racine. Si une double barre oblique ( //) est utilisée dans un chemin, elle est également toujours correcte. Mais, si aucune barre oblique n'est utilisée sur l'une ou l'autre des variables, ce serait incorrect (par exemple /home/user1/MyFoldersubFold1).

Levon
la source
3
pourquoi je reçois ce message: Myscript.sh: ligne 4: / home / user1 / MyFolder / subFold1: Est un répertoire
Hakim
2
Il vous manque un / du chemin. L'objectif est en ligne /home/user1/MyFolder/subFold1 , vous auriez donc besoin en ligne new_path=$base/$subdir. Mais alors que faites-vous si le chemin donné comprend un '/' de fin?
Thrasi
1
@Thrasi ajoutez simplement le '/' de fin à la variable de sous-répertoire ou newpath=$base/$subdir/vous pouvez jouer avec ceci directement sur la ligne de commande
user12345
3
@ user12345, Oui ... laisse toujours la solution ci-dessus incorrecte.
Thrasi
5

Le script suivant catène plusieurs chemins (relatifs / absolus) (BASEPATH) avec un chemin relatif (SUBDIR):

shopt -s extglob
SUBDIR="subdir"
for BASEPATH in '' / base base/ base// /base /base/ /base//; do
  echo "BASEPATH = \"$BASEPATH\" --> ${BASEPATH%%+(/)}${BASEPATH:+/}$SUBDIR"
done

La sortie est:

BASEPATH = "" --> subdir
BASEPATH = "/" --> /subdir
BASEPATH = "base" --> base/subdir
BASEPATH = "base/" --> base/subdir
BASEPATH = "base//" --> base/subdir
BASEPATH = "/base" --> /base/subdir
BASEPATH = "/base/" --> /base/subdir
BASEPATH = "/base//" --> /base/subdir

Le shopt -s extglobn'est nécessaire que pour permettre à BASEPATH de se terminer sur plusieurs barres obliques (ce qui est probablement absurde). Sans globalisation étendue, vous pouvez simplement utiliser:

echo ${BASEPATH%%/}${BASEPATH:+/}$SUBDIR

ce qui se traduirait par le moins soigné mais fonctionnant toujours:

BASEPATH = "" --> subdir
BASEPATH = "/" --> /subdir
BASEPATH = "base" --> base/subdir
BASEPATH = "base/" --> base/subdir
BASEPATH = "base//" --> base//subdir
BASEPATH = "/base" --> /base/subdir
BASEPATH = "/base/" --> /base/subdir
BASEPATH = "/base//" --> /base//subdir
Carlo Wood
la source
1

Je travaillais avec mon script shell qui doit faire des trucs de jonction de chemin comme vous le faites.

Le truc c'est que les deux chemins aiment

/data/foo/bar

/data/foo/bar/ 

sont valides.

Si je veux ajouter un fichier à ce chemin comme

/data/foo/bar/myfile

il n'y avait pas de méthode native (comme os.path.join () en python) dans le shell pour gérer une telle situation.

Mais j'ai trouvé un truc

Par exemple, le chemin de base était stocké dans une variable shell

BASE=~/mydir

et le dernier nom de fichier que vous souhaitez rejoindre était

FILE=myfile

Ensuite, vous pouvez attribuer votre nouveau chemin comme ceci

NEW_PATH=$(realpath ${BASE})/FILE

et alors vous obtiendrez

$ echo $NEW_PATH

/path/to/your/home/mydir/myfile

la raison est très simple, la commande "realpath" réduirait toujours la barre oblique de fin pour vous si nécessaire

余晓晨
la source
0
#!/usr/bin/env bash

mvFiles() {
    local -a files=( file1 file2 ... ) \
             subDirs=( subDir1 subDir2 ) \
             subDirs=( "${subDirs[@]/#/$baseDir/}" )

    mkdir -p "${subDirs[@]}" || return 1

    local x
    for x in "${subDirs[@]}"; do
        cp "${files[@]}" "$x"
    done
}



main() {
    local baseDir
    [[ -t 1 ]] && echo 'Enter a path:'
    read -re baseDir
    mvFiles "$baseDir"
}

main "$@"
Ormaaj
la source
0

Cela devrait fonctionner pour un répertoire vide (vous devrez peut-être vérifier si la deuxième chaîne commence par /laquelle doit être traitée comme un chemin absolu?):

#!/bin/bash

join_path() {
    echo "${1:+$1/}$2" | sed 's#//#/#g'
}

join_path "" a.bin
join_path "/data" a.bin
join_path "/data/" a.bin

Production:

a.bin
/data/a.bin
/data/a.bin

Référence: Extension des paramètres du shell

tsl0922
la source