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

91,924

Solution 1

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 ${@%/}

Solution 2

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

Solution 3

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

Solution 4

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

Solution 5

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")/"
}
Share:
91,924
sid_com
Author by

sid_com

Updated on February 18, 2022

Comments

  • sid_com
    sid_com over 2 years

    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.

  • glenn jackman
    glenn jackman over 12 years
    +1: To be highly pedantic, that will remove a single slash, not all trailing slashes. To remove any number of trailing slashes: shopt -s extglob; echo "${@%%+(/)}"
  • Gordon Davisson
    Gordon Davisson over 12 years
    Warning: you may not want to remove trailing slashes in all cases. If "/" is supplied as an argument, removing the trailing slash will have ...unfortunate consequences.
  • Dave
    Dave over 9 years
    PROTIP: Combine tr -s / with variable regex to remove repeated slashes then remove trailing slash. e.g. DIR=$(echo //some///badly/written///dir////// | tr -s /); DIR=${DIR%/}
  • twalberg
    twalberg over 9 years
    @Dave Why spawn an external tr when it can be done entirely within the shell, as shown by @glennjackman above? Never mind that the OP didn't ask to have strings of / characters squashed from earlier parts of the string...
  • Dave
    Dave over 9 years
    @twalberg tr removes ANY repeated slashes. glennjackman example only removes TRAILING slashes. Also, I'm not sure that it is POSIX compliant. My example is POSIX compliant and does more cleanup. It was just a related thought. Doesn't answer the OP question directly.
  • twalberg
    twalberg over 9 years
    @Dave Exactly. The OP did not ask to remove ANY repeated slashes. Just trailing ones...
  • Julian F. Weinert
    Julian F. Weinert over 9 years
    @glennjackman looks nice, but doe not do ANYTHING. string_in == string_out
  • glenn jackman
    glenn jackman over 9 years
    Really? At a bash prompt, run set -- one///// two// three four/; shopt -s extglob; echo "${@%%+(/)}" and tell me what you see
  • glenn jackman
    glenn jackman over 9 years
    To query a setting: shopt extglob with no options
  • Kyle Strand
    Kyle Strand over 9 years
    @twalberg I appreciate "protips" that aren't just alternative answers to OP's question in comment threads.
  • ingyhere
    ingyhere over 8 years
    This is Extended Pattern Language and you must set extglob.
  • einpoklum
    einpoklum almost 8 years
    I'd say a separate script is overkill, see @Ivan's answer.
  • Sean Bright
    Sean Bright almost 8 years
    @einpoklum, it's in a "separate script" so that OP (or anyone) can easily just copy/paste it to test locally.
  • Alec Jacobson
    Alec Jacobson over 7 years
    This doesn't work on Mac OS X's builtin bash. The solution by Sean Bright above does: ${VAR%/}
  • tetsujin
    tetsujin almost 7 years
    Don't forget to quote your variables, in case they contain whitespace: TRIMMED=$(echo "$VALUE" | sed 's:/*$::')
  • Chris Johnson
    Chris Johnson almost 7 years
    Actually that isn't necessary inside the $() construct. However it is also harmless :) so it's probably a good practice to use double quotes like "$VALUE" so you don't have to decide when to and when not to use the double quotes.
  • Adam
    Adam almost 7 years
    Any way to combine this with an earlier capture? I want to strip protocol and (if present) trailing slash from a URL but echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)/*$|\1|' doesn't work (since the capture group matches the trailing slash as well i guess). I can do it with two commands: echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)$|\1|' -e 's|/*$||' but wondered if it could be done with one?
  • David Tonhofer
    David Tonhofer almost 7 years
    Worrying about whether "tr" is spawned as a subprocess indicates that shell is not the correct solution for a problem (unless one is answering a twisty question for the Computer Bowl). Reading clarity trumps everything in 2010+ (insofar as that can even be had in sh/bash, stuff's is badly entropic with backwards compatibility)
  • MitchellK
    MitchellK about 5 years
    This answer worked better for me with a very big input file. A simple sed 's:/*$::' < in.txt > out.txt does the job in seconds
  • Livy
    Livy almost 5 years
    It requires all nodes in the path except for the last one to exist. If the user throws in some non-existence path, realpath shall fail.
  • maoizm
    maoizm over 4 years
    @Livy realpath --canonicalize-missing works absolutely correctly with any non-existing part of path
  • mivk
    mivk over 3 years
    Nice and simple, BUT BEWARE: it breaks on arguments containing spaces! (at least if used in the way the OP intended).
  • Mark Ribau
    Mark Ribau over 3 years
    and realpath is missing on some platforms :-(
  • mendel
    mendel over 3 years
    rtFolderin=$1 rtFolder=${$rtFolderin%%+(/)} does not work for shell argument