如何使用sed剥离多个空格?


69

sed在AIX上没有按照我的想法去做。我正在尝试在IOSTAT的输出中用单个空格替换多个空格:

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

#iostat|grep "hdisk1"|sed -e"s/[ ]*/ /g"
 h d i s k 1 0 . 1 6 . 3 0 . 5 6 3 3 4 5 8 8 0 1 1 2 4 3 2 3 5 4

sed应该为整个组(/ g)搜索并用单个空格(/ /)替换多个空格(/ [] * /),但不仅是这样,它还分隔了每个字符。

我究竟做错了什么?我知道它一定很简单... AIX 5300-06

编辑:我有一台计算机,有10多个硬盘。我将此作为另一个程序的参数进行监视。

我遇到的问题是“ awk'{print $ 5}'不起作用,因为我在第二阶段使用了$ 1等,并给出了Print命令错误。我正在寻找grep / sed / cut版本。似乎有效的是:

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

当我认为[]的意思是“仅一个”时,它们是“ 0或更多”。卸下支架即可正常工作。三个非常好的答案确实很快使选择“答案”变得困难。

Answers:


52

使用grep是多余的,sed可以做到相同。问题在于*该匹配也使用0个空格,您必须改为使用\+

iostat | sed -n '/hdisk1/s/ \+/ /gp'

如果您sed不支持\+metachar,则可以

iostat | sed -n '/hdisk1/s/  */ /gp'

AIX似乎不支持+,但是删除[]似乎可以解决问题。
WernerCD 2011年

我尝试使用sed -n版本...发生的情况是我有一台装有10+个驱动器的计算机,所以它开始执行1、10、11等...我试图添加一个空间/ hdisk1 /,它给了我“无法识别的功能”。似乎有效的是>> iostat | grep“ hdisk1” | SED -e的/ * / / G”
WernerCD

67

/[ ]*/匹配零个或多个空格,因此字符之间的空字符串匹配。

如果您要匹配“一个或多个空格”,请使用以下一项:

... | sed 's/  */ /g'
... | sed 's/ \{1,\}/ /g'
... | tr -s ' '

啊... []使它成为“可选”。这就解释了。
WernerCD 2011年

5
@WernerCD,否*使其变为“可选”。[ ]只是列出其中只有一个字符(空格)的字符列表。它是量词的*意思是“先前事物的零个或多个”
glenn jackman 2011年

嗯...更准确地说,就是将它从单个空格/ * /更改为双倍空格。我知道了
WernerCD 2011年

我正在尝试搜索仅搜索双
精度

6
+1是最简单的tr -s ' '解决方案
Andrejs 2016年

12

将您的*运算符更改为+。您要匹配零个或多个前一个字符,后者匹配每个字符,因为不是空格的所有内容都是... um ...零个实例。您需要匹配一个或多个。实际上,匹配两个或多个会更好

括号中的字符类对于匹配一个字符也是不必要的。您可以使用:

s/  \+/ /g

...除非您也想匹配制表符或其他类型的空格,否则字符类是一个好主意。


AIX似乎不支持+。
WernerCD 2011年

1
@WernerCD:然后尝试s/ */ /g(三个空格,注释格式将其折叠)。star运算符将使前一个字符成为可选字符,因此,如果要使它与两个或多个字符匹配,则需要自己匹配前两个字符(两个空格),然后添加第三个空格和一个星号以使第三个和后面的空格成为可选字符。
卡莱布

3
@userunknown:实际上我根本没有混合两件事,其他人都是:)用一个空格代替一个空格是没有意义的,您只需要对至少有两个连续空格的匹配执行此操作。确实需要两个空格和一个加号,或者三个空格和一个星号。
迦勒(Caleb)

@userunknown:没什么大不了的,只是浪费一点处理时间,并且抛出了诸如比赛计数器之类的东西。
Caleb)

8

您始终可以按以下顺序匹配最后一次出现:

s/\(sequence\)*/\1/

因此,您处在正确的轨道上,而不是将序列替换为空格-将其替换为最后一个出现-单个空格。这样,如果空格序列匹配的,则顺序减少到一个单一的空间,但如果空字符串匹配,则空字符串替换本身-而没有坏处,没有犯规。因此,例如:

sed 's/\( \)*/\1/g' <<\IN                                    
# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty:      tin         tout    avg-cpu: % user % sys % idle % iowait
          0.2         31.8                9.7   4.9   82.9      2.5

