O script a seguir fará (quase) o que você pede.
#!/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
Ele lê todas as linhas de entrada, seleciona alguns valores (meus meios de regex), substitui a linha pelos valores selecionados e, finalmente, imprime a linha agora modificada (para STDOUT).
Resultado
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/
Se escrevermos o regex em uma única linha:
s!^(Modified\s+folders:\s+)((?:[^/]+/){1,3}).*?$!$1$2!;
então parece um pouco assustador, mas na verdade é bem simples. O operador básico é o operador de substituição s///
do Perl.
s/foo/bar/;
substituirá toda ocorrência de foo
com bar
. s
nos permite alterar o delimitador de /
para algo diferente. Eu usei um !
aqui, então também poderíamos escrever
s!foo!bar!;
O !
que não quer dizer not
que é apenas um caráter arbitrário aqui. sLfooLbarL;
funcionaria também. Fazemos isso porque, se usarmos o padrão /
, precisaremos escapar /
dos parâmetros (que são conhecidos como sintaxe do palito). Considere que queremos substituir o caminho /old/path
por /new/path
. Agora compare:
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)
Também podemos aplicar o x
modificador ao arquivo s///
. Permite um espaço em branco arbitrário (até novas linhas e comentários) no padrão (lado esquerdo) para melhorar a legibilidade. Agora o loop pode ser escrito como:
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;
}
O "script" completo também pode ser escrito como uma linha:
perl -pe 's!^(Modified\s+folders:\s+)((?:[^/]+/){1,3}).*?$!$1$2!x;' file
Atualizar
Acabei de perceber que a Modified folders:
string também pode ser vista como um componente do caminho. Portanto, o padrão pode ser simplificado para
perl -pe 's!^((?:[^/]+/){1,3}).*?$!$1!;' file
>
caracteres no início das linhas e quebras de linha duplas ou se é sua tentativa de formatar o conteúdo com precisão.