Como concatenar duas strings para construir um caminho completo

93

Estou tentando escrever um script bash. Neste script, quero que o usuário insira o caminho de um diretório. Em seguida, quero acrescentar algumas strings no final dessa string e construir um caminho para alguns subdiretórios. Por exemplo, suponha que o usuário insira uma string como esta:

/home/user1/MyFolder

Agora eu quero criar 2 subdiretórios neste diretório e copiar alguns arquivos para lá.

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

Como posso fazer isso?

Hakim
fonte
1
O que você tentou até agora? Além disso, parte da sua pergunta é sobre como obter informações do usuário e a outra parte sobre como construir o caminho? Ou apenas o caminho?
Levon

Respostas:

133

O padrão POSIX determina que vários /sejam tratados como um único /em um nome de arquivo. Assim //dir///subdir////fileé o mesmo que /dir/subdir/file.

Assim, concatenar duas strings para construir um caminho completo é tão simples como:

full_path="$part1/$part2"
Dunas
fonte
11
Exceto se $ part1 pode ser uma string vazia.
Tuure Laurinolli
1
@TuureLaurinolli Não entendo de onde você vem. A concatenação acima ainda resultaria em um caminho válido. O caminho pode não existir, mas ainda seria válido. por exemplo. "" + "/" + "Documents""/Documents".
Dunas
17
Se as próprias partes são caminhos relativos e part1 pode estar vazio, o resultado pode mudar de caminho relativo para caminho absoluto.
Tuure Laurinolli
2
@TuureLaurinolli Então você pode simplesmente adicionar ./à frente. Mas talvez o usuário queira concatenar caminhos absolutos. Caso contrário, eles poderiam simplesmente adicionar ./à frente para forçar o caminho a ser relativo. Observe também que "$path1/./$path2"é o mesmo 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

E se você quiser usar readline para obter a conclusão e tudo mais, adicione um -eà chamada para read:

read -e -p "Enter a directory: " BASEPATH
Sean Bright
fonte
1
Infelizmente, isso não funciona quando BASEPATH está vazio. O que eu preciso é algo assim que só adicione um / quando ainda não terminar com uma barra E não estiver vazio. Assim, quando termina em um caractere de nome de arquivo legal.
Carlo Wood
16

Simplesmente concatenar a parte do seu caminho não vai conseguir o que você deseja?

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

Você pode então criar as pastas / diretórios conforme necessário.

Uma convenção é terminar os caminhos do diretório com /(por exemplo /home/) porque os caminhos que começam com / podem ser confundidos com o diretório raiz. Se uma barra dupla ( //) for usada em um caminho, também estará correto. Mas, se nenhuma barra for usada em nenhuma das variáveis, seria incorreto (por exemplo /home/user1/MyFoldersubFold1).

Levon
fonte
3
por que recebo esta mensagem: Myscript.sh: linha 4: / home / user1 / MyFolder / subFold1: É um diretório
Hakim
2
Você está perdendo um / do caminho. O objetivo é embutido, /home/user1/MyFolder/subFold1 então você precisaria embutir new_path=$base/$subdir. Mas então o que você faz se o caminho fornecido inclui um final '/'?
Thrasi
1
@Thrasi basta adicionar o final '/' à variável do subdiretório ou newpath=$base/$subdir/você pode brincar com isso diretamente na linha de comando
user12345
3
@ user12345, Sim ... ainda deixa a solução acima incorreta.
Thrasi
5

O script a seguir catena vários caminhos (relativos / absolutos) (BASEPATH) com um caminho relativo (SUBDIR):

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

O resultado é:

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

O shopt -s extglobé necessário apenas para permitir que BASEPATH termine em várias barras (o que provavelmente é um absurdo). Sem o globing estendido, você pode apenas usar:

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

o que resultaria em menos organizado, mas ainda 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 madeira
fonte
1

Eu estava trabalhando com meu script de shell que precisa fazer algumas coisas de path join como você faz.

A questão é que ambos os caminhos gostam

/data/foo/bar

/data/foo/bar/ 

é válido.

Se eu quiser anexar um arquivo a este caminho, como

/data/foo/bar/myfile

não havia nenhum método nativo (como os.path.join () em python) no shell para lidar com tal situação.

Mas eu encontrei um truque

Por exemplo, o caminho base foi armazenado em uma variável shell

BASE=~/mydir

e o último nome de arquivo do qual você deseja entrar era

FILE=myfile

Então você pode atribuir seu novo caminho assim

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

e então você obterá

$ echo $NEW_PATH

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

o motivo é bastante simples, o comando "realpath" sempre cortaria a barra de encerramento para você, se necessário

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

Isso deve funcionar para um dir vazio (você pode precisar verificar se a segunda string começa com a /qual deve ser tratada como um caminho absoluto?):

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

Resultado:

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

Referência: Expansão de Parâmetros de Shell

tsl0922
fonte