如何使用bash提取子字符串

时间:2014-04-03 18:33:31

标签: bash

我有一个表示日期的字符串:

    "May 5 2014"

我想知道如何提取" 5"从中。

到目前为止我尝试过的事情:

   echo "May 5 2014" | sed 's/[^0-9]*\s//'

返回" 5 2014"

对不起补救问题。对bash来说是新手。

6 个答案:

答案 0 :(得分:4)

使用cut

echo "May 5 2014" | cut -d' ' -f2

awk

echo "May 5 2014" | awk '{print $2}'

如果你想在没有外部工具的情况下使用它,它将分为两个步骤:

s="May 5 2014"
t="${s#* }"
echo "${t% *}"

答案 1 :(得分:4)

如果您正在编写需要解析日期字符串的脚本,那么您肯定可以使用sed等来完成它,而且确实已经有几个答案可以很好地完成这个操作。

但是,我的建议是让date计划为您做繁重的工作:

$ date -d "May 5 2014" +%-d
5

date程序的维护者毫无疑问花费了很多小时和几天的日期解析代码。为什么不利用这项工作而不是自己动手呢?

修改

添加了BSD解决方案,例如for(Mac OS X)

date -j -f '%b %d %Y' 'May 5 2014' '+%d'
BSD上的

需要告诉date"传入"日期为-f format,并以+format格式输出。 -j表示未设置日期

答案 2 :(得分:4)

Bash的内置读取命令可以将输入拆分为多个变量。 '<<<<<<<告诉read从以下字符串中获取输入。

read first second remainder <<< "May 5 2014"

之后,“$ first”将为“May”,“$ second”将为“5”,“$ remaining”将为“2014”

通常的做法是使用''作为不感兴趣字段的占位符,因为shell会自动覆盖$

read _ day _ <<< 'May 5 2014 utc'

答案 3 :(得分:3)

与sed,一种可能性是:

echo "May 5 2014" | sed 's/.* \([0-9]*\) .*/\1/'
另一个

echo "May 5 2014" | sed 's/[^ ]* //;s/ [^ ]*//'

另一个

echo "May 5 2014" | sed 's/\(.*\) \(.*\) \(.*\)/\2/'

使用grep

echo "May 5 2014" | grep -oP '\b\d{1,2}\b'

或perl

echo "May 5 2014" | perl -lanE 'say $F[1]'

作为好奇心

echo "May 5 2014" | xargs -n1 | head -2 | tail -1
echo "May 5 2014" | xargs -n1 | sed -n 2p
echo "May 5 2014" | xargs -n1 | egrep '^[0-9]{1,2}$'

最后,纯粹的bash解决方案,无需启动任何外部命令

aaa="May 5 2014"
[[ $aaa =~ (.*)[[:space:]](.*)[[:space:]](.*) ]] && echo ${BASH_REMATCH[2]}

aaa="May 5 2014"
re="(.*) (.*) (.*)"
[[ $aaa =~ $re ]] && echo ${BASH_REMATCH[2]}

修改

因为Keith Reynolds要求一些基准,我测试了以下脚本。使用time并不是完美的基准测试工具,但可以提供一些见解。

  • 每个测试输出结果的N倍(由wc计算的数量)
  • 注意,外部命令只执行10_000次,而纯bash解决方案执行100_000次

这是脚本:

xbench_with_read() {
    let i=$1; while ((i--)); do
        read _ day _ <<< 'May 5 2014'
        echo $day
    done
}

xbench_regex_3x_assign() {
    let i=$1; while ((i--)); do
        aaa="May 5 2014"
        re="(.*) (.*) (.*)"
        [[ $aaa =~ $re ]] && month="${BASH_REMATCH[1]}" && day="${BASH_REMATCH[2]}" && year="${BASH_REMATCH[3]}" && echo "$day"
    done
}

xbench_regex_1x_assign() {
    let i=$1; while ((i--)); do
        aaa="May 5 2014"
        re="(.*) (.*) (.*)"
        [[ $aaa =~ $re ]] && day=${BASH_REMATCH[2]} && echo "$day"
    done
}

