如何使用sed删除行末空格?

141

我有一个简单的shell脚本,可以从文件中删除尾随空格。有没有办法使这个脚本更加紧凑(不创建临时文件)?

sed 's/[ \t]*$//' $1 > $1__.tmp
cat $1__.tmp > $1
rm $1__.tmp

2
你可以使用 mv 代替 catrm。你为什么要那样使用 cat 呢?为什么不使用 cp 呢? - Dennis Williamson
1
我使用了从这个问题学到的知识来创建一个递归删除行尾空格的Shell脚本 - David Tuite
1
你的解决方案在使用MinGW时实际上更好,因为Windows上的sed存在一个错误:https://dev59.com/LWYq5IYBdhLWcg3wpyNE - Cody Piersall
请注意,使用cat覆盖原始文件而不是mv实际上会替换原始文件中的数据(即,它不会破坏硬链接)。使用许多解决方案中提出的sed -i也不会这样做。换句话说,继续做你正在做的事情。 - William Pursell
12个回答

206

您可以在Linux和Unix中使用sed的就地选项-i:

sed -i 's/[ \t]*$//' "$1"

请注意,该表达式将在OSX上删除尾随的t(您可以使用gsed来避免此问题)。它也可能会在BSD上删除它们。

如果您没有gsed,在OSX上这里是正确的(但难以阅读的)sed语法:

sed -i '' -E 's/[ '$'\t'']+$//' "$1"

三个单引号字符串最终被连接成一个单一的参数/表达式。Bash中没有连接运算符,您只需将字符串一个接一个地放置,中间不加空格即可。
在bash中,$'\t'解析为一个字面制表符(使用ANSI-C quoting),因此制表符被正确地连接到表达式中。

1
我在我的电脑上遇到了无法更新的问题,错误提示如下:sed: 未识别的标记:i - javaPlease42
2
嗯,从某种意义上说,它也存在漏洞,会删除所有尾随的“t” :) - Good Person
2
"sed:未识别的标志:i -" 这在OSX上发生。您需要在-i后为备份文件添加扩展名,例如:sed -i .bak 's/[ \t]*$//' $1。 - Aimon Bustardo
1
@GoodPerson 如果你不是在开玩笑,那么你可能忘记转义 t 了 :) \t 表示制表符,对于那些可能还不知道的人来说。 - Sean Allred
2
@SeanAllred并不是在开玩笑:除非你使用GNU sed(它在许多其他方面也存在问题),否则它完全无法正常工作。 - Good Person
显示剩余4条评论

70

至少在Mountain Lion上,Viktor的答案也会在行尾出现字符“t”时将其删除。以下修复此问题:

sed -i '' -e's/[[:space:]]*$//' "$1"

1
我的sed也需要一个-E,表示“扩展(现代)正则表达式”。 - Jared Beck
1
codaddict的回答在OS X(现在的macOS)上有同样的问题。这是该平台上唯一的解决方案。 - Franklin Yu
@JaredBeck 我在 El Capitan 上使用的 sed 没有出现问题。 - Franklin Yu
感谢!所以要修剪:|sed -r -e 's"^[[:space:]]*""' -e 's"[[:space:]]*$""'"` - Aquarius Power

20

感谢codaddict提出的-i选项。

以下命令可解决Snow Leopard上的问题。

sed -i '' -e's/[ \t]*$//' "$1"

我在这里发现了这个链接,http://joemaller.com/823/quick-note-about-seds-edit-in-place-option/ - Viktor
8
就像 @acrollet 所说的那样,你不能在非GNU sed中使用 \t,因为它会被解释为字母 t。这个命令似乎只能工作,可能是因为文件中没有制表符或句子结尾处没有字母 t。建议不要在未指定备份后缀的情况下使用 '' - Scrutinizer
如果分辨率仅适用于Snow Leopard,也许问题应该是“如何在MacOS上删除尾随空格?” - Boyd

14

最好也引用$1:

sed -i.bak 's/[[:blank:]]*$//' "$1"

5

我有一个脚本放在我的.bashrc里,可以在OSX和Linux (仅限bash)上工作。

