如何将Shell脚本中的部分字符串提取为变量

时间:2015-07-29 19:42:14

标签: regex shell sh

我正在尝试在sh。

中执行以下操作

这是我的档案:

synchronized

如何将4个数字拉成4个不同的变量?我现在花了大约一个小时在sed和awk手册页上,我正在旋转我的车轮。

4 个答案:

答案 0 :(得分:2)

采用我之前的回答来使用@chepner建议的heredoc方法:

read run failures errors skipped <<EOF
$(grep -E '^Tests run: ' <file.in | tr -d -C '[:digit:][:space:]')
EOF

echo "Tests run: $run"
echo "Failures: $failures"
echo "Errors: $errors"
echo "Skipped: $skipped"

或者(把它放到shell函数中以避免覆盖&#34; $ @&#34;在脚本的持续时间内):

unset IFS # assert default values
set -- $(grep -E '^Tests run: ' <in.file | tr -d -C '[:digit:][:space:]')
run=$1; failures=$2; errors=$3; skipped=$4

请注意,这只是安全的,因为当以这种方式运行时,tr的输出中不会出现浮点字符; set -- $(something)通常可以更好地避免使用。

现在,如果你是为bash而不是POSIX sh编写的,你可以在shell内部执行正则表达式匹配(假设在下面你的输入文件相对较短):

#!/bin/bash
re='Tests run: ([[:digit:]]+), Failures: ([[:digit:]]+), Errors: ([[:digit:]]+), Skipped: ([[:digit:]]+)'
while IFS= read -r line; do
  if [[ $line =~ $re ]]; then
    run=${BASH_REMATCH[1]}
    failed=${BASH_REMATCH[2]}
    errors=${BASH_REMATCH[3]}
    skipped=${BASH_REMATCH[4]}
  fi
done <file.in

如果你的输入文件很短,那么用grep预过滤它可能会更有效率,从而将最后一行更改为:

done < <(egrep -E '^Tests run: ' <file.in)

答案 1 :(得分:1)

根据输入文件的格式,您可以在此处的文档中捕获grep的输出,然后将其与read分成四个部分进行后处理。

IFS=, read part1 part2 part3 part4 <<EOF
$(grep '^Tests run' input.txt)
EOF

然后从每个部分中删除不需要的前缀。

run=${part1#*: }
failures=${part2#*: }
errors=${part3#*: }
skipped=${part4#*: }

答案 2 :(得分:0)

假设文件中只有一行以Tests run:开头,并且该文件名为foo.txt,则以下命令将创建4个可以使用的shell变量:

eval $(awk 'BEGIN{ FS="(: |,)" }; /^Tests run/{ print "TOTAL=" $2 "\nFAIL=" $4 "\nERROR=" $6 "\nSKIP=" $8 }' foo.txt); echo $TOTAL; echo $SKIP; echo $ERROR; echo $FAIL

echo $TOTAL; echo $SKIP; echo $ERROR; echo $FAIL只是为了证明环境变量存在且可以使用。

更可读的awk脚本是:

BEGIN { FS = "(: |,)" }

/^Tests run/ {
    print "TOTAL=" $2 "\nFAIL=" $4 "\nERROR=" $6 "\nSKIP=" $8
}

FS = "(: |,)"告诉awk考虑&#34; :&#34;或&#34; ,&#34;作为字段分隔符。

然后eval命令将作为命令读取awk脚本的结果,并因此创建4个环境变量。

注意:由于使用了eval,您必须信任foo.txt文件的内容,因为可以伪造以Tests run:开头的行之后可以有命令。

您可以通过在awk脚本中使用更严格的正则表达式来改善这一点:/^Tests run: \d+, Failures: \d+, Errors: \d+, Skipped: \d+$/

完整的命令将是:

eval $(awk 'BEGIN{ FS="(: |,)" }; /^Tests run: \d+, Failures: \d+, Errors: \d+, Skipped: \d+$/{ print "TOTAL=" $2 "\nFAIL=" $4 "\nERROR=" $6 "\nSKIP=" $8 }' foo.txt); echo $TOTAL; echo $SKIP; echo $ERROR; echo $FAIL

答案 3 :(得分:-1)

有更短的版本,但这一个“显示”每一步。

#!/bin/bash
declare -a arr=`grep 'Tests ' a | awk -F',' '{print $1 "\n" $2 "\n" $3 "\n" $4}' | sed 's/ //g' | awk -F':' '{print $2}'`
echo $arr
for var in $arr
do
    echo $var
done