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

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

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

Может ли кто-нибудь указать мне любую команду или сценарий оболочки, чтобы сделать то же самое?

3
задан 22 January 2018 в 15:24

9 ответов

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

$ 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 используйте ERE s|old|new| замените old на new [^/]* любое количество символов, которые не являются / $ конец строки /? ноль или один / (pattern) сохранить pattern, чтобы ссылаться позже с \1 .* любое количество любых символов | (неуказанное) shell pipe - передает результат команды левой стороны в правую сторону команды cut -d '/' использовать / в качестве разделителя -f 1,2,3 печатать первые три поля
6
ответ дан 22 May 2018 в 15:23
  • 1
    Не соответствует второй строке примера, OP хочет также удалить имена файлов ... – Byte Commander 22 January 2018 в 14:53
  • 2
    Моя попытка была бы grep -Po '^.*?(/[^/]*){0,2}(?=/|$)', но это страдает той же проблемой, и я не могу понять это сейчас. Не стесняйтесь использовать его, если он вам поможет. – Byte Commander 22 January 2018 в 14:54
  • 3
    @ByteCommander благодарит за это. Я исправил это, но мне не нравится трубопровод. Я буду искать лучший способ – Zanna 22 January 2018 в 15:03
  • 4
    @zanna спасибо тонну. Спасибо, что упростили для меня вещи. Мне действительно нужно изучить сценарии оболочки ... В любом случае, спасибо. Полюбите хорошего друга. – john deo 22 January 2018 в 15:15
  • 5
    @PerlDuck мои знания о Perl является уровнем для начинающих. Пожалуйста, напишите ответ :) – Zanna 22 January 2018 в 15:16

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

$ 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 используйте ERE s|old|new| замените old на new [^/]* любое количество символов, которые не являются / $ конец строки /? ноль или один / (pattern) сохранить pattern, чтобы ссылаться позже с \1 .* любое количество любых символов | (неуказанное) shell pipe - передает результат команды левой стороны в правую сторону команды cut -d '/' использовать / в качестве разделителя -f 1,2,3 печатать первые три поля
6
ответ дан 17 July 2018 в 22:29

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

$ 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 используйте ERE s|old|new| замените old на new [^/]* любое количество символов, которые не являются / $ конец строки /? ноль или один / (pattern) сохранить pattern, чтобы ссылаться позже с \1 .* любое количество любых символов | (неуказанное) shell pipe - передает результат команды левой стороны в правую сторону команды cut -d '/' использовать / в качестве разделителя -f 1,2,3 печатать первые три поля
6
ответ дан 23 July 2018 в 23:07

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

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

Он читает каждую строку ввода, выбирает из нее некоторые значения (мои средства регулярного выражения), заменяет строку выбранными (!).

Вывод

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/

Если мы будем писать регулярное выражение в одной строке:

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

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

s/foo/bar/;

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

s!foo!bar!;

. [F16] не означает 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
4
ответ дан 22 May 2018 в 15:23
grep -oP '^.*?(/.*?){0,2}(?=/)'

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

^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включен
3
ответ дан 22 May 2018 в 15:23
  • 1
    Не могли бы вы расширить свой ответ с подробностями о том, что это должно делать? – derHugo 22 January 2018 в 19:20
  • 2
    Это более холодно, чем ответ Perl , поскольку он также уменьшает конечную /. – PerlDuck 22 January 2018 в 19:21
grep -oP '^.*?(/.*?){0,2}(?=/)'

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

^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включен
3
ответ дан 17 July 2018 в 22:29

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

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

Он читает каждую строку ввода, выбирает из нее некоторые значения (мои средства регулярного выражения), заменяет строку выбранными (!).

Вывод

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/

Если мы будем писать регулярное выражение в одной строке:

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

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

s/foo/bar/;

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

s!foo!bar!;

. [F16] не означает 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
4
ответ дан 17 July 2018 в 22:29
grep -oP '^.*?(/.*?){0,2}(?=/)'

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

^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включен
3
ответ дан 23 July 2018 в 23:07
  • 1
    Не могли бы вы расширить свой ответ с подробностями о том, что это должно делать? – derHugo 22 January 2018 в 19:20
  • 2
    Это более холодно, чем ответ Perl , поскольку он также уменьшает конечную /. – PerlDuck 22 January 2018 в 19:21

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

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

Он читает каждую строку ввода, выбирает из нее некоторые значения (мои средства регулярного выражения), заменяет строку выбранными (!).

Вывод

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/

Если мы будем писать регулярное выражение в одной строке:

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

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

s/foo/bar/;

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

s!foo!bar!;

. [F16] не означает 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
4
ответ дан 23 July 2018 в 23:07

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

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