6

Say for example I have a path like

path_1=/this/is/a/path/with/slash/

How do I get the following:

/this/is/a/path/with/slash

so the path without the last "/"

  • 1
    Assuming the original path is valid (…/slash/ leads to a directory), if you want to postpend a component and you're not sure if $path_1 ends with /, don't bother and just postpend /component: …/slash//component will work like …/slash/component. There's at least one situation when the trailing slash matters though: if slash is a symlink to a directory then …/slash/ means the directory, certainly not the symlink, while …/slash may mean the symlink. Then your question is useful. – Kamil Maciorowski Jan 30 '22 at 22:27
  • Isn't his a duplicate? I.e.: I cannot believe this hadn't be asked and answered several hundred times. – U. Windl Feb 01 '22 at 08:45
  • See https://stackoverflow.com/a/47592882/6607497 for example. – U. Windl Feb 01 '22 at 09:06
  • that answer is barely understandable. The replies to this question are much better. – KansaiRobot Feb 02 '22 at 02:07

5 Answers5

29

All POSIX shells have (c.f. man bash) "Parameter Expansion: Remove matching suffix pattern". So, use

$ echo "${path_1%/}"
/this/is/a/path/with/slash

If the variable's value does not end in a slash, then the value would be outputted without modification.

RudiC
  • 8,969
  • 5
    This has one possible problem: if the path is just "/" (i.e. the filesystem root), you don't want to remove the / because it'll turn into the empty string, which means something entirely different. – Gordon Davisson Jan 31 '22 at 16:41
11

You can use realpath

DIR=/tmp/foo///
echo "$(realpath -s "$DIR")"
# output: /tmp/foo
Kusalananda
  • 333,661
VP.
  • 267
3

Another way of removing the slash is

 $ echo "$(dirname -- "$path")/$(basename -- "$path")"

We just combined the two common shell commands dirname and basename.

ilkkachu
  • 138,973
Jabir Ali
  • 191
  • 1
  • 3
  • 3
    You need to quote the expansion of $path and also the command substitution itself to avoid the issues mentioned in When is double-quoting necessary?. (See also What's the right way to quote $(command $arg)?). Also if the filename can start with a dash, -- is needed so that it's not taken as a bunch of options. Of course this will also rewrite paths like foo/ into ./foo. – ilkkachu Jan 30 '22 at 19:27
  • 1
    It's unfortunate that you use path as the variable's name. In the bash shell, it doesn't matter, but the path array variable in the zsh shell is tied to the PATH variable and will contain the elements of the PATH variable's :-delimited values. – Kusalananda Jan 30 '22 at 21:32
2

You can use sed in a simple way like this:

$ echo $path_1|sed 's-/$--'
/this/is/a/path/with/slash

Explanation:

  • s: Substitute. The first - marks the end of the command.
  • /$: Search pattern. The $ means look at the end of the line.
  • There is nothing between the second and third -, meaning, "replace the ending / with nothing".
  • You can use any character instead of - the most common is /, but since we are looking for the / itself, it is easier to use some other character. If one really wants to use /, then it would need to be escaped like so:

$ echo $path_1|sed 's/\/$//'

1

There are several options. I usually canonicalise the path. When you canonicalise a path, you get the base path.

For example, if the path is a link to a folder, the canonical form will get the actual path. It will also remove all double-slashes, which although unusual, are allowed in Unix and Linux.

Suppose ~/lf is a link to ~/.hidden/food/limes/.

PATHNAME=~/lf//price/
CANONICAL_PATH="$( realpath --canonicalize-existing "${PATHNAME}" )"
echo "${CANONICAL_PATH}"

The result would be /home/kamil/.hidden/food/limes/price

This also works for files, block devices, etc., although of course they don't have a trailing slash. For example, on my system:

PATHNAME=/dev/disk/by-partlabel/Boot
CANONICAL_PATH="$( realpath --canonicalize-existing "${PATHNAME}" )"
echo "${CANONICAL_PATH}"

The result is /dev/nvme0n1p2

If you aren't sure that the path exists, you need to add some error-checking.

PATHNAME=~/lf//price/
CANONICAL_PATH="$( realpath --canonicalize-existing "${PATHNAME}" 2>/dev/null )"
if [[ -z ${CANONICAL_PATH} ]]
then
    echo "Path doesn't exist: ${PATHNAME}" >&2
else
   echo "Canonical path is ${CANONICAL_PATH}"
fi