Использование регулярных выражений в оболочке script

Каков правильный способ синтаксического анализа строки с использованием регулярных выражений в оболочке linux script? Я написал следующий script, чтобы напечатать мой SO rep на консоли, используя curl и sed (не только потому, что я безупречен - я пытаюсь изучить некоторые сценарии оболочки и регулярное выражение перед переключением на 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 &amp, # 9679;\u003c/span\u003e\u003cspan class=\" badgecount \ "\u003e1\u003c/span\u003e\u003c/span\u003e" }

Я думаю, мои вопросы:

  • Каков правильный способ синтаксического анализа строки с использованием регулярных выражений в оболочке linux script?
  • Правильно ли использовать sed здесь?
  • Можно ли это сделать с помощью grep?
  • Есть ли какая-нибудь другая команда, которая станет более легкой/подходящей?

Ответ 1

Команда 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 до одного (вы можете дать несколько команд с помощью -e).

Ответ 2

Вам может быть интересно использовать Perl для таких задач. В качестве демонстрации здесь представлен Perl script, который печатает нужное число:

#!/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";

В этом script вам необходимо установить модуль JSON, который вы можете сделать только с помощью команды cpan JSON.

Ответ 3

Для работы с JSON в оболочке script используйте jsawk, который как awk, но для JSON.

json=$(curl -s http://stackoverflow.com/users/flair/165297.json)
echo $json | jsawk 'return this.reputation' # 2,747

Ответ 4

Мое предложение:

$ 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 для сохранения последней или первой строки файла

Ответ 5

sed подходит, но вы создадите новый процесс для каждого используемого sed (который может быть слишком тяжелым в более сложных сценариях). grep не подходит. Это инструмент поиска, который использует регулярные выражения для поиска интересующих строк.

Perl является одним из подходящих решений здесь, являющимся языком сценариев оболочки с мощными функциями регулярного выражения. Он будет делать все, что вам нужно, не разворачивая отдельные процессы (в отличие от обычных сценариев оболочки Unix) и имеет огромную библиотеку дополнительных функций.

Ответ 6

Вы можете сделать это с помощью grep. Существует -o переключатель в grep witch extract, только соответствующая строка не целая строка.

$ echo $json | grep -o '"reputation":"[0-9,]\+"' | grep -o '[0-9,]\+'
2,747

Ответ 7

1) Каков правильный способ анализа строки с использованием регулярных выражений в оболочке linux script?

Инструменты, которые включают возможности регулярного выражения, включают 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

Ответ 8

sed - это абсолютно правильная команда для вашей задачи, но она может быть не единственной.

grep может оказаться полезным, но, как вы говорите, он печатает всю строку. Это наиболее полезно для фильтрации строк многострочного файла и отбрасывания строк, которые вам не нужны.

Эффективные сценарии оболочки могут использовать комбинацию команд (не только двух вы упомянули), используя таланты каждого из них.

Ответ 9

Слепо:

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)}'

Ответ 10

Вы можете использовать соответствующую библиотеку (как отмечали другие):

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"'

в зависимости от комбинации ОС/оболочки.

Ответ 11

Простой RegEx через оболочку

Не обращая внимания на конкретный код, о котором идет речь, могут появиться моменты, когда вы хотите выполнить быстрое замену regex-all из stdin в stdout с помощью оболочки простым способом, используя синтаксис строк, похожий на JavaScript.

Ниже приведены некоторые примеры для тех, кто ищет способ сделать это. Perl - лучшая ставка на Mac, так как в нем отсутствуют некоторые опции sed. Если вы хотите получить 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;