What is the simplest way to remove a trailing slash from each parameter?

138.8k Views Asked by At

What is the simplest way to remove a trailing slash from each parameter in the '$@' array, so that rsync copies the directories by name?

rsync -a --exclude='*~' "$@" "$dir"

The title has been changed for clarification. To understand the comments and answer about multiple trailing slashes see the edit history.

10

There are 10 best solutions below

15
On BEST ANSWER

You can use the ${parameter%word} expansion that is detailed here. Here is a simple test script that demonstrates the behavior:

#!/bin/bash

# Call this as:
#   ./test.sh one/ two/ three/ 
#
# Output:
#  one two three

echo ${@%/}
0
On

Not the most beautiful way, but quick and easy.

I just add a slash and remove all doubles. Assuming such a pattern will not be found elsewhere.

WORD="abc/"
WORD=$WORD'/'
WORD=`echo $WORD | sed s/'\/\/'/''/g`
echo $WORD
0
On

Taking note of a couple comments in the accepted answer:

  1. Replace all repeated slashes //[...] with a single slash / (per @Dave comment)
  2. Remove trailing slash unless it is also the leading slash (i.e., the root filepath /) (per @GordonDavisson comment)
trimSlash() { for s; do sed -E 's://*:/:g; s:(^/)?/*$:\1:' <<< "${s}"; done; }

Not as concise as the answers using parameter substitution, but I think its worth the diligence.

Some test cases:

$ trimSlash "/" "///" "a/" "a/a/" "a///a/" "a/a" "a///a" "a///" "/a/a/" "///a///"
/
/
a
a/a
a/a
a/a
a/a
a
/a/a
/a
4
On

This works for me: ${VAR%%+(/)}

As described here http://wiki.bash-hackers.org/syntax/pattern

May need to set the shell option extglob. I can't see it enabled for me but it still works

3
On

realpath resolves given path. Among other things it also removes trailing slashes. Use -s to prevent following simlinks

DIR=/tmp/a///
echo $(realpath -s $DIR)
# output: /tmp/a
0
On

FYI, I added these two functions to my .bash_profile based on the answers found on SO. As Chris Johnson said, all answers using ${x%/} remove only one slash, these functions will do what they say, hope this is useful.

rem_trailing_slash() {
    echo "$1" | sed 's/\/*$//g'
}

force_trailing_slash() {
    echo "$(rem_trailing_slash "$1")/"
}
0
On

Approach I have used, when trimming directory arguments that are intended for rsync, here using dirname and basename to split the path and then recombining the parts without the trailing slash.

raw_dir=/a/b/c/
trim_dir=$(dirname "$raw_dir")"/"$(basename "$raw_dir")
0
On

Completely POSIX compliant

# recursively remove trailing slashes
remove_slashes() {
    res="${1%/}"
    if [ "$1" = "$res" ]
    then echo "$res"
    else remove_slashes "$res"
    fi
}

# test:
remove_slashes a/b/
remove_slashes a/b////
remove_slashes ""
remove_slashes ///
remove_slashes ///a
0
On

In zsh you can use the :a modifier.

export DIRECTORY='/some//path/name//'

echo "${DIRECTORY:a}"

=> /some/path/name

This acts like realpath but doesn't fail with missing files/directories as argument.

4
On

The accepted answer will trim ONE trailing slash.

One way to trim multiple trailing slashes is like this:

VALUE=/looks/like/a/path///

TRIMMED=$(echo "$VALUE" | sed 's:/*$::')

echo "$VALUE" "$TRIMMED"

Which outputs:

/looks/like/a/path/// /looks/like/a/path