[linux] How to concatenate two strings to build a complete path

I am trying to write a bash script. In this script I want user to enter a path of a directory. Then I want to append some strings at the end of this string and build a path to some subdirectories. For example assume user enters an string like this:

/home/user1/MyFolder

Now I want to create 2 subdirectories in this directory and copy some files there.

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

How can I do this?

This question is related to linux bash

The answer is


The POSIX standard mandates that multiple / are treated as a single / in a file name. Thus //dir///subdir////file is the same as /dir/subdir/file.

As such concatenating a two strings to build a complete path is a simple as:

full_path="$part1/$part2"

The following script catenates several (relative/absolute) paths (BASEPATH) with a relative path (SUBDIR):

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

The output of which is:

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

The shopt -s extglob is only necessary to allow BASEPATH to end on multiple slashes (which is probably nonsense). Without extended globing you can just use:

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

which would result in the less neat but still working:

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

I was working around with my shell script which need to do some path joining stuff like you do.

The thing is, both path like

/data/foo/bar

/data/foo/bar/ 

are valid.

If I want to append a file to this path like

/data/foo/bar/myfile

there was no native method (like os.path.join() in python) in shell to handle such situation.

But I did found a trick

For example , the base path was store in a shell variable

BASE=~/mydir

and the last file name you wanna join was

FILE=myfile

Then you can assign your new path like this

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

and then you`ll get

$ echo $NEW_PATH

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

the reason is quiet simple, the "realpath" command would always trim the terminating slash for you if necessary


#!/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 "$@"

Won't simply concatenating the part of your path accomplish what you want?

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

You can then create the folders/directories as needed.

One convention is to end directory paths with / (e.g. /home/) because paths starting with a / could be confused with the root directory. If a double slash (//) is used in a path, it is also still correct. But, if no slash is used on either variable, it would be incorrect (e.g. /home/user1/MyFoldersubFold1).


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

And if you want to use readline so you get completion and all that, add a -e to the call to read:

read -e -p "Enter a directory: " BASEPATH

This should works for empty dir (You may need to check if the second string starts with / which should be treat as an absolute path?):

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

Output:

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

Reference: Shell Parameter Expansion