Como remover os últimos n caracteres de uma string no Bash?

202

Eu tenho uma variável varem um script Bash segurando uma string, como:

echo $var
"some string.rtf"

Desejo remover os últimos 4 caracteres dessa string e atribuir o resultado a uma nova variável var2, para que

echo $var2
"some string"

Como posso fazer isso?

becko
fonte
1
Duplicado de extração-substring-em-bash
Håkon Hægland

Respostas:

15

Primeiro, você deve ser explícito sobre o que deseja. Se você souber que a sequência termina .rtfe deseja remover a .rtf, poderá usar var2=${var%.rtf}. Se você não sabe qual é o sufixo, mas deseja remover tudo que começa com o último ., pode usar var2=${var%.*}. Se você quiser apenas manter tudo atualizado ., pode usar var2=${var%%.*}. Essas duas últimas opções terão o mesmo resultado se houver apenas um período, mas se houver mais de um, você deve decidir qual deseja.

Se você realmente deseja sempre remover um número exato de caracteres, aqui estão algumas opções.

Você especificou o bash especificamente, portanto, começaremos com os bash builtins. O que funcionou por mais tempo é a mesma sintaxe de remoção de sufixo que usei acima: para remover quatro caracteres, use var2=${var%????}. Você precisa de um ponto de interrogação por caractere removido, portanto, isso fica complicado para comprimentos de substring maiores.

Ligeiramente mais recente é substring extração: var2=${var::${#var}-4}. Aqui você pode colocar qualquer número no lugar de 4para remover um número diferente de caracteres. (O ${#var}é substituído pelo comprimento da string, portanto, na verdade, é necessário manter os primeiros caracteres (comprimento - 4).)

Versões mais recentes do bash (especificamente 4+, o que significa que o MacOS não funciona) permite simplificar isso apenas var2=${var::-4}.

Se você não estiver realmente usando o bash, mas algum outro shell do tipo POSIX, a remoção do sufixo ainda funcionará, mesmo em traços antigos simples (onde nenhum dos outros funcionará). Em zsh, todos eles funcionam, mas você tem que colocar um 0entre os dois pontos: var2=${var:0:-4}etc. Em ksh, é necessário o 0 e também tem que usar a explícita comprimento-4 expressão: var2=${var:0:${#var}-4}.

Obviamente, você pode usar a substituição de comandos para fazê-lo com a ajuda de um programa utilitário; há muitos que funcionarão, mas algo como var2=$(cut -c -4 <<<"$var")provavelmente é a opção mais curta.

Mark Reed
fonte
241

Você pode fazer assim:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"
phe
fonte
9
bash 4+ eu acho.
Etan Reisner
69
É bash4+, mas na versão anterior você pode usar um pouco mais ${v::${#v}-4}.
chepner
8
Não funcionou para mim, o bash diz '' Má substituição ''
Ivan Marjanovic 28/10
15
por alguma razão, no zsh ${v::-4}croaks "zsh: fechamento esperado". Mas a resposta de @fredtantini abaixo ${v:0:-4}funciona bem.
Pierre D
6
Erro com: -2: expressão de substring <0
Edward
173

Para remover quatro caracteres do final da cadeia, use ${var%????}.

Para remover tudo após o .uso final ${var%.*}.

Etan Reisner
fonte
12
Resposta pura do shell e funcionará no BASH 3.x, encontrado em muitos sistemas que se recusaram a implementar o BASH 4.x devido a problemas de licenciamento.
26414 David W.
1
Isso é legal, pois é robusto contra a obtenção de cordas mais curtas; as variações de deslocamento do índice falham então.
Raphael
funciona em bash 3.2.25 - a melhor resposta possível - exatamente o que eu estava procurando
capser
Excelente resposta. Que tal uma remoção na frente da corda?
precisa saber é o seguinte
3
@ user2023370 A consulta da documentação deve revelar que existe uma substituição de parâmetro correspondente ${var#????}.
Tripleee
48

O que funcionou para mim foi:

echo "hello world" | rev | cut -c5- | rev
# hello w

Mas eu o usei para aparar linhas em um arquivo, e é por isso que parece estranho. O uso real foi:

cat somefile | rev | cut -c5- | rev

cutapenas leva você a recuar de alguma posição inicial, o que é ruim se você precisar de linhas de comprimento variável. Portanto, essa solução inverte ( rev) a string e agora nos relacionamos com sua posição final, depois a usa cutcomo mencionado e a reverte (de novo rev) para sua ordem original.

Reut Sharabani
fonte
Isso não está correto e não é uma resposta para o que está sendo solicitado. revinverte linhas, não cordas. echo $SOME_VAR | rev | ...provavelmente não se comportará como seria de esperar.
Mohammad Nasirifar
2
@ Mohamed Por causa da citação quebrada, essa é uma única linha.
Tripleee 14/07/19
38

Usando expansão variável / substituição de substring :

$ {var /% Padrão / Substituição}

Se o sufixo de var corresponder ao padrão, substitua Substituição pelo padrão.

Então você pode fazer:

~$ echo ${var/%????/}
some string

Alternativamente,

Se você tem sempre as mesmas 4 letras

~$ echo ${var/.rtf/}
some string

Se está sempre terminando em .xyz:

~$ echo ${var%.*}
some string

Você também pode usar o comprimento da string:

~$ len=${#var}
~$ echo ${var::len-4}
some string

ou simplesmente echo ${var::-4}

fredtantini
fonte
len=${#var}; echo ${var::len-4}poderia ser reduzido para echo ${var:0:-4}:-) EDIT: ou como @iyonizer apontou, apenas echo ${var::-4}...
anishsane
26

Você poderia usar sed,

sed 's/.\{4\}$//' <<< "$var"

Exemplo:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string
Avinash Raj
fonte
1
e como atribuo o resultado var2?
Becko
isso é bom porque funciona em uma linha como esta ... | sed /. \ {4 \} $ // '. Pode ser usado sem o uso de variáveis
Sam
3

Eu tentei o seguinte e funcionou para mim:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}

Resultado: hel

Priya Jain
fonte
1

Espero que o exemplo abaixo ajude,

echo ${name:0:$((${#name}-10))} -> ${name:start:len}

  • No comando acima, name é a variável
  • start é o ponto inicial da string
  • len é o comprimento da string que deve ser removida.

Exemplo:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

Resultado:

    Enter:Siddharth Murugan
    Siddhar

Nota: O Bash 4.2 adicionou suporte para substring negativo

Siddharth Murugan
fonte
Isso é muito mais detalhado do que uma substituição de padrão simples compatível com Bourne, como na resposta de Etan Reisner.
Tripleee
0

Isso funcionou para mim, calculando o tamanho da string.
É fácil ecoar o valor que você precisa retornar e depois armazená-lo como abaixo

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?

alguma corda

Saurabh
fonte
0

Nesse caso, você pode usar o nome da base, assumindo que possui o mesmo sufixo nos arquivos que deseja remover.

Exemplo:

basename -s .rtf "some string.rtf"

Isso retornará "alguma string"

Se você não conhece o sufixo e deseja remover tudo depois e incluindo o último ponto:

f=file.whateverthisis
basename "${f%.*}"

gera "arquivo"

% significa costeleta,. é o que você está cortando, * é curinga

Greta
fonte