Jak połączyć dwa ciągi, aby zbudować kompletną ścieżkę

95

Próbuję napisać skrypt basha. W tym skrypcie chcę, aby użytkownik wprowadził ścieżkę do katalogu. Następnie chcę dołączyć kilka ciągów na końcu tego ciągu i zbudować ścieżkę do niektórych podkatalogów. Na przykład załóżmy, że użytkownik wprowadza następujący ciąg:

/home/user1/MyFolder

Teraz chcę utworzyć 2 podkatalogi w tym katalogu i skopiować tam kilka plików.

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

Jak mogę to zrobić?

Hakim
źródło
1
Czego spróbowałeś do tej pory? Czy część pytania dotyczy uzyskiwania informacji od użytkownika, a druga - budowania ścieżki? A może po prostu ścieżka?
Levon

Odpowiedzi:

136

Standard POSIX wymaga, aby wiele było /traktowanych jako pojedyncze /w nazwie pliku. Zatem //dir///subdir////filejest tym samym, co /dir/subdir/file.

W związku z tym połączenie dwóch ciągów w celu zbudowania pełnej ścieżki jest proste, jak:

full_path="$part1/$part2"
Wydmy
źródło
11
Z wyjątkiem sytuacji, gdy $ part1 może być pustym łańcuchem.
Tuure Laurinolli
1
@TuureLaurinolli Nie rozumiem, skąd pochodzisz. Powyższa konkatenacja nadal skutkowałaby prawidłową ścieżką. Ścieżka może nie istnieć, ale nadal byłaby ważna. na przykład. "" + "/" + "Documents"daje "/Documents".
Dunes
17
Jeśli same części są ścieżkami względnymi, a część 1 może być pusta, wynik może zmienić się ze ścieżki względnej na ścieżkę bezwzględną.
Tuure Laurinolli,
2
@TuureLaurinolli Następnie możesz po prostu dodać ./z przodu. Ale może użytkownik chce połączyć ścieżki bezwzględne. W przeciwnym razie mogliby po prostu dodać ./do przodu, aby wymusić względną ścieżkę. Zauważ też, że "$path1/./$path2"to to samo, co "$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

A jeśli chcesz użyć readline, aby ukończyć i tak dalej, dodaj -edo wywołania read:

read -e -p "Enter a directory: " BASEPATH
Sean Bright
źródło
1
Niestety, to nie działa, gdy BASEPATH jest pusty. To, czego potrzebuję, to coś takiego, które dodaje / tylko wtedy, gdy nie kończy się już ukośnikiem ORAZ nie jest puste. Tak więc, kiedy kończy się na legalnym znaku nazwy pliku.
Carlo Wood
17

Czy po prostu połączenie części ścieżki nie przyniesie tego, czego chcesz?

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

Następnie możesz utworzyć foldery / katalogi według potrzeb.

Jedną z konwencji jest kończenie ścieżek katalogów /(np. /home/), Ponieważ ścieżki zaczynające się od / mogą zostać pomylone z katalogiem głównym. Jeśli //w ścieżce użyto podwójnego ukośnika ( ), to również jest poprawne. Ale jeśli na żadnej ze zmiennych nie zostanie użyty ukośnik, będzie on niepoprawny (np /home/user1/MyFoldersubFold1.).

Levon
źródło
3
dlaczego otrzymuję tę wiadomość: Myscript.sh: wiersz 4: / home / user1 / MyFolder / subFold1: Is a directory
Hakim
2
Brakuje / ze ścieżki. Celem jest inline, /home/user1/MyFolder/subFold1 więc potrzebujesz inline new_path=$base/$subdir. Ale co zrobić, jeśli podana ścieżka zawiera na końcu „/”?
Thrasi
1
@Thrasi po prostu dodaj końcowe '/' albo do zmiennej subdir, albo newpath=$base/$subdir/możesz grać z tym bezpośrednio w linii poleceń
user12345
3
@ user12345, Tak ... nadal pozostawia powyższe rozwiązanie niepoprawne.
Thrasi
5

Poniższy skrypt łączy kilka (względnych / bezwzględnych) ścieżek (BASEPATH) ze ścieżką względną (SUBDIR):

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

Wynik którego jest:

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

Jest shopt -s extglobto konieczne tylko, aby pozwolić BASEPATH kończyć się wieloma ukośnikami (co jest prawdopodobnie nonsensem). Bez rozszerzonego globowania możesz po prostu użyć:

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

co spowodowałoby mniej schludne, ale nadal działające:

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
źródło
1

Pracowałem z moim skryptem powłoki, który musi łączyć ścieżki, tak jak ty.

Chodzi o to, że obie ścieżki

/data/foo/bar

/data/foo/bar/ 

są ważne.

Jeśli chcę dołączyć plik do tej ścieżki, na przykład

/data/foo/bar/myfile

nie było natywnej metody (jak os.path.join () w pythonie) w powłoce do obsługi takiej sytuacji.

Ale znalazłem sztuczkę

Na przykład ścieżka podstawowa była przechowywana w zmiennej powłoki

BASE=~/mydir

a ostatnia nazwa pliku, do którego chcesz dołączyć, to

FILE=myfile

Następnie możesz przypisać nową ścieżkę w ten sposób

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

a wtedy dostaniesz

$ echo $NEW_PATH

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

powód jest bardzo prosty, polecenie „realpath” zawsze usuwałoby kończący ukośnik, jeśli to konieczne

余晓晨
źródło
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
źródło
0

Powinno to zadziałać dla pustego katalogu (może być konieczne sprawdzenie, czy zaczyna się drugi ciąg, od /którego należy traktować jako ścieżkę bezwzględną?):

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

Wynik:

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

Odniesienie: Rozszerzenie parametrów powłoki

tsl0922
źródło