Разбор регулярного выражения s/[^ ]* \([^ ]*\).*/\1/ Linux

Структура команды sed:

  • s/ — команда substitute (замена)
  • [^ ]* \([^ ]*\).* — шаблон поиска (pattern)
  • / — разделитель
  • \1 — замена (replacement)
  • / — завершающий разделитель

Пошаговый разбор шаблона поиска:

1. [^ ]*

text

[^ ] - класс символов, НЕ пробел (любой символ кроме пробела)
*     - ноль или более повторений предыдущего элемента

Вместе: [^ ]* = «ноль или более любых символов, кроме пробела»

  • В строке «Hello World Test» это соответствует «Hello»

2.  (пробел)

text

  - обычный пробел (литерал)
  • Соответствует пробелу между «Hello» и «World»

3. \( и \)

text

\( ... \) - захватывающая группа (группировка с запоминанием)
  • В sed скобки экранируются обратным слэшем для создания группы
  • Содержимое группы будет доступно как \1

4. [^ ]* внутри группы

text

Внутри \( ... \): [^ ]*
  • То же самое: «ноль или более любых символов кроме пробела»
  • В нашем случае соответствует «World»
  • Это группа 1 (запомненная часть)

5. .*

text

.   - любой одиночный символ (кроме перевода строки)
*   - ноль или более повторений

Вместе: .* = «ноль или более любых символов»

  • В строке соответствует » Test» (пробел + «Test»)

Итоговое соответствие для «Hello World Test»:

text

Шаблон: [^ ]*  \([^ ]*\) .*
          |     |   |     |
          |     |   |     " Test" (остаток строки)
          |     |   "World" (группа 1)
          |     пробел
          "Hello" (первое слово)

Полное соответствие: Hello World Test (вся строка)


Разбор части замены /\\1/:

  • \1 — ссылка на первую захватывающую группу (то, что в первых \(...\))
  • В нашем случае группа 1 содержит World

Как это работает:

  1. Находится вся строка по шаблону
  2. Из найденного извлекается группа 1 («World»)
  3. Вся найденная строка заменяется на содержимое группы 1
  4. Результат: «World»

Эквиваленты в других инструментах:

В awk (проще):

bash

echo "Hello World Test" | awk '{print $2}'

В sed с другими подходами:

bash

# Удалить все до первого пробела и после второго слова
echo "Hello World Test" | sed 's/^[^ ]* //; s/ .*//'

# Только второе слово
echo "Hello World Test" | sed -E 's/^[^ ]+ ([^ ]+).*/\1/'

Визуализация процесса:

text

