A string Bash substitui vários caracteres por um

8

Estou substituindo, de um título de feed, todos os caracteres, exceto letras e dígitos, por um traço para usar o resultado como um nome de arquivo seguro para qualquer sistema de arquivos:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

No entanto, eu gostaria de condensar todos os traços repetidos com um único Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

Descobri que posso consegui-lo usando uma substituição de duas passagens:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

Eu pensei que poderia fazê-lo em uma única passagem como:

$ echo ${t//[^A-Za-z0-9]+/-}

mas não funciona.

Qualquer pista?

Nota: não quero usar sednem outras ferramentas

neurino
fonte

Respostas:

8

Você precisa de algo mais poderoso do que os curingas tradicionais do shell. No bash, defina a extglobopção, que fornece acesso a expressões regulares em padrões glob através de uma sintaxe incomum herdada do ksh.

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}
Gilles 'SO- parar de ser mau'
fonte
Obrigado, houve um comentário de fered sob jw013 resposta com esta solução. Algumas informações sobre compatibilidade com outros shells dessa sintaxe? Não me preocupo muito com isso, apenas para saber mais sobre shoptquais conchas o suportam.
Neurino
O @neurino shopté específico para o bash. A sintaxe de padrão que ele habilita está sempre disponível em todas as variantes do ksh. No zsh, essa sintaxe deve ser ativada com setopt ksh_glob. O POSIX não possui esse recurso, seus curingas são menos poderosos que os regexps. Os shells que não sejam bash / ksh / zsh, que na prática geralmente significam cinzas hoje em dia, tendem a seguir os curingas POSIX.
Gilles 'SO- stop be evil'
Bem, neste momento eu prefiro mais compatibilidade e flexibilidade com um pouco mais em cima: echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'. Aceito sua resposta, pois faz exatamente o que foi solicitado em questão.
Neurino
@neurino Se você deseja portabilidade para outras conchas, pode seguir a resposta de glenn jackman . A propósito, observe que a ${var/PATTERN/REPLACEMENT}construção também é específica para ksh / bash / zsh.
Gilles 'SO- stop be evil'
Prefiro sedque conheça melhor sua sintaxe e comportamento, posso adicionar facilmente uma instrução para remover traços iniciais / finais, não preciso me preocupar com \nchar. É sedmuito menos disponível do que tr?
Neurino
7

tr é uma boa ferramenta para este trabalho

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}
Glenn Jackman
fonte
Obrigado, +1, eu nunca me lembro de tr... No entanto, eu estava tentando fazê-lo em Bash, caso contrário, eu iria com sed:echo "$t" | sed -r 's/[^A-Za-z0-9]+/-/g'
neurino
Down votado porque entra em conflito comNote: I don't want to go with sed or other tools
Paul Calabro 24/05
3

Se você quiser ficar com o bash puro, terá que se contentar com a solução de duas passagens. As substituições de string Bash usam globs , como na expansão do nome do caminho, e não expressões regulares. Os únicos caracteres especiais em globs são *, ?e [], cujo áspero equivalentes em expressões regulares são .*, .e []. Dê uma olhada no wiki do Wooledge e nas seções da página de manual e para mais informações.bash(1)Parameter ExpansionPathname Expansion

Apenas como comentário, é provável que uma expansão de duas passagens no bash puro seja mais rápida do que tentar fazer a mesma coisa invocando um programa externo, para que eu não me preocupasse muito com isso.

jw013
fonte
Obrigado, vou verificar o link. Minha preocupação é que eu tenho que fazer esse trabalho mais de uma vez em todo o script, então minha única preocupação era ter o mesmo código repetido repetidamente, comprometendo a legibilidade. De qualquer forma, estou apresentando uma solução educada que vou postar. Cheers
neurino
Você pode colocar esse código em uma função para evitar a repetição de código.
Jw013
É o que estou fazendo, mas, como você sabe, as funções festança não pode retornar strings ... ou, pelo menos, era o que eu pensava antes de 10 minutos atrás :)
neurino
4
Aqui estão alguns exemplos com do-s e-don't-s - Bash Extended Globbing .. Para o exemplo acima, seria:shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}"
Peter.O
1
@fered: obrigado, muito interessante, vou dar uma olhada. O seu link url tem um caractere extra e retorna a 404, o trabalho é Bash Extensão Globbing
neurino