Manipulação de string de bash de tubulação

9

Eu li algumas outras perguntas sobre manipulação de string de bash de tubulação, mas elas parecem ser aplicativos especializados.

Essencialmente, existe uma maneira de fazer o abaixo mais simples?

ao invés de

$ string='hello world'; string2="${string// /_}"; echo "${string2^^}"
HELLO_WORLD

algo como

$ echo 'hello world' | $"{-// /_}" | "${ -^^}"
HELLO_WORLD

Editar Estou interessado em permanecer dentro de manipulações do bash, se possível, para manter a velocidade (em vez de sed / awk, que tendem a desacelerar bastante meus scripts)

Edit2: @jimmij

Eu gosto do segundo exemplo e me levou a fazer uma função.

bash_m() { { read x; echo "${x// /_}"; } | { read x; echo "${x^^}"; }; }
echo hello world | bash_m
HELLO_WORLD
Miati
fonte
1
por que você acha que sed / awk seria lento para esse fim? Eles são tão rápidos quanto eles vêm.
Mkc # 26/14
1
@Ketan Sed e awk são processos separados, portanto nunca podem ser rápidos como algo que o bash pode fazer de forma nativa sem iniciar um processo separado. Normalmente, essa diferença é quase imperceptível, mas onde o desempenho importa nos scripts de shell geralmente é um certo loop ou a computação está sendo repetida um número muito grande de vezes, e a geração de milhares de processos será notavelmente mais lenta do que uma simples manipulação de string no bash nativamente.
jw013
2
@ jw013 Isto é verdade para cadeias curtas como "olá mundo" da pergunta, mas se a cadeia for muito longa, digamos o trmanual, o oposto será verdadeiro porque o tempo de geração dos processos é insignificante em comparação com o tempo de manipulação de cadeias para o qual sede awksão dedicados. Se a string é extremamente longa, digamos o manual completo do bash, o bash pode se recusar a prosseguir completamente, devido a algumas limitações internas.
jimmij
2
@ jw013 estou afirmando código de manipulação de string que do bash é menos eficientes ferramentas, em seguida, dedicado como sed, awk, trou semelhantes. Veja a resposta do gena2x, que eu editei há algum tempo, adicionando exatamente essas informações: unix.stackexchange.com/questions/162221/… você pode compará-lo com terdon answer para a mesma pergunta em que ele dá tempo para seqüências curtas nas quais a geração do processo do caso leva mais tempo. Você pode testá-lo e postar o resultado.
jimmij
1
@Miati Por que você acha que esse extra read x; echo $xé melhor para o desempenho? A sintaxe não parece mais curta ou mais limpa. x=${x// /_}; x=${x^^}é uma maneira muito mais concisa de fazer a mesma coisa que {read x; echo ${x.... Quanto ao desempenho, o @jimmij apontou que tr/ sedseria mais rápido do que a bashcontagem de garfos sendo igual. O uso de um pipe sempre resulta em um processo extra, portanto o argumento de salvar um garfo não se aplica mais. Portanto, se você estiver usando pipes, use sed/ tretc. Se você puder fazê-lo no bash, faça-o e pule essa read x; echo $xbobagem.
jw013

Respostas:

9

O que jimmij disse. Seu último exemplo é o mais próximo que você pode chegar do que está tentando na sua expressão.

Aqui está uma variante desse tema:

echo 'hello world'|echo $(read s;s=${s^^};echo ${s// /_})

Eu estaria inclinado a usar tr, pois é bastante rápido.

echo 'hello world'|tr ' [:lower:]' '_[:upper:]'

Suponho que seja uma pena que o bash não permita a expansão de parâmetros aninhados; OTOH, o uso de tais expressões aninhadas pode facilmente levar a códigos difíceis de ler. A menos que você realmente precise que as coisas sejam executadas o mais rápido possível, é melhor escrever um código fácil de ler, entender e manter, em vez de um código de aparência inteligente que seja uma PITA para depuração. E se você realmente fazer precisa de coisas a ser feito em alta velocidade você deve estar usando código compilado, não um script.

PM 2Ring
fonte
7

Você não pode passar expansões de parâmetro dessa maneira. Quando você se refere ao xuso do $símbolo como na "${x}"forma, ele deve ser o nome da variável real, não uma entrada padrão, pelo menos não bash. Em zshvocê pode executar substituições de parâmetros aninhadas da seguinte maneira:

$ x=''hello world'
$ echo ${${x// /_}:u}
HELLO_WORLD

(nota: :ué para zsho mesmo que ^^para bash)

O aninhamento no bash não é possível e acho que o que você escreveu em questão é o melhor possível, mas se por algum motivo estranho você precisar envolver tubos na equação, poderá tentar o seguinte:

$ echo 'hello world' | { read x; echo "${x// /_}"; } | { read y; echo "${y^^}"; }
HELLO_WORLD
jimmij
fonte
1
Considerando nossa conversa recente sobre como tr/ sedsão mais rápidos do que bashno processamento de strings e considerando como você está usando pipes para passar strings via E / S padrão, vejo literalmente ponto zero para realizar essas operações no bash em oposição a tr/ sed. Por que alguém | { read x; echo $x... }em oposição a alguém | sedque faz a mesma coisa?
jw013
1
@ jw013 falando francamente eu vejo quase nenhum ponto. É apenas um exemplo para envolver forçosamente os pipes no problema, porque o OP os pediu explicitamente e não queria usar programas externos (ambos echoe readsão embutidos no bash, portanto, em princípio, um pouco mais rápido). Como eu já escrevi na resposta, manipulações progressivas de parâmetros que o OP tem na pergunta é a melhor que posso obter na minha opinião para esta tarefa no bash. De qualquer forma, o problema é bastante acadêmico.
jimmij