Извлеките соответствующую группу между слешами, используя sed, grep breaks для специальных символов - PullRequest
0 голосов
/ 08 сентября 2018

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

some_word/part_i_want_to_extract/optional_string
                                ^               
                                | from here is optional (including /)

Также часть, которую я хочу извлечь, должна содержать только буквенные цифры, _ и -.

примеров для sed:

echo 'test/alpha_numeric-9034/something' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numeric-9034

echo 'test/alpha_numer$ic-9034/something' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numer

echo 'test/alpha_numer$ic-9034' | sed -r 's|^.*(test)/([[:alnum:]_-]*)(/[[:alnum:]]*)?.*$|\2|' // returns alpha_numer

Я хочу, чтобы секунда / третье потерпели неудачу (ничего не должно возвращаться), как бы я это сделал?

те же примеры в grep:

echo 'test/alpha_numeric-9034/something' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numeric-9034

echo 'test/alpha_numer$ic-9034/something' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numer

echo 'test/alpha_numer$ic-9034' | grep -oP "^(test)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?" // returns alpha_numer

Также в grep использование $ в конце ничего не дает. Вот демонстрационная версия , которая работает для регулярных выражений на основе php, я не смог найти ни одного рабочего инструмента на Perl. Любая помощь будет благодарна.

Больше примеров того, что должно возвращаться

'test/alpha_numeric-9034/something' -> alpha_numeric-9034
'test/alpha_numer$ic-9034/something' -> should be nothing (since it has $)
'test/alpha_numeric-9034' -> alpha_numeric-9034
'test/QR-9034' -> QR-9034

Ответы [ 4 ]

0 голосов
/ 08 сентября 2018

Будьте понятны, просты, эффективны, надежны, переносимы и т. Д. И просто используйте awk:

$ awk -F'/' '($1=="test") && ($2~/^[[:alnum:]_-]+$/){print $2}' file
alpha_numeric-9034
alpha_numeric-9034
QR-9034

или

$ awk -F'/' '{print (($1=="test") && ($2~/^[[:alnum:]_-]+$/) ? $2 : "")}' file
alpha_numeric-9034

alpha_numeric-9034
QR-9034

в зависимости от того, хотите ли вы ничего или вывод пустой строки при несовпадающем вводе.

0 голосов
/ 08 сентября 2018

Вот это awk

awk -F\/ 'NF>2 && $2 ~ /^[0-9a-zA-Z_-]+$/ {print $2}'
alpha_numeric-9034

Или это:

awk -F\/ 'NF>2 && $2 ~ /^[[:alnum:]_-]+$/ {print $2}'
alpha_numeric-9034

Или это:

awk -F\/ 'NF>2 && $2 !~ /[!@#$%^&*()+=~]/ {print $2}'
alpha_numeric-9034

Он проверяет, есть ли минимум 2 /, и печатает первые данные между //, если они содержат правильное значение
PS Я бы сказал, что - и _ это специальные символы

0 голосов
/ 08 сентября 2018

Я собираюсь добавить решение sed, однако sed не будет идеальным вариантом:

sed -r 's~^test/([[:alnum:]_-]*)(/|$)|.~\1~g'

Это ищет входную строку, начинающуюся с test, следующую за шаблоном /[[:alnum:]_-]* до / или концом входной строки. Другая сторона чередования - это период, который должен совпадать при неудаче. Флаг g также включен. (Я не уверен, почему sed -r 's~^test/([[:alnum:]_-]*)(/|$)|.*~\1~' не сработало. Если у кого-то есть подсказка, он / она приветствуется.)

Контрольные примеры:

$ echo 'test/al_num-0$' | ...

$ echo 'test/al_num-0' | ...
al_num-0
$ echo 'test/al_num-0/something' | ...
al_num-0
0 голосов
/ 08 сентября 2018

Обновление Добавлена ​​версия для редактирования последнего вопроса.


С Perl (как помечено), используя ваши эхо-строки данных в файле data.txt

perl -wnE'@m = m{^test/([\w-]+)(?=/)}g; say "@m" if @m' data.txt

При этом печатается только alpha_numeric-9034 с первой строки.

Я использую [\w-], перейдите к классу POSIX , [[:alnum:]_-]если хочешь.Другой вариант - перечислить запрещенные символы вместе с / в отрицательном классе, [^/...].

Код захватывает разрешенные символы после ^test/ до следующего /, используя положительный прогнозутверждать, что / есть.Происхождение, представляющее собой « утверждение нулевой ширины », не использует этот слеш, иначе это не удастся с более чем двумя слешами.

Предполагая путь в качестве цели, код захватывается между слешамитолько когда что-то есть, отбрасывая //, но все еще совпадая с линией.Если вы хотите, чтобы «ничего» между косыми чертами, измените квантификатор + на *, и вы получите пустую строку для этой пары //.

Она также работает с любым количеством слешей, извлекаячто находится между последовательными.Протестировано добавлением строки test/first/yet/more/end в файл, использованный выше, для

alpha_numeric-9034
first yet more

Примечание Последнее редактирование вопроса позволяет test/QR-9034, поэтому без вторая косая черта.Это противоречит первоначальному утверждению и явным ранним разъяснениям, а решения выше (Perl) и ниже (bash) не были предназначены для него и не будут работать в этом случае.

Однако, это (намного) проще, если мы можем иметь не более двух слешей

perl -wnE'say $1 if m{^test/([\w-]+)/?}' data.txt

Это соответствует тому, что следует ^test/, как указано выше, до следующего необязательный (?) косая черта.


Если это действительно касается парсинга путей, пожалуйста, используйте модули, которые делают именно это.


Разъяснено, что цельэто делается в bash с «минимальной поддержкой инструмента / языка» .Тогда это можно сделать прямо в bash.Это не будет кратким, как в Perl, но другие инструменты не используются.В одну сторону

#!/bin/bash

string='test/one/two/end'

# Build array of fields using / for the separator
IFS='/' read -ra ary <<< "$string"

# Note: don't know how the presumed bash script is organized
# Use checks below (or alternatives) for flow control you need

# Check for non :alnum: characters. Iterating is a bit slow but clear
for i in "${ary[@]}"; do
    if [[ "$i" =~ [^[:alnum:]] ]]; then
        echo "Element $i has non-alnum"
        has_special=1
        break
    fi  
done

if [[ ${#ary[@]} -le 1 || ${ary[0]} != "test" || $has_special ]]; then
    echo "No match"
else
    # Remove first and last elements
    unset 'ary[${#ary[@]}-1]'
    unset 'ary[${ary[0]}]'

    echo "${ary[@]}"
 fi

Это печатает строку: one two (или No match, если строка / $IFS изменена так, чтобы потерпеть неудачу)

Все части вышеперечисленного могут бытьсделано другими способами.Комментарии

  • Проверки (не для alnum, test/ и общего соответствия) даются просто, так как не указано, какое управление потоком подходит.Реструктуризация для лучшей организации программы

  • Итерация, используемая для проверки элементов массива, ясна, но медленна;Есть и другие способы.Если есть интерес к этому, пожалуйста, дайте мне знать, и я отредактирую.Кроме того, сама строка может быть проверена, но тогда мы не можем (просто) использовать :alnum:, поскольку она содержит /

  • readпрактически самый эффективный способ разбить строку разделителями на массив, без разветвлений, внешних инструментов или ресурсов

  • На более новой версии bash (4.3+?) вы можете просто сделать unset 'array[-1]' etc

  • В последнем bash вышеуказанные изменения $IFS только в пределах текущей команды

  • Если не было совпадений, вся строка находится впервый элемент ary, поэтому я проверяю размер

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...