Исходная строка: "Hello World Test"
Шаблон поиска:   /[^ ]* \([^ ]*\).*/
                 ├─────┤│├─────┤├─┤
                 │     │││     ││ │
                 │     │││     ││ └─ .* = " Test"
                 │     │││     ││
                 │     │││     │└─ \) конец группы 1
                 │     │││     │
                 │     ││└─────┴─ [^ ]* = "World" (группа 1)
                 │     ││
                 │     │└─ \( начало группы 1
                 │     │
                 │     └─ пробел
                 │
                 └─ [^ ]* = "Hello"

Найдено: "Hello World Test"
Группа 1: "World"

Замена: s/найденное/группа1/
Результат: "World"

Этот паттерн находит первое слово + пробел + второе слово + всё остальное, и заменяет всё найденное на только второе слово.

подставив для тестов группу 2 или 0 то не получил hello и не получил, вот разбираем как

Ключевой момент:

В sed только то, что в \(...\) становится доступным как \1\2 и т.д.

В нашем шаблоне: [^ ]* \([^ ]*\).*

  • \([^ ]*\) — это первая и единственная группа
  • [^ ]* перед пробелом НЕ в скобках — не группа
  • .* в конце НЕ в скобках — не группа

Ваши тесты объясняются:

1. \1 — работает

bash

echo "Hello World Test" | sed 's/[^ ]* \([^ ]*\).*/\1/'
# Вывод: World

Потому что \1 ссылается на \([^ ]*\), что соответствует «World»

2. \2 — ошибка

bash

echo "Hello World Test" | sed 's/[^ ]* \([^ ]*\).*/\2/'
# Ошибка: invalid reference \2

Потому что у нас только одна группа (\1). Группы \2 не существует.

3. \0 — особая история

bash

echo "Hello World Test" | sed 's/[^ ]* \([^ ]*\).*/\0/'
# Вывод: Hello World Test

\0 в sed — это вся совпавшая строка, а не группа!
То есть шаблон нашел «Hello World Test», и \0 ссылается на всё это.

Чтобы получить Hello или Test, нужно изменить шаблон:

Получить «Hello» (первое слово):

bash

# Добавляем группу для первого слова
echo "Hello World Test" | sed 's/\([^ ]*\) \([^ ]*\).*/\1/'
# или
echo "Hello World Test" | sed 's/^\([^ ]*\).*/\1/'
# Вывод: Hello

Получить «Test» (третье слово):

bash

# Группа для третьего слова
echo "Hello World Test" | sed 's/[^ ]* [^ ]* \([^ ]*\)/\1/'
# или
echo "Hello World Test" | sed 's/.* \([^ ]*\)$/\1/'  # последнее слово
# Вывод: Test

Пример с несколькими группами:

bash

echo "Hello World Test" | sed 's/\([^ ]*\) \([^ ]*\) \([^ ]*\)/\1-\2-\3/'
# Вывод: Hello-World-Test
# Группы: \1=Hello, \2=World, \3=Test

echo "Hello World Test" | sed 's/\([^ ]*\) \([^ ]*\) \([^ ]*\)/\2/'
# Вывод: World  (вторая группа)

echo "Hello World Test" | sed 's/\([^ ]*\) \([^ ]*\) \([^ ]*\)/\3/'
# Вывод: Test   (третья группа)

Таблица групп для разных шаблонов:

Шаблон sedГруппыЧто доступно
[^ ]* \([^ ]*\).*\([^ ]*\) = 1 группа\1 = «World»
\([^ ]*\) [^ ]* [^ ]*\([^ ]*\) = 1 группа\1 = «Hello»
[^ ]* [^ ]* \([^ ]*\)\([^ ]*\) = 1 группа\1 = «Test»
\([^ ]*\) \([^ ]*\) \([^ ]*\)3 группы\1=»Hello», \2=»World», \3=»Test»

Правила sed для групп:

  1. Только \(...\) создают нумерованные группы (\1\2, …)
  2. \0 — всегда вся совпавшая строка (даже без групп)
  3. Нумерация групп идет по порядку открывающих \(
  4. Максимум 9 групп (\1\9) в старом sed

Ваши примеры с объяснением:

bash

# Исходный: [^ ]* \([^ ]*\).*
# Только World в скобках -> только \1

# Чтобы получить Hello:
sed 's/\([^ ]*\) .*/\1/'  # Hello в скобках

# Чтобы получить Test:
sed 's/.* \([^ ]*\)$/\1/'  # Test в скобках, .* до пробела

# Чтобы получить всё кроме World:
sed 's/\([^ ]*\) \([^ ]*\) \(.*\)/\1 \3/'  # Hello Test

Практическое правило:

Если хотите что-то извлечь с помощью \номер в sed — оберните это в \(...\) в шаблоне поиска!

а что бы было без скобок , а какой в этих скобках еще может быть символ или символы и что они означают и может важен порядок этих символов [^ ]* text [^ ] — класс символов, НЕ пробел (любой символ кроме пробела)

 разберем что внутри [ ] и что означает конструкция без скобок.

1. Без скобок \(...\)

Без захватывающих скобок:

bash

echo "Hello World Test" | sed 's/[^ ]* [^ ]* .*/REPLACEMENT/'
# Находит: "Hello World Test"
# Заменяет ВСЮ строку на "REPLACEMENT"

Без \(...\) вы не можете использовать \1\2 и т.д.
Можете использовать только специальные символы:

  • & — вся найденная строка
  • \0 — то же самое что & (вся найденная строка)

Пример:

bash

echo "Hello World Test" | sed 's/[^ ]* [^ ]* .*/& &/'
# Вывод: Hello World Test Hello World Test
# & = вся найденная строка

echo "Hello World Test" | sed 's/[^ ]* [^ ]* .*/Found: &/'
# Вывод: Found: Hello World Test

2. Что внутри [^ ] — это КЛАСС СИМВОЛОВ

Конструкция [...] означает «любой ОДИН символ из перечисленных»

Варианты классов символов:

Базовые:

bash

[abc]     # любой из символов: a, b, или c
[a-z]     # любой символ от a до z
[A-Z]     # любой символ от A до Z
[0-9]     # любая цифра
[a-zA-Z]  # любая буква (латиница)

Специальные внутри [ ]:

bash

[^abc]    # НЕ a, НЕ b, НЕ c (любой символ кроме этих)
[-.]      # дефис или точка (дефис в начале/конце не диапазон)
[\]\[]    # экранированные квадратные скобки
[.*+?]    # метасимволы теряют спец. значение внутри [ ]

Ваш случай [^ ]:

bash

[^ ]      # любой символ КРОМЕ пробела
          # ^ внутри [ ] означает отрицание/инверсию
          # пробел - просто символ пробела

3. Что означает [^ ]*:

Разберем по частям:

text

[^ ]  - класс: "любой один символ кроме пробела"
*     - квантификатор: "ноль или более повторений предыдущего"

Вместе: [^ ]* = «ноль или более любых символов кроме пробела»

Примеры соответствия:

bash

""        # соответствует (ноль символов)
"a"       # соответствует
"abc"     # соответствует  
"123"     # соответствует
"a-b"     # соответствует
"a b"     # НЕ соответствует (есть пробел!)

4. Порядок символов ВНУТРИ [ ]

НЕ важен для большинства символов:

bash

[abc] ≡ [bac] ≡ [cba]  # одинаково

ВАЖЕН для:

  1. Диапазонов a-z0-9:bash[a-z] # от a до z (корректно) [z-a] # ошибка или undefined behavior [0-9] # от 0 до 9 (корректно) [9-0] # ошибка
  2. Специальных символов:bash[]-] # ] или — (специальные символы в определенных позициях) [^] # если ^ не первый символ — просто каретка [^-] # ^ первый = отрицание, — не первый = просто дефис

Правила позиционирования внутри [ ]:

text

[^abc]    # ^ ПЕРВЫЙ символ = отрицание класса
[a^bc]    # ^ НЕ первый = просто символ каретки
[a-c]     # - между символами = диапазон  
[-ac]     # - первый = просто дефис
[ac-]     # - последний = просто дефис
[a\-c]    # \- экранированный = просто дефис

5. Примеры с пробелом в классе символов:

bash

# Пробел как обычный символ внутри []
[abc ]      # a, b, c, или пробел
[a-z ]      # любая буква ИЛИ пробел
[^ ]        # НЕ пробел (ваш случай)
[^a-z ]     # НЕ буква и НЕ пробел
[[:space:]] # POSIX класс: любой пробельный символ (пробел, таб, и т.д.)

6. Сравнение: со скобками \(...\) и без

С захватывающими скобками (группа):

bash

echo "Hello World" | sed 's/\([^ ]*\) \([^ ]*\)/\2 \1/'
# Находит: "Hello World"
# Группа 1 (\1): "Hello"
# Группа 2 (\2): "World"
# Замена: "\2 \1" = "World Hello"
# Результат: "World Hello"

Без захватывающих скобок:

bash

echo "Hello World" | sed 's/[^ ]* [^ ]*/Replacement/'
# Находит: "Hello World"
# Нет групп
# Заменяет на "Replacement"
# Результат: "Replacement"

# Или с & (вся найденная строка):
echo "Hello World" | sed 's/[^ ]* [^ ]*/Before & After/'
# Результат: "Before Hello World After"

7. Практическая разница на примере:

Исходная строка: "John 25 Engineer"

С группами (можно переставлять части):

bash

echo "John 25 Engineer" | sed 's/\([^ ]*\) \([^ ]*\) \([^ ]*\)/\3: \1 (\2 years)/'
# Вывод: "Engineer: John (25 years)"
# \1=John, \2=25, \3=Engineer

Без групп (только вся строка):

bash

echo "John 25 Engineer" | sed 's/[^ ]* [^ ]* [^ ]*/& - processed/'
# Вывод: "John 25 Engineer - processed"
# & = "John 25 Engineer"

8. Важные отличия:

АспектБез \(...\)С \(...\)
Можно ссылатьсятолько на всю строку (& или \0)на части (\1\2, …)
Использованиепростая заменаизвлечение, перестановка, сложные замены
Памятьне запоминает частизапоминает части для повторного использования
Пример заменыs/pattern/replacement/s/\(part1\) \(part2\)/\2 \1/

9. Еще пример с [^ ]*:

bash

# Файл с данными:
# Name Age City
# John 25 NY
# Alice 30 LA

# Без групп - просто удалить первые 2 слова:
sed 's/[^ ]* [^ ]* //' file.txt
# Вывод:
# City
# NY  
# LA

# С группами - извлечь только возраст (второе слово):
sed 's/[^ ]* \([^ ]*\) [^ ]*/\1/' file.txt
# Вывод:
# Age
# 25
# 30

Ключевой вывод:
[^ ]* = «слово» (последовательность непробельных символов)
\(...\) = «запомнить эту часть для использования в замене»
Без \(...\) вы не можете манипулировать отдельными частями найденного текста.

Задачки от простых к сложным

Фразы-ключи для запоминания:

  1. [^ ] = «НЕ пробел» (один любой символ кроме пробела)
  2. [^ ]* = «слово» (ноль или больше не-пробелов)
  3. \(...\) = «запомни это» (создает группу)
  4. \1\2 = «вспомни 1-е, 2-е» (ссылки на группы)
  5. & или \0 = «всё найденное» (вся совпавшая строка)
  6. [abc] = «или a, или b, или c» (класс символов)
  7. [a-z] = «от a до z» (диапазон символов)
  8. [^abc] = «НЕ a, НЕ b, НЕ c» (отрицание класса)

ЗАДАЧИ

Уровень 1: Базовый (понимание [^ ]*)

Задача 1.1:
Из строки «apple banana cherry» извлеките только первое слово

bash

echo "apple banana cherry" | sed 's/______/______/'
# Ожидаемый вывод: apple

Задача 1.2:
Из строки «John Doe» удалите первое слово

bash

echo "John Doe" | sed 's/______/______/'
# Ожидаемый вывод: Doe

Задача 1.3:
Замените всё после первого слова на «…»

bash

echo "Hello beautiful world" | sed 's/______/______/'
# Ожидаемый вывод: Hello...

Уровень 2: Группы \(...\)

Задача 2.1:
Поменяйте местами «cat» и «dog» в строке «cat dog»

bash

echo "cat dog" | sed 's/______/______/'
# Ожидаемый вывод: dog cat

Задача 2.2:
Из «Name: Alice Age: 25» извлеките только возраст

bash

echo "Name: Alice Age: 25" | sed 's/______/______/'
# Ожидаемый вывод: 25

Задача 2.3:
Преобразуйте «file.txt» в «txt.file»

bash

echo "file.txt" | sed 's/______/______/'
# Ожидаемый вывод: txt.file

Уровень 3: Классы символов [ ]

Задача 3.1:
Удалите все цифры из строки

bash

echo "abc123def456" | sed 's/______/______/'
# Ожидаемый вывод: abcdef

Задача 3.2:
Оставьте только буквы (удалите всё кроме a-z, A-Z)

bash

echo "Hello123 World!@#" | sed 's/______/______/g'
# Ожидаемый вывод: HelloWorld

Задача 3.3:
Из «ip: 192.168.1.1» извлеките только IP-адрес

bash

echo "ip: 192.168.1.1" | sed 's/______/______/'
# Ожидаемый вывод: 192.168.1.1

Уровень 4: Комбинирование

Задача 4.1:
Из «user=admin role=super» извлеките значение role

bash

echo "user=admin role=super" | sed 's/______/______/'
# Ожидаемый вывод: super

Задача 4.2:
Преобразуйте дату «2024-05-20» в «20/05/2024»

bash

echo "2024-05-20" | sed 's/______/______/'
# Ожидаемый вывод: 20/05/2024

Задача 4.3:
Из «ERROR 404: Not Found» извлеките код ошибки

bash

echo "ERROR 404: Not Found" | sed 's/______/______/'
# Ожидаемый вывод: 404

Уровень 5: Сложные (реальные кейсы)

Задача 5.1:
Из лог-строки извлеките IP-адрес

bash

echo "192.168.1.1 - admin [20/May/2024:10:30:45] GET /index.html" | sed 's/______/______/'
# Ожидаемый вывод: 192.168.1.1

Задача 5.2:
Поменяйте формат имени «Doe, John» → «John Doe»

bash

echo "Doe, John" | sed 's/______/______/'
# Ожидаемый вывод: John Doe

Задача 5.3:
Извлеките расширение файла из полного пути

bash

echo "/home/user/docs/report.pdf" | sed 's/______/______/'
# Ожидаемый вывод: pdf

Уровень 6: Экспертный

Задача 6.1:
Разделить строку на три группы и вывести в обратном порядке

bash

echo "one two three" | sed 's/______/______/'
# Ожидаемый вывод: three two one

Задача 6.2:
Из «CPU: 45%, RAM: 78%, DISK: 23%» извлеките все три значения

bash

echo "CPU: 45%, RAM: 78%, DISK: 23%" | sed 's/______/______/'
# Ожидаемый вывод: 45 78 23

Задача 6.3:
Удалить всё внутри круглых скобок (включая скобки)

bash

echo "Hello (remove this) World (and this too)" | sed 's/______/______/g'
# Ожидаемый вывод: Hello  World 

ПОДСКАЗКИ ДЛЯ САМОПРОВЕРКИ:

Для уровня 1:

  • Используйте [^ ]* для «слова»
  • s/pattern/replacement/ без групп

Для уровня 2:

  • Оберните то, что нужно сохранить, в \(...\)
  • Используйте \1\2 для ссылок

Для уровня 3:

  • [0-9] для цифр
  • [a-zA-Z] для букв
  • [^ ] для «не пробел»
  • Добавьте g в конце для глобальной замены

Для уровня 4:

  • Комбинируйте [^ ]* с группами
  • Используйте конкретные символы как разделители

Для уровня 5:

  • Учитывайте специфичные форматы (IP, даты, пути)
  • Используйте более точные шаблоны

Для уровня 6:

  • Множественные группы \(...\)
  • Специальные символы: \. для точки, \s* для пробелов
  • Флаги вроде g для повторной замены

ПРИМЕР РЕШЕНИЯ (чтобы понять формат):

Простая задача: Из «Hello World» получить «World»

bash

# Решение:
echo "Hello World" | sed 's/[^ ]* \([^ ]*\)/\1/'
# или
echo "Hello World" | sed 's/^[^ ]* //'

Попробуйте

Оставьте комментарий