Bash - 用空格替换下划线,除了前导/尾随空格

时间:2014-11-01 23:48:59

标签: bash shell sed

我想要用空格替换单词之间的下划线,并且单独留下前导和尾随下划线。例如:

__hello_world_a_b___
hello___world

应该成为

__hello world a b___
hello   world

5 个答案:

答案 0 :(得分:1)

将Bash与其正则表达式支持一起使用:

string='__hello_world_a_b___'
[[ $string =~ ^(_*)(.*[^_])(_*)$ ]]
echo "${BASH_REMATCH[1]}${BASH_REMATCH[2]//_/ }${BASH_REMATCH[3]}"

要检查它是否有效,让我们创建一个将字符串作为参数的脚本:

#!/bin/bash

string=$1
[[ $string =~ ^(_*)(.*[^_])(_*)$ ]]
echo "${BASH_REMATCH[1]}${BASH_REMATCH[2]//_/ }${BASH_REMATCH[3]}"

请调用此脚本bananachmod +x banana,然后继续:

$ ./banana '__hello_world_a_b___'
__hello world a b___
$ ./banana '__hello_world_a_b'
__hello world a b
$ ./banana 'hello_world_a_b___'
hello world a b___
$ ./banana 'hello_world_a_b'
hello world a b
$ ./banana '___'

$ # the previous output is empty
$ ./banana $'___hello_world_with\na_newline___'
___hello world with
a newline___
$ ./banana 'hello___world'
hello   world

答案 1 :(得分:0)

您可以简单地使用以下Perl命令,该命令使用PCRE动词(*SKIP)(*F)

$ echo "hello___world" | perl -pe 's/(?:^_+|_+$)(*SKIP)(*F)|_/ /g'
hello   world
$ echo "__hello_world_a_b___" | perl -pe 's/(?:^_+|_+$)(*SKIP)(*F)|_/ /g'
__hello world a b___

上述正则表达式将匹配除前导和尾随之外的所有_

答案 2 :(得分:0)

另一种纯粹的Bash可能性,它不会使用正则表达式,而是以非常行人的方式扩展整体:

#!/bin/bash

shopt -s extglob

string=$1

wo_leading=${string##+(_)}
wo_underscore=${wo_leading%%+(_)}

printf -v leading '%*s' "$((${#string}-${#wo_leading}))"
printf -v trailing '%*s' "$((${#wo_leading}-${#wo_underscore}))"

echo "${leading// /_}${wo_underscore//_/ }${trailing// /_}"

变量wo_leading将包含不带前导下划线的字符串,变量wo_underscore将包含不带前导和尾随下划线的字符串。从这里开始,很容易获得前导和尾随下划线的数量,用wo_underscore中的空格替换下划线并将所有内容放在一起。

答案 3 :(得分:0)

另一个Perl答案:

perl -pe 's/(?<=[^\W_])(_+)(?=[^\W_])/ " " x length($1) /ge' <<END
__hello_world_a_b___
hello___world
END
__hello world a b___
hello   world

即:一系列下划线前面是一个字符,除了下划线之外是一个单词字符,后跟一个字符,除了下划线之外是一个单词字符。

答案 4 :(得分:0)

如果您有GNU awk,可以使用

awk '{match($0,"^(_*)(.*[^_])(_*)$",arr); print arr[1] gensub("_"," ","g",arr[2]) arr[3]}'