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 "/"

KansaiRobot
  • 175
  • 2
  • 7
  • 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](https://unix.stackexchange.com/a/1919/108618) 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](https://unix.stackexchange.com/a/29836/108618), 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

27

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,889
  • 2
  • 10
  • 22
  • 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
  • 320,670
  • 36
  • 633
  • 936
VP.
  • 268
  • 1
  • 13
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
  • 133,243
  • 15
  • 236
  • 397
Jabir Ali
  • 181
  • 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?](https://unix.stackexchange.com/questions/68694/when-is-double-quoting-necessary). (See also [What's the right way to quote $(command $arg)?](https://unix.stackexchange.com/questions/443989/whats-the-right-way-to-quote-command-arg/443990#443990)). 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/\/$//'

Hopping Bunny
  • 502
  • 3
  • 4
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
Paddy Landau
  • 306
  • 2
  • 10