Cómo concatenar dos cadenas para construir una ruta completa

93

Estoy intentando escribir un script bash. En este script, quiero que el usuario ingrese la ruta de un directorio. Luego, quiero agregar algunas cadenas al final de esta cadena y construir una ruta a algunos subdirectorios. Por ejemplo, suponga que el usuario ingresa una cadena como esta:

/home/user1/MyFolder

Ahora quiero crear 2 subdirectorios en este directorio y copiar algunos archivos allí.

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

¿Cómo puedo hacer esto?

Hakim
fuente
1
¿Qué has intentado hasta ahora? Además, ¿parte de su pregunta sobre cómo obtener información del usuario y la otra parte sobre cómo crear la ruta? ¿O solo el camino?
Levon

Respuestas:

135

El estándar POSIX exige que varios /se traten como uno solo /en un nombre de archivo. Así //dir///subdir////filees lo mismo que /dir/subdir/file.

Como tal, concatenar dos cadenas para construir una ruta completa es tan simple como:

full_path="$part1/$part2"
Dunas
fuente
11
Excepto si $ part1 puede ser una cadena vacía.
Tuure Laurinolli
1
@TuureLaurinolli No entiendo de dónde vienes. La concatenación anterior aún resultaría en una ruta válida. Es posible que la ruta no exista, pero aún sería válida. p.ej. "" + "/" + "Documents"da "/Documents".
Dunes
17
Si las partes en sí son rutas relativas y la parte 1 puede estar vacía, el resultado puede pasar de una ruta relativa a una absoluta.
Tuure Laurinolli
2
@TuureLaurinolli Entonces, simplemente podría agregar ./al frente. Pero tal vez el usuario quiera concatizar rutas absolutas. De lo contrario, podrían simplemente agregar ./al frente para forzar que la ruta sea relativa. También tenga en cuenta que "$path1/./$path2"es lo mismo 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

Y si desea usar readline para completar y todo eso, agregue -ea la llamada a read:

read -e -p "Enter a directory: " BASEPATH
Sean Bright
fuente
1
Desafortunadamente, esto no funciona cuando BASEPATH está vacío. Lo que necesito es algo así que solo agrega una / cuando aún no termina de una barra oblicua Y no está vacía. Por lo tanto, cuando termina en un carácter de nombre de archivo legal.
Carlo Wood
17

¿No logrará simplemente concatenar la parte de su camino lo que desea?

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

A continuación, puede crear las carpetas / directorios según sea necesario.

Una convención es terminar las rutas de directorio con /(por ejemplo /home/) porque las rutas que comienzan con / podrían confundirse con el directorio raíz. Si //se usa una barra doble ( ) en una ruta, también es correcta. Pero, si no se usa una barra en ninguna de las variables, sería incorrecta (por ejemplo /home/user1/MyFoldersubFold1).

Levon
fuente
3
por qué recibo este mensaje: Myscript.sh: línea 4: / home / user1 / MyFolder / subFold1: Es un directorio
Hakim
2
Te falta un / del camino. El objetivo está en línea, /home/user1/MyFolder/subFold1 por lo que necesitaría en línea new_path=$base/$subdir. Pero entonces, ¿qué haces si la ruta dada incluye un '/' final?
Thrasi
1
@Thrasi simplemente agregue el final '/' a la variable subdir o newpath=$base/$subdir/puede jugar con esto directamente en la línea de comando
user12345
3
@ user12345, Sí ... todavía deja la solución anterior incorrecta.
Thrasi
5

El siguiente script categoriza varias rutas (relativas / absolutas) (BASEPATH) con una ruta relativa (SUBDIR):

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

El resultado del cual es:

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

los shopt -s extglob solamente es necesario para permitir BASEPATH para terminar en múltiples barras (que es probablemente sin sentido). Sin globular extendido, solo puede usar:

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

lo que resultaría en el menos ordenado pero aún funcionando:

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
fuente
1

Estaba trabajando con mi script de shell que necesita hacer algunas cosas de unión de ruta como lo hace usted.

La cosa es que ambos caminos como

/data/foo/bar

/data/foo/bar/ 

son validos.

Si quiero agregar un archivo a esta ruta como

/data/foo/bar/myfile

no había un método nativo (como os.path.join () en python) en shell para manejar tal situación.

Pero encontré un truco

Por ejemplo, la ruta base se almacenó en una variable de shell

BASE=~/mydir

y el último nombre de archivo al que te quieres unir era

FILE=myfile

Entonces puedes asignar tu nueva ruta así

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

y luego obtendrás

$ echo $NEW_PATH

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

la razón es muy simple, el comando "realpath" siempre recorta la barra final si es necesario

余晓晨
fuente
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
fuente
0

Esto debería funcionar para un directorio vacío (es posible que deba verificar si la segunda cadena comienza con la /que debe tratarse como una ruta absoluta):

#!/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

Salida:

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

Referencia: Expansión de parámetros de shell

tsl0922
fuente