xbench_var_expansion() {
    let i=$1; while ((i--)); do
        s="May 5 2014"
        t="${s#* }"
        echo "${t% *}"
    done
}

xbench_ext_cut() {
    let i=$1; while ((i--)); do
        echo "May 5 2014" | cut -d' ' -f2
    done
}

xbench_ext_grep() {
    let i=$1; while ((i--)); do
        echo "May 5 2014" | grep -oP '\b\d{1,2}\b'
    done
}

xbench_ext_sed() {
    let i=$1; while ((i--)); do
        echo "May 5 2014" | sed 's/\(.*\) \(.*\) \(.*\)/\2/'
    done
}

xbench_ext_xargs() {
    let i=$1; while ((i--)); do
        echo "May 5 2014" | xargs -n1 | sed -n 2p
    done
}

title() {
    echo '~ -'$___{1..20} '~' >&2
    echo "Timing $1 $2 times" >&2
}

for script in $(compgen -A function | grep xbench)
do
    cnt=100000
    #external programs run 10x less times
    [[ $script =~ _ext_ ]] && cnt=$(( $cnt / 10 ))
    title $script $cnt
    time $script $cnt | wc -l
done

以下是原始结果:

~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_ext_cut 10000 times
   10000

real    0m37.752s
user    0m14.587s
sys 0m25.723s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_ext_grep 10000 times
   10000

real    1m35.570s
user    0m21.778s
sys 0m34.524s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_ext_sed 10000 times
   10000

real    0m41.628s
user    0m15.310s
sys 0m26.422s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_ext_xargs 10000 times
   10000

real    1m42.235s
user    0m46.601s
sys 1m11.238s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_regex_1x_assign 100000 times
  100000

real    0m11.215s
user    0m8.784s
sys 0m0.907s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_regex_3x_assign 100000 times
  100000

real    0m14.669s
user    0m12.419s
sys 0m1.027s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_var_expansion 100000 times
  100000

real    0m5.148s
user    0m4.658s
sys 0m0.788s
~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~ - ~
Timing xbench_with_read 100000 times
  100000

real    0m27.700s
user    0m6.279s
sys 0m19.724s

按实际执行时间排序

纯bash解决方案100_000次

  1. xbench_var_expansion - real 0m5.148s - 5.2 sec
  2. xbench_regex_1x_assign - real 0m11.215s - 11.2 sec
  3. xbench_regex_3x_assign - real 0m14.669s - 14.7 sec
  4. xbench_with_read - 真正的0m27.700s - 27.7
  5. 这里没什么惊喜 - 变量扩展只是最快的解决方案。

    外部程序只有10_000次

    1. xbench_ext_cut - real 0m37.752s - 37.8 sec
    2. xbench_ext_sed - real 0m41.628s - 41.6 sec
    3. xbench_ext_grep - 真实1分35.570秒 - 95.6
    4. xbench_ext_xargs - real 1m42.235s - 102.2 sec
    5. 这里有两个惊喜(至少对我而言):

      • grep解决方案是sed
      • 的2倍诈骗者
      • xargs(好奇心解决方案)仅略慢于grep

      Enviromnent:

      $ uname -a
      Darwin marvin.local 13.1.0 Darwin Kernel Version 13.1.0: Thu Jan 16 19:40:37 PST 2014; root:xnu-2422.90.20~2/RELEASE_X86_64 x86_64
      
      $ LC_ALL=C bash --version
      GNU bash, version 4.2.45(2)-release (i386-apple-darwin13.0.0)
      Copyright (C) 2011 Free Software Foundation, Inc.
      License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
      

答案 4 :(得分:0)

使用awk:

echo "May 5 2014" | awk '{print $2}'

答案 5 :(得分:0)

您可以使用bash substring expansion并应用偏移量(:4)和长度(:1)值。只需在字符串格式发生变化的情况下调整偏移量和长度值。

以下是一个例子:

$ date_format="May 5 2014"
$ echo "${date_format:4:1}"
5

$ date_format="2014 May 5"
$ echo "${date_format: -1:1}"    # <- Watch that space before the negative value
5

$ date_format="5 May 2014"
$ echo "${date_format:0:1}"
5
相关问题