function trim_trailing_space() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will trim (in place) trailing spaces in the given file (remove unwanted spaces at end of lines)"
    echo "Usage :"
    echo "$FUNCNAME file"
    return
  fi
  local file=$1
  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    sed -E -i ''  's/[[:space:]]*$//' $file
  else
    sed -i  's/[[:space:]]*$//' $file
  fi
}

我补充一点:
SRC_FILES_EXTENSIONS="js|ts|cpp|c|h|hpp|php|py|sh|cs|sql|json|ini|xml|conf"

function find_source_files() {
  if [[ $# -eq 0 ]]; then
    echo "$FUNCNAME will list sources files (having extensions $SRC_FILES_EXTENSIONS)"
    echo "Usage :"
    echo "$FUNCNAME folder"
    return
  fi
  local folder=$1

  unamestr=$(uname)
  if [[ $unamestr == 'Darwin' ]]; then
    #specific case for Mac OSX
    find -E $folder -iregex '.*\.('$SRC_FILES_EXTENSIONS')'
  else
    #Rhahhh, lovely
    local extensions_escaped=$(echo $SRC_FILES_EXTENSIONS | sed s/\|/\\\\\|/g)
    #echo "extensions_escaped:$extensions_escaped"
    find $folder -iregex '.*\.\('$extensions_escaped'\)$'
  fi
}

function trim_trailing_space_all_source_files() {
  for f in $(find_source_files .); do trim_trailing_space $f;done
}

5
var1="\t\t Test String trimming   "
echo $var1
Var2=$(echo "${var1}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
echo $Var2

1
嘿,这正是我需要的!其他发布的sed解决方案在我的bash脚本中集成管道(和管道和管道...)变量分配时存在问题,但您的开箱即用。 - Eric L.

4

对于那些寻求效率的人(需要处理许多文件或大文件),使用+重复操作符而不是*可以使命令速度提高两倍以上。

使用GNU sed:

sed -Ei 's/[ \t]+$//' "$1"
sed -i 's/[ \t]\+$//' "$1"   # The same without extended regex

我还快速进行了另一项基准测试:使用[ \t]代替[[:space:]]同样可以显著加快进程(GNU sed v4.4):

sed -Ei 's/[ \t]+$//' "$1"

real    0m0,335s
user    0m0,133s
sys 0m0,193s

sed -Ei 's/[[:space:]]+$//' "$1"

real    0m0,838s
user    0m0,630s
sys 0m0,207s

sed -Ei 's/[ \t]*$//' "$1"

real    0m0,882s
user    0m0,657s
sys 0m0,227s

sed -Ei 's/[[:space:]]*$//' "$1"

real    0m1,711s
user    0m1,423s
sys 0m0,283s

2
为了删除当前目录中所有文件的尾部空白,我使用以下命令:
ls | xargs sed -i 's/[ \t]*$//'

这对于BSD sed不起作用:echo test | sed 's/[ \t]*$//'会被截断为tes - undefined

2
在特定情况下,对于sed命令而言,如其他人所提到的那样,-i选项是最简单和最明智的选择。
在更一般的情况下,来自moreutils集合的sponge正好可以满足您的需求:它允许您用处理后的结果替换文件,以一种特别设计的方式,从而避免处理步骤因覆盖正在处理的文件而出现问题。引用sponge手册的话:“sponge读取标准输入并将其写入指定的文件。与shell重定向不同,sponge在写出输出文件之前吸收了所有的输入。这允许构建从同一个文件读取和写入的管道。”

https://joeyh.name/code/moreutils/


1

这些答案让我感到困惑。这两个sed命令在Java源文件上都能够正常执行:

  • sed 's/\s\+$/ 文件名
  • sed 's/[[:space:]]\+$// 文件名

为了测试目的,我使用了:

 $ echo "  abc       " | sed 's/\s\+$/-xx/'
abc-xx
 $ echo -e "  abc   \t\t    " | sed 's/\s\+$/-xx/'
abc-xx

将所有尾随的空格替换为"-xx"。

@Viktor希望避免使用临时文件,个人建议只使用-i => 带有备份后缀的原地操作。至少在我确定命令可行之前是这样的。

抱歉,我觉得现有的回答有点含糊不清。sed是一个直截了当的工具。90%的时间里,以一种直截了当的方式来处理它会更容易。或者我可能错过了什么,很高兴在那里得到纠正。


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