【问题标题】:Split a sentence using space in bash script在 bash 脚本中使用空格分割句子
【发布时间】:2014-10-22 13:05:50
【问题描述】:

如何使用空格分割句子,然后从第二个单词开始打印?

例如,如果我的句子是Hello World Good Morning,那么我想打印如下:

World
Good
Morning

【问题讨论】:

  • 预期输出是 word + new line + word ... ?还是空格分隔?
  • 你为什么发布这样的预期输出?这里所有精彩的答案都只针对上述预期输出。

标签: bash shell ubuntu


【解决方案1】:

cut:

$ echo "Hello World Good Morning" | cut -d' ' -f2-
World Good Morning

这告诉cut 根据delimiter 空间“剪切”(令人惊讶)并从第二个字段打印到末尾。


sed:

$ echo "Hello World Good Morning" | sed 's/^[^ ]* //'
World Good Morning

这会从行的开头 (^) 获取一个不包含空格 ([^ ]*) 的字符块,然后是一个空格并将其替换为空内容。这样,第一个单词就被删除了。


bash

$ while IFS=" " read -r _ b; do echo "$b"; done <<< "Hello World Good Morning"
World Good Morning

这会将字段分隔符设置为空格并读取虚拟变量_ 中的第一个块和变量$b 中的其余部分。然后,它打印$b


同样在awk,使用这个Ed Morton's approach

$ echo 'Hello World Good Morning' | awk '{sub(/([^ ]+ +){1}/,"")}1'
World Good Morning

这会将not space characters 的1 块+spaces 的块替换为空字符串。

【讨论】:

  • 不确定这是否是 OP 的拼写错误,但他的输出有一个小写 w 并且他的输出在每个单词后都有一个换行符
  • 嗨,这两个都很好用。你能告诉sed 's/^[^ ]* //'发​​生了什么吗?
  • @UvaisIbrahim 当然!查看我的更新,我正在写它。
  • 哦@jaypal,你无处不在:D
【解决方案2】:

您可以将记录分隔符更改为 awk 中的空格并从第二条记录开始打印:

$ awk 'NR>1' RS=' ' <<<"Hello World Good Morning"
World
Good
Morning

正如 cmets 中所指出的,在输出的末尾还有一个额外的空白行。这来自输入末尾的换行符。如果您使用的是 GNU awk,可以通过将记录分隔符设置为 [[:space:]] 字符类来抑制它:

$ awk 'NR>1' RS='[[:space:]]' <<<"Hello World Good Morning"

或者,按照fedorqui 的建议,您可以使用printf 而不是echo 将变量传递给awk:

printf '%s' 'Hello World Good Morning' | awk 'NR>1' RS=' '

【讨论】:

  • 为什么最后显示一个空行?
  • 因为输入的末尾包含换行符,而print 也添加了换行符。
  • 或者你也可以使用printf "Hello World Good Morning" | awk 'NR&gt;1' RS=' '
  • @glenn 我正要提这个,我现在已经编辑了。
  • @fedorqui 也是一个不错的建议,尽管我认为通常使用格式说明符是个好主意。我已经编辑包含它,谢谢。
【解决方案3】:

可以使用 split+glob 操作符:

sentence="Hello World Good Morning"
set -f # disable the glob part
IFS=" " # split on space characters:
set  -- $sentence # apply the split+glob operator
                  # (leaving a variable expansion unquoted)

现在$1 包含Hello...$4 包含Morning

shift 1 # (or just "shift") shifts the positional parameters by 1
printf '%s\n' "$@" # print them

请注意,它会根据空格字符的序列进行拆分,并忽略前导和尾随。

以上内容适用于除zsh 之外的任何Bourne-like 或POSIX shell,而不仅仅是bash。使用zsh,除非在 sh 仿真中,否则在变量扩展时没有隐式 split+glob 运算符。有一个显式拆分 $=var 和显式 glob $~var 运算符。所以在zsh:

sentence="Hello World Good Morning"
IFS=" "
set -- $=sentence
shift
printf '%s\n' "$@"

或者:

words=($=sentence)
printf '%s\n' $words[2,-1]

zsh 还具有变量扩展标志,包括用于在给定字符串上拆分的s,以及一种比其他 shell 更一致的嵌套变量扩展方式,因此:

$ printf '%s\n' ${${(s: :)sentence}[2,-1]}
World
Good
Morning

【讨论】:

  • 斯蒂芬,你的意思是:() { :;};printf 吗? ;)
  • 禁用通配符是个好主意。 Shellshock 先生,因为你,我们做了很多工作;)谁发明了 shellshock 这个名字?
  • Mr.Shellshock,恰当的名字。
【解决方案4】:

一个特定于 bash 的示例:

$ read -ra words <<< "Hello World Good Morning" && printf "%s\n" "${words[@]:1}"
World
Good
Morning

【讨论】:

  • 假设 sentence 不包含换行符并且 IFS 未设置或包含空格(默认情况下或未设置时,也会在选项卡上拆分)。将 read -a 替换为 read -A 以使其在 ksh93 或 zsh 中工作。 &lt;&lt;&lt; 来自 zsh${words[@]:1} 来自 ksh93。
【解决方案5】:
$ more good
Hello World Good Morning

$ perl -p -e 's:Hello W:w:g;s: :\n:g' good 
world
Good
Morning

perl -p -e # Inline edit

's:Hello W:w:g #look for Hello followed by a space and the letter W, replace it with w

;s: :\n:g' # After you replace it, find any spaces and replace with a newline

【讨论】:

    【解决方案6】:

    如果您的 grep 支持 -P 选项,这只能通过 grep 实现。

    grep -oP '(?:^\S+|(?<!^)\G)\h*\K\S+'
    

    例子:

    $ echo 'Hello World Good Morning' | grep -oP '(?:^\S+|(?<!^)\G)\h*\K\S+'
    World
    Good
    Morning
    $ echo 'Hello World Good' | grep -oP '(?:^\S+|(?<!^)\G)\h*\K\S+'
    World
    Good
    $ echo 'Hello World' | grep -oP '(?:^\S+|(?<!^)\G)\h*\K\S+'
    World
    

    【讨论】:

      【解决方案7】:

      如果要分行打印,可以使用tr:

      echo "Hello World Good Morning"|tr ' ' '\n'
      

      从第二个单词开始打印:

      echo "Hello World Good Morning"|tr ' ' '\n'|tail -n+2
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-03
        • 1970-01-01
        • 2016-09-16
        • 2013-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-23
        相关资源
        最近更新 更多