У меня есть следующие строки в файле:
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
Может ли кто-нибудь указать мне любую команду или сценарий оболочки, чтобы сделать то же самое?
Возможно, так:
$ 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
Возможно, так:
$ 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
Возможно, так:
$ 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
Следующий скрипт выполнит (почти), как вы просите.
#!/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
grep -oP '^.*?(/.*?){0,2}(?=/)'
краткое объяснение используемого темного регулярного выражения:
^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включенgrep -oP '^.*?(/.*?){0,2}(?=/)'
краткое объяснение используемого темного регулярного выражения:
^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включенСледующий скрипт выполнит (почти), как вы просите.
#!/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
grep -oP '^.*?(/.*?){0,2}(?=/)'
краткое объяснение используемого темного регулярного выражения:
^... i начало строки .*? a seq. (но только необходимое количество), чтобы соответствовать предпусковым /.*?){0,2} 0, 1 или 2 каталогам (?=/) смотреть вперед, а затем /, который не включенСледующий скрипт выполнит (почти), как вы просите.
#!/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