在linux shell脚本中使用正则表达式解析字符串的正确方法是什么?我编写了以下脚本,使用curl
和sed
在控制台上打印我的SO代表(不仅仅是因为我反复疯狂 - 我试图在切换到之前学习一些shell脚本和正则表达式的Linux)。
json=$(curl -s http://stackoverflow.com/users/flair/165297.json)
echo $json | sed 's/.*"reputation":"\([0-9,]\{1,\}\)".*/\1/' | sed s/,//
但不知怎的,我觉得sed
不适合在这里使用。我听说grep
完全是关于正则表达式并且稍微探讨了一下。但显然它会在找到匹配时打印整行 - 我试图从单行文本中提取一个数字。这是我正在处理的字符串的缩小版本(由curl
返回)。
{“displayName”:“Amarghosh”,“声誉”:“2,737”,“badgeHtml”:“\ u003cspan title = \”1银徽章\“\ u003e \ u003cspan class = \”badge2 \“\ u003e& #9679; \ u003c / span \ u003e \ u003cspan class = \“badgecount \”\ u003e1 \ u003c / span \ u003e \ u003c / span \ u003e“}
我想我的问题是:
sed
在这里使用是正确的吗? grep
完成吗? 答案 0 :(得分:12)
grep
命令将从许多行中选择所需的行,但不会直接操作该行。为此,您在管道中使用sed
:
someCommand | grep 'Amarghosh' | sed -e 's/foo/bar/g'
或者,可以使用awk
(或perl
,如果可用)。在我看来,它是一个比sed
更强大的文本处理工具。
someCommand | awk '/Amarghosh/ { do something }'
对于简单的文本操作,只需坚持使用grep/sed
组合。如果您需要更复杂的处理,请前往awk
或perl
。
我的第一个想法就是使用:
echo '{"displayName":"Amarghosh","reputation":"2,737","badgeHtml"'
| sed -e 's/.*tion":"//' -e 's/".*//' -e 's/,//g'
将sed
进程的数量保持为1(您可以使用-e
提供多个命令)。
答案 1 :(得分:8)
您可能有兴趣将Perl用于此类任务。作为演示,这里是一个打印所需数字的Perl脚本:
#!/usr/local/bin/perl
use warnings;
use strict;
use LWP::Simple;
use JSON;
my $url = "http://stackoverflow.com/users/flair/165297.json";
my $flair = get ($url);
my $parsed = from_json ($flair);
print "$parsed->{reputation}\n";
此脚本要求您安装JSON模块,只需使用命令cpan JSON
即可完成。
答案 2 :(得分:5)
要在shell脚本中使用JSON,请使用{em>类似于awk的jsawk,但是对于JSON 。
json=$(curl -s http://stackoverflow.com/users/flair/165297.json)
echo $json | jsawk 'return this.reputation' # 2,747
答案 3 :(得分:3)
我的主张:
$ echo $json | sed 's/,//g;s/^.*reputation...\([0-9]*\).*$/\1/'
我在sed参数中放了两个命令:
s/,//g
用于删除所有逗号,特别是声誉值中包含的逗号。
s/^.*reputation...\([0-9]*\).*$/\1/
找到该行中的信誉值,并用该值替换整行。
在这种特殊情况下,我发现sed
提供了最紧凑的命令而不会降低可读性。
其他用于操作字符串的工具(不仅仅是正则表达式)包括:
grep
,awk
,perl
tr
用于替换字符cut
,paste
用于处理多列输入bash
本身及其丰富的$(...)
语法,用于访问变量tail
,head
用于保存文件的最后一行或第一行答案 4 :(得分:2)
sed
是合适的,但是您将为您使用的每个sed
生成一个新进程(在更复杂的情况下可能过于重量级)。 grep
并不合适。这是一个使用regexp查找感兴趣的行的搜索工具。
Perl是一个合适的解决方案,是一种具有强大正则表达式功能的shell脚本语言。它可以完成你需要的大部分工作而不会产生独立的进程(与普通的Unix shell脚本不同),并且拥有庞大的附加功能库。
答案 5 :(得分:2)
你可以用grep来做。 grep女巫提取中的-o开关只匹配字符串而不是整行。
$ echo $json | grep -o '"reputation":"[0-9,]\+"' | grep -o '[0-9,]\+'
2,747
答案 6 :(得分:2)
1)在linux shell脚本中使用正则表达式解析字符串的正确方法是什么?
包含正则表达式功能的工具包括sed,grep,awk,Perl,Python等等。即使是较新版本的Bash也具有正则表达式功能。您需要做的就是查找有关如何使用它们的文档。
2)在这里使用是正确的吗?
它可以,但不是必需的。
3)可以使用grep吗?
完成
是的,它可以。你将使用sed或其他方法构建类似的正则表达式。请注意,grep只是执行它所做的事情,如果你想修改任何文件,它将不会为你做。
4)是否还有其他命令更容易/更合适?
当然。正则表达式可以是强大的,但它不一定是每次使用的最佳工具。它还取决于“更容易/适当”的含义。 在regex上使用最小问题的另一种方法是使用字段/分隔符方法。你寻找可以“分裂”的模式。例如,在你的情况下(我已经下载了165297.json文件,而不是使用curl ..(但它是相同的)
awk 'BEGIN{
FS="reputation" # split on the word "reputation"
}
{
m=split($2,a,"\",\"") # field 2 will contain the value you want plus the rest
# Then split on ":" and save to array "a"
gsub(/[:\",]/,"",a[1]) # now, get rid of the redundant characters
print a[1]
}' 165297.json
输出:
$ ./shell.sh
2747
答案 7 :(得分:1)
sed
对您的任务来说是完全有效的命令,但它可能不是唯一的命令。
grep
也可能有用,但正如你所说它打印整行。它最有用的是过滤多行文件的行,并丢弃你不想要的行。
高效的shell脚本可以使用命令的组合(不仅仅是你提到的两个),利用每个命令的才能。
答案 8 :(得分:0)
盲目:
echo $json | awk -F\" '{print $8}'
类似(字段分隔符可以是正则表达式):
awk -F'{"|":"|","|"}' '{print $5}'
更聪明(查找密钥并打印其值):
awk -F'{"|":"|","|"}' '{for(i=2; i<=NF; i+=2) if ($i == "reputation") print $(i+1)}'
答案 9 :(得分:0)
您可以使用合适的库(如其他人所述):
E:\Home> perl -MLWP::Simple -MJSON -e "print from_json(get 'http://stackoverflow.com/users/flair/165297.json')->{reputation}"
或
$ perl -MLWP::Simple -MJSON -e 'print from_json(get "http://stackoverflow.com/users/flair/165297.json")->{reputation}, "\n"'
取决于OS / shell组合。
答案 10 :(得分:0)
忽略有问题的特定代码,有时你可能想要使用类似于JavaScript的字符串语法以简单的方式使用shell从stdin到stdout进行快速正则表达式替换。
下面是一些寻找方法的人的例子。由于缺少一些sed选项,Perl在Mac上是更好的选择。如果您想将stdin作为变量,可以使用MY_VAR=$(cat);
。
echo 'text' | perl -pe 's/search/replace/g'; # using perl
echo 'text' | sed -e 's/search/replace/g'; # using sed
这是一个自定义,可重用的正则表达式函数的示例。参数是源字符串(或 - 对于stdin),搜索,替换和选项。
regex() {
case "$#" in
( '0' ) exit 1 ;; ( '1' ) echo "$1"; exit 0 ;;
( '2' ) REP='' ;; ( '3' ) REP="$3"; OPT='' ;;
( * ) REP="$3"; OPT="$4" ;;
esac
TXT="$1"; SRCH="$2";
if [ "$1" = "--" ]; then [ ! -t 0 ] && read -r TXT; fi
echo "$TXT" | perl -pe 's/'"$SRCH"'/'"$REP"'/'"$OPT";
}
echo 'text' | regex -- search replace g;