如何简单地从每个参数中删除尾随斜线?

161

如何从 '$@' 数组的每个参数中最简单地删除尾部斜杠,以便 rsync 按名称复制目录?

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

标题已经更改以进行澄清。要了解有关多个尾随斜杠的评论和答案,请查看编辑历史记录。


2
可能是从变量末尾删除斜杠的重复问题。 - Warren Dew
那些不知道的人,在bash中,a//ba/b是相同的。因此,在某些情况下,一个人不必删除尾部斜杠。 - Gayan Weerakutti
9个回答

245
您可以使用详细介绍 这里${parameter%word} 扩展。这是一个演示行为的简单测试脚本:
#!/bin/bash

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

echo ${@%/}

46
严格来说,这将删除一个斜杠,而不是所有尾随的斜杠。要删除任意数量的尾随斜杠,请使用以下命令:shopt -s extglob; echo "${@%%+(/)}" - glenn jackman
28
警告:在某些情况下,您可能不想删除所有尾随斜杠。如果“/”作为参数提供,则删除尾随斜杠将产生不良后果。 - Gordon Davisson
14
提示:结合变量正则表达式使用 tr -s / 命令来删除重复的斜杠,然后删除末尾的斜杠。例如:DIR=$(echo //some///badly/written///dir////// | tr -s /); DIR=${DIR%/} - Dave
1
真的吗?在bash提示符下,运行set -- one///// two// three four/; shopt -s extglob; echo "${@%%+(/)}",告诉我你看到了什么。 - glenn jackman
1
担心“tr”是否作为子进程生成表明shell不是解决问题的正确方案(除非回答计算机比赛中的扭曲问题)。在2010年以后,阅读清晰度胜过一切(就算在sh/bash中能够实现,也会因为向后兼容性而变得混乱无序)。 - David Tonhofer
显示剩余9条评论

61

被接受的答案会删除一个尾随斜线。

删除多个尾随斜线的一种方法是这样的:

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

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

echo "$VALUE" "$TRIMMED"

输出为:

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

1
不要忘记引用变量,以防它们包含空格:TRIMMED=$(echo "$VALUE" | sed 's:/*$::') - tetsujin
3
$() 结构中其实不需要这样做,但也没有坏处 :) 所以最好的做法是使用双引号,如 "$VALUE",这样就不必决定何时使用双引号和何时不使用。 - Chris Johnson
有没有办法将其与早期捕获组合起来?我想从URL中剥离协议和(如果存在)尾部斜杠,但是echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)/*$|\1|'不起作用(因为捕获组也匹配尾部斜杠)。我可以用两个命令完成它:echo "https://www.example.com/foo/" | sed -e 's|https*://\(.*\)$|\1|' -e 's|/*$||'但想知道是否可以用一个命令完成? - Adam
这个答案对于我来说在处理非常大的输入文件时效果更好。一个简单的 sed 's:/*$::' < in.txt > out.txt 可以在几秒钟内完成工作。 - MitchellK

40

3
查询设置:shopt extglob,无选项。 - glenn jackman
4
这是扩展模式语言,你必须设置extglob - ingyhere
2
这在Mac OS X的内置bash上不起作用。Sean Bright上面提供的解决方案可以:${VAR%/} - Alec Jacobson
来自Perl和正则表达式领域的陷阱是,我的本能反应是半忘记使用globbing和模式匹配,而是尝试${VAR%%/*},它删除第一个斜杠到结尾的所有内容(例如,“foo///bar/foobar”变成“foo”)。附言:令人惊讶的是,在Fedora 36中,默认情况下启用了extglob。 - Kevin

37

realpath 用于解析给定路径。除其他功能外,它还会删除末尾的斜杠。使用 -s 来避免跟随符号链接。

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

3
realpath 需要路径中除了最后一个节点以外的所有节点都存在。如果用户输入了一些不存在的路径,realpath 将会失败。 - Livy
4
@Livy realpath --canonicalize-missing 命令能够完全正确地处理路径中任何不存在的部分。 - maoizm
在一些平台上缺少 realpath :-( - Mark

11

提供信息,我根据在SO上找到的答案将这两个功能添加到了我的.bash_profile中。正如Chris Johnson所说,使用${x%/}的所有答案仅会删除一个斜杠,而这些功能将执行它们所说的操作,希望这对您有用。

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

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

6
zsh中,您可以使用:a修饰符。
export DIRECTORY='/some//path/name//'

echo "${DIRECTORY:a}"

=> /some/path/name

这类似于realpath,但不会因为缺少文件/目录作为参数而失败。

1

在处理用于 rsync 的目录参数时,我通常使用以下方法进行修剪,使用 dirnamebasename 分割路径,然后重新组合部分,不包括尾部斜杠。

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

0
注意接受答案中的一些评论:
  1. 将所有重复的斜杠 //[...] 替换为单个斜杠 /(根据 @Dave 的评论)
  2. 删除尾随斜杠,除非它也是前导斜杠(即根文件路径 /)(根据 @GordonDavisson 的评论)
trimSlash() { for s; do sed -E 's://*:/:g; s:(^/)?/*$:\1:' <<< "${s}"; done; }

虽然没有使用参数替换的答案那么简洁,但我认为它值得努力。

一些测试用例:

$ 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

-1

这不是最美观的方式,但快捷简便。

我只需添加一条斜杠并删除所有双重的部分。假设这样的模式不会在其他地方被找到。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接