Удалите определенные поля из строки

У меня есть следующие строки в файле:

Modified folders: html/project1/old/dev/vendor/symfony/yaml/Tests/bla.yml
Modified folders: html/port5/.DS_Store
Modified folders: html/trap/dev8/.DS_Store
Modified folders: html/bla3/test/appl/.DS_Store
Modified folders: html/bla4/pro1/app/bla/Api2.php
Modified folders: html/bla10/dev/appl/language/.DS_Store
Modified folders: html/bla11/dev/appl/language/abc.txt

Это - в основном вывод rsync. Я хотел бы перечислить все строки файла до 3 мест каталога, как

Modified folders: html/project1/old
Modified folders: html/port5
Modified folders: html/trap/dev8
Modified folders: html/bla3/test
Modified folders: html/bla4/pro1
Modified folders: html/bla10/dev
Modified folders: html/bla11/dev

Кто-либо может предоставить мне какой-либо командный сценарий или сценарий оболочки, чтобы сделать то же?

2
задан 22 January 2018 в 14:24

3 ответа

Возможно, как это:

$ sed -r 's|/[^/]*$||' file | sed -r 's|([^/]*/?[^/]*/?[^/]*).*|\1|'
Modified folders: html/project1/old
Modified folders: html/port5
Modified folders: html/trap/dev8
Modified folders: html/bla3/test
Modified folders: html/bla4/pro1
Modified folders: html/bla10/dev
Modified folders: html/bla11/dev

Или можно сделать вторую часть с cut:

sed -r 's|/[^/]*$||' file | cut -d '/' -f 1,2,3

Примечания

  • -r используйте ДО
  • s|old|new| замена old с new
  • [^/]* любое количество символов, которые не являются /
  • $ конец строки
  • /? нуль или один /
  • (pattern) сохранить pattern к ссылке позже с \1
  • .* любое количество любых символов
  • | (неупомянутый) канал оболочки - передает вывод команды левой стороны к команде правой стороны
  • cut -d '/' использовать / как разделитель
  • -f 1,2,3 распечатайте первые три поля
6
ответ дан 2 December 2019 в 01:26

Следующий сценарий (почти) сделает, как Вы просите.

#!/usr/bin/env perl

use strict;
use warnings;

while(<DATA>) {
    s!^(Modified\s+folders:\s+)((?:[^/]+/){1,3}).*?$!$1$2!;
    print;
}

__DATA__
Modified folders: html/project1/old/dev/vendor/symfony/yaml/Tests/bla.yml
Modified folders: html/port5/.DS_Store
Modified folders: html/trap/dev8/.DS_Store
Modified folders: html/bla3/test/appl/.DS_Store
Modified folders: html/bla4/pro1/app/bla/Api2.php
Modified folders: html/bla10/dev/appl/language/.DS_Store
Modified folders: html/bla11/dev/appl/language/abc.txt

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

Вывод

Modified folders: html/project1/old/
Modified folders: html/port5/
Modified folders: html/trap/dev8/
Modified folders: html/bla3/test/
Modified folders: html/bla4/pro1/
Modified folders: html/bla10/dev/
Modified folders: html/bla11/dev/

Если мы пишем regex в одной одной строке:

s!^(Modified\s+folders:\s+)((?:[^/]+/){1,3}).*?$!$1$2!;

затем это выглядит немного страшным, но это на самом деле довольно просто. Основной оператор является оператором замены s/// от Perl.

s/foo/bar/;

заменит каждое происшествие foo с bar. s позволяет нам изменять разделитель от / к чему-то другому. Я использовал a ! здесь, таким образом, мы могли также записать

s!foo!bar!;

! не означает not это - просто произвольный символ здесь. sLfooLbarL; работал бы также. Мы делаем это потому что, если мы используем стандарт / мы должны были бы выйти / в параметрах (который затем известен как синтаксис зубочистки). Полагайте, что мы хотим заменить путь /old/path с /new/path. Теперь сравните:

s/\/old\/path/\/new\/path/; # escaping of / needed
s!/old/path!/new/path!;     # no escaping of / needed (but of ! if we had one in the text)

Мы можем также подать заявку x модификатор к s///. Это позволяет, чтобы произвольный пробел (даже новые строки и комментарии) в шаблоне (левая сторона) улучшил удобочитаемость. Теперь цикл может быть записан как:

while(<DATA>) {
    s!^                         # match beginning of line
      (Modified\s+folders:\s+)  # the word "Modified", followed by 1 ore more 
                                # whitespace \s+,
                                # the literal "folders:", also followed by 1 or 
                                # more whitespace.
                                # We capture that match in $1 (that's why we have 
                                # parens around it).
      (                         # begin of 2nd capture group (in $2)
        (?:                     #   begin a group that is NOT captured (because of the "?:"
         [^/]+/                 #   one or more characters that are not a slash followed by a slash
        )                       #   end of group
        {1,3}                   #   this group should appear one to three times
      )                         # close capture group $2, i.e. remember the 1-3x slash thing
      .*?$                      # followed by arbitrary characters up to the end of line
     !$1$2!x;                   # Replace the line with the two found captures $1 and $2, i.e.
                                # with the text "Modified folders:" and the 1-3x slash thing.
    print;
}

Полный "сценарий" может также быть записан как острота:

perl -pe 's!^(Modified\s+folders:\s+)((?:[^/]+/){1,3}).*?$!$1$2!x;' file

Обновление

Я просто понял что Modified folders: строка может рассматриваться как компонент пути также. Таким образом, шаблон может быть упрощен до

perl -pe 's!^((?:[^/]+/){1,3}).*?$!$1!;' file
3
ответ дан 2 December 2019 в 01:26
grep -oP '^.*?(/.*?){0,2}(?=/)'

краткое объяснение темного regexp использовало:

  • ^... я начало строки
  • .*? seq. символов (но просто необходимая сумма) для соответствия предварительному пути
  • /.*?){0,2} 0, 1 или 2 каталога
  • (?=/) предусмотрите выражение - сопровождаемый a / это не включено
3
ответ дан 2 December 2019 в 01:26

Другие вопросы по тегам:

Похожие вопросы: