Eu tenho esse pequeno script no sh
(Mac OSX 10.6) para examinar uma variedade de arquivos. O Google parou de ser útil neste momento:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
Até agora (obviamente, para você, shell gurus), $name
apenas contém 0, 1 ou 2, dependendo se for grep
encontrado que o nome do arquivo corresponde ao problema fornecido. O que eu gostaria é capturar o que está dentro dos parênteses ([a-z]+)
e armazenar isso em uma variável .
Eu gostaria de usar grep
apenas, se possível . Se não, por favor, não Python ou Perl, etc. sed
ou algo parecido - eu sou novo no shell e gostaria de atacar isso do ângulo purista * nix.
Além disso, como um bônus super legal , estou curioso para saber como concatenar uma string com casca? O grupo que eu capturei era a string "somename" armazenada em $ name e eu queria adicionar a string ".jpg" ao final, não é cat $name '.jpg'
?
Por favor, explique o que está acontecendo, se você tiver tempo.
grep
, entãosed
seria ótimo, se é possível resolver usandosed
.Respostas:
Se você estiver usando o Bash, nem precisará usar
grep
:É melhor colocar o regex em uma variável. Alguns padrões não funcionarão se incluídos literalmente.
Isso usa
=~
qual é o operador de correspondência de regex do Bash. Os resultados da correspondência são salvos em uma matriz chamada$BASH_REMATCH
. O primeiro grupo de captura é armazenado no índice 1, o segundo (se houver) no índice 2, etc. O índice zero é a correspondência completa.Você deve estar ciente de que, sem âncoras, essa regex (e a que está sendo usada
grep
) corresponderá a qualquer um dos seguintes exemplos e mais, que pode não ser o que você está procurando:Para eliminar o segundo e o quarto exemplos, faça sua regex assim:
que diz que a sequência deve começar com um ou mais dígitos. O quilate representa o início da string. Se você adicionar um cifrão no final da regex, faça o seguinte:
o terceiro exemplo também será eliminado, pois o ponto não está entre os caracteres na regex e o cifrão representa o final da string. Observe que o quarto exemplo também falha nessa correspondência.
Se você possui o GNU
grep
(por volta de 2.5 ou posterior, acho, quando o\K
operador foi adicionado):O
\K
operador (look-behind de comprimento variável) faz com que o padrão anterior corresponda, mas não inclui a correspondência no resultado. O equivalente de comprimento fixo é(?<=)
- o padrão seria incluído antes do parêntese de fechamento. Você deve usar\K
se os quantificadores podem coincidir com cordas de comprimentos diferentes (por exemplo+
,*
,{2,4}
).O
(?=)
operador corresponde a padrões de comprimento fixo ou variável e é chamado de "antecipação". Também não inclui a sequência correspondente no resultado.Para fazer a correspondência não diferenciar maiúsculas de minúsculas, o
(?i)
operador é usado. Afeta os padrões que o seguem, portanto sua posição é significativa.A regex pode precisar ser ajustada, dependendo da existência de outros caracteres no nome do arquivo. Você notará que, neste caso, mostro um exemplo de concatenação de uma sequência de caracteres ao mesmo tempo em que a substring é capturada.
fonte
/K
operador balança.grep
. Também foi aceito pelo OP e votou bastante. Obrigado pelo voto negativo.Isso não é realmente possível com o puro
grep
, pelo menos geralmente não.Mas se o seu padrão for adequado, você poderá usar
grep
várias vezes em um pipeline para reduzir sua linha para um formato conhecido e, em seguida, extrair o pouco que desejar. (Embora ferramentas gostemcut
esed
sejam muito melhores nisso).Suponha, por uma questão de argumento, que seu padrão seja um pouco mais simples:
[0-9]+_([a-z]+)_
você pode extrair isso da seguinte maneira:O primeiro
grep
removeria todas as linhas que não correspondessem ao seu padrão geral; o segundogrep
(que--only-matching
especificou) exibirá a parte alfa do nome. Isso funciona apenas porque o padrão é adequado: "parte alfa" é específica o suficiente para extrair o que você deseja.(Além disso: pessoalmente, eu usaria
grep
+cut
para obter o que você procura:.echo $name | grep {pattern} | cut -d _ -f 2
Issocut
analisa a linha em campos dividindo o delimitador_
e retorna apenas o campo 2 (os números dos campos começam em 1)).A filosofia do Unix é ter ferramentas que fazem uma coisa, e fazê-lo bem, e combiná-las para realizar tarefas não triviais, então eu diria que
grep
+sed
etc é uma maneira mais Unixy de fazer as coisas :-)fonte
for f in $files; do name=
eco $ f | grep -oEi '[0-9] + _ ([az] +) _ [0-9a-z] *' | corte -d _ -f 2;
Aha!Percebo que uma resposta já foi aceita para isso, mas, de um "ângulo estritamente * nix purista", parece que é a ferramenta certa para o trabalho
pcregrep
, que parece não ter sido mencionada ainda. Tente alterar as linhas:para o seguinte:
para obter apenas o conteúdo do grupo de captura 1.
A
pcregrep
ferramenta utiliza a mesma sintaxe com a qual você já usougrep
, mas implementa a funcionalidade necessária.O parâmetro
-o
funciona exatamente como agrep
versão, se estiver vazio, mas também aceita um parâmetro numéricopcregrep
, que indica qual grupo de captura você deseja mostrar.Com esta solução, há um mínimo de mudanças necessárias no script. Você simplesmente substitui um utilitário modular por outro e ajusta os parâmetros.
Nota interessante: Você pode usar vários argumentos -o para retornar vários grupos de captura na ordem em que aparecem na linha.
fonte
pcregrep
não está disponível por padrão noMac OS X
que é o que os usos OPpcregrep
parece não entender o dígito após o-o
: "Letra de opção desconhecida '1' em" -o1 ". Também não há menção dessa funcionalidade ao olharpcregrep --help
7.8 2008-09-05
.echo 'r123456 foo 2016-03-17' | pcregrep -o1 'r([0-9]+)' 123456
pcregrep
8.41 (instalado comapt-get install pcregrep
onUbuntu 16.03
) não reconhece o-Ei
comutador. Funciona perfeitamente sem ele, no entanto. No macOS, com opcregrep
instalado viahomebrew
(também 8.41), como @anishpatel menciona acima, pelo menos no High Sierra, o-E
switch também não é reconhecido.Não é possível apenas em grep eu acredito
para sed:
Vou dar uma facada no bônus:
fonte
sed
solução não funciona. Simplesmente imprime tudo no meu diretório.Esta é uma solução que usa gawk. É algo que eu acho que preciso usar com frequência, então criei uma função para ele
usar apenas fazer
fonte
\s
. Sabes como arranjar isso?Uma sugestão para você - você pode usar a expansão de parâmetros para remover a parte do nome do último sublinhado em diante e da mesma forma no início:
Então
name
terá o valorabc
.Consulte os documentos para desenvolvedores da Apple , procure 'Expansão de parâmetros'.
fonte
se você tem bash, pode usar globbing estendido
ou
fonte