Disks:        % tm_act     Kbps      tps    Kb_read   Kb_wrtn
hdisk9           0.2      54.2       1.1   1073456960  436765896
hdisk7           0.2      54.1       1.1   1070600212  435678280
hdisk8           0.0       0.0       0.0          0         0
hdisk6           0.0       0.0       0.0          0         0
hdisk1           0.1       6.3       0.5   63344916  112429672
hdisk0           0.1       5.0       0.2   40967838  98574444
cd0              0.0       0.0       0.0          0         0
hdiskpower1      0.2     108.3       2.3   2144057172  872444176

# iostat | grep hdisk1
hdisk1           0.1       6.3       0.5   63345700  112431123

IN

输出值

# iostat
System configuration: lcpu=4 drives=8 paths=2 vdisks=0

tty: tin tout avg-cpu: % user % sys % idle % iowait
 0.2 31.8 9.7 4.9 82.9 2.5

Disks: % tm_act Kbps tps Kb_read Kb_wrtn
hdisk9 0.2 54.2 1.1 1073456960 436765896
hdisk7 0.2 54.1 1.1 1070600212 435678280
hdisk8 0.0 0.0 0.0 0 0
hdisk6 0.0 0.0 0.0 0 0
hdisk1 0.1 6.3 0.5 63344916 112429672
hdisk0 0.1 5.0 0.2 40967838 98574444
cd0 0.0 0.0 0.0 0 0
hdiskpower1 0.2 108.3 2.3 2144057172 872444176

# iostat | grep hdisk1
hdisk1 0.1 6.3 0.5 63345700 112431123

综上所述,最好在这种情况下完全避免使用正则表达式,而改为:

tr -s \  <infile

4
+1是为了简化实际答案,iostat | tr -s \
2015年

'tr -s \'与'tr -s“”'相同。让我意识到,可以通过使用“ \”转义来在字符串中将空格作为参数传递。我看到它也可以在shell脚本中使用。很酷的应用程序。
randominstanceOfLivingThing

5

请注意,您也可以尝试做,即

iostat | grep "hdisk1 " | sed -e's/  */ /g' | cut -d" " -f 5

通过

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$re"; done

如果您以后又尝试访问其他字段和/或计算某些内容,则这可能特别有用,例如:

iostat | while read disk tma kbps tps re wr; do [ "$disk" = "hdisk1" ] && echo "$(( re/1024 )) Mb"; done

非常好。第一个版本有效。我的AIX盒子似乎不喜欢第二个盒子。全部三个方框输出:“ $ [re / 1024] Mb”。我使用的监视工具具有报告转换功能,因此对我来说不是“必需”的东西,但我喜欢它。
WernerCD 2011年

@enzotib感谢您纠正while
rozcietrzewiacz 2011年

@WernerCD嗯,这$[ .. ]可能在bash的最新版本中可用(也许也是zsh)。我将答案更新为更便携$(( .. ))
rozcietrzewiacz 2011年

做到了。我得查一下。时髦
WernerCD 2011年

0

您可以使用以下脚本将多个空格转换为单个空格,TAB或任何其他字符串:

$ ls | compress_spaces.sh       # converts multiple spaces to one
$ ls | compress_spaces.sh TAB   # converts multiple spaces to a single tab character
$ ls | compress_spaces.sh TEST  # converts multiple spaces to the phrase TEST
$ compress_spaces.sh help       # show the help for this command

compress_spaces.sh

function show_help()
{
  IT=$(CAT <<EOF

  usage: {REPLACE_WITH}

  NOTE: If you pass in TAB, then multiple spaces are replaced with a TAB character

  no args -> multiple spaces replaced with a single space
  TAB     -> multiple spaces replaced with a single tab character
  TEST    -> multiple spaces replaced with the phrase "TEST"

  )
  echo "$IT"
  exit
}

if [ "$1" == "help" ]
then
  show_help
fi

# Show help if we're not getting data from stdin
if [ -t 0 ]; then
  show_help
fi

REPLACE_WITH=${1:-' '}

if [ "$REPLACE_WITH" == "tab" ]
then
  REPLACE_WITH=$'\t'
fi
if [ "$REPLACE_WITH" == "TAB" ]
then
  REPLACE_WITH=$'\t'
fi

sed "s/ \{1,\}/$REPLACE_WITH/gp"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.