substituição complexa de shell puro em variável

3

Existe alguma maneira de substituir texto em variáveis em vários padrões no momento ou até mesmo usando a referência de volta?

Por exemplo, tenho FILE=filename.ext e quero alterá-lo para filename_sometext.ext . Mas eu não sei que a extensão do arquivo é .ext . Tudo o que sei é que a extensão é depois do último ponto. Então eu posso fazer isso em duas etapas:

EXT=${FILE##*.}
FILE=${FILE%.*}_sometext.$EXT

Posso fazer isso em uma etapa (algo como ${FILE/.\(*\)/_sometext.} [que não funciona])?

A propósito, eu preciso fazê-lo em shell puro sem sed / awk / etc. Meu shell é ksh , mas se há maneira de fazê-lo com bashisms eu gostaria de saber também.

    
por rush 11.05.2012 / 10:18

4 respostas

5

Expansão do parâmetro Bash diz que a variável ( FILE no seu exemplo) deve ser um nome de parâmetro. Então eles não aninham. E a última parte de ${param/pattern/replacement} deve ser uma string. Portanto, as referências anteriores não são suportadas.

Meu único conselho é usar

${EXT:+.$EXT}

para evitar adicionar um ponto final se o arquivo não tiver extensão.

UPDATE

Aparentemente referências anteriores são suportadas em ksh93 .

Então você poderia usar algo como

FILE="${FILE/@(.*)/_something}"
    
por 11.05.2012 / 10:45
3

Neste caso em particular, acho que FILE=${FILE%.*}_sometext.${FILE##*.} faria o trabalho.

    
por 11.05.2012 / 11:22
0

Variáveis.

Em qualquer shell, você pode concatenar variáveis ou valores:

$ a=One; b=Two; c=Three
$ d="$a$b$c"; echo $d
OneTwoThree

O tipo de estruturas ${parameter%word} é POSIX tem sido suportado na maioria dos shells por um longo tempo.
Assumindo FILE=filename.ext , você poderia fazer:

$ FILE="${FILE%.*}_sometext.${FILE##*.}"; echo "$FILE"
filename_sometext.ext

Essa é uma linha (conforme solicitado) e funciona na maioria dos shells.

Também é possível usar variáveis (mesmo com vários pontos):

#!/bin/bash
FILE=file.one.name.ext
ADDTEXT="_sometext"
EXT="${FILE##*.}"
echo "EXT=$EXT"
echo "final FILE=${FILE%.*}${ADDTEXT}.$EXT"

Ou em apenas uma linha:

ONEFILE=${FILE%.*}${ADDTEXT}.${FILE##*.}
echo "one   FILE=$ONEFILE"

Substituição de padrões

O que é complicado é tentar usar ${parameter/pattern/string} «Substituição de padrões.»

Uma variável funciona em muitos shells (até mesmo em busybox):

NEWTEXT="${ADDTEXT}.${FILE##*.}"
echo "ash   FILE=${FILE/.*/${NEWTEXT}}"

Mas não em mais antigos sh ells.

Apenas para FILE=filename.ext :
Isso funciona no bash (e ksh, ksh93, mksh, zsh), mas não em ash, dash, sh ou csh

echo "bash  FILE=${FILE/%.*/${ADDTEXT}.${FILE##*.}}"

Observe que ele está usando o caractere % para indicar uma correspondência no final do parâmetro (mas, infelizmente, ele é ávido e come todos os pontos em FILE=file.one.name.ext ).

Conclusão

O melhor método para controlar quão gananciosa é a substituição é usar variáveis separadas (em oposição a algumas de "Substituição de padrões").

    
por 19.10.2015 / 01:55
0
FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"

Se a variável do shell $FILE contiver um . , o comando acima atribuirá a ela o valor antigo menos tudo após o último . , em seguida, _sometext e durará o último valor antigo . e tudo depois.

E assim:

FILE=some.dot
FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"
printf %s\n "$FILE"
some_sometext.dot

Se a variável do shell $FILE não não contiver um ponto, então a string _sometext é simplesmente anexada ao final do valor antigo:

FILE=no_dots_at_all
FILE="${FILE%.*}_sometext${FILE#"${FILE%.*}"}"
printf %s\n "$FILE"
no_dots_at_all_sometext

Isso é um comportamento muito diferente do que você pode obter se você não aninhou a expansão de parâmetro como eu faço no final:

FILE=no_dots_at_all
FILE="${FILE%.*}_sometext${FILE##*.}"
printf %s\n "$FILE"
no_dots_at_all_sometextno_dots_at_all

Quando você aninha a expansão do parâmetro, ele é avaliado de dentro para fora e, portanto, a primeira coisa que acontece é:

for    FILE in    some.dot no_dots_at_all
do     printf %s\n '${FILE#'"${FILE%.*}"'}'
done
${FILE#some}
${FILE#no_dots_at_all}

... o shell remove quaisquer bits correspondentes e usa os restos como o padrão para remover da expansão externa. E assim, quando não são nenhum bit correspondente e a variável expande completamente, a expansão externa elimina todos da variável, em vez de substituí-la em sua expressão duas vezes.

    
por 19.10.2015 / 09:35