Comment supprimer les n derniers caractères d'une chaîne dans Bash?

202

J'ai une variable vardans un script Bash contenant une chaîne, comme:

echo $var
"some string.rtf"

Je souhaite supprimer les 4 derniers caractères de cette chaîne et affecter le résultat à une nouvelle variable var2, de sorte que

echo $var2
"some string"

Comment puis-je faire ceci?

Becko
la source
1
Duplicate of extract-substring-in-bash
Håkon Hægland

Réponses:

16

Tout d'abord, vous devez être explicite sur ce que vous voulez. Si vous savez que la chaîne se termine par .rtfet que vous souhaitez supprimer le .rtf, vous pouvez utiliser var2=${var%.rtf}. Si vous ne savez pas quel est le suffixe mais que vous souhaitez supprimer tout ce qui commence par le dernier ., vous pouvez utiliser var2=${var%.*}. Si vous ne voulez que tout garder jusqu'au premier ., vous pouvez utiliser var2=${var%%.*}. Ces deux dernières options ont le même résultat s'il n'y a qu'une seule période, mais s'il peut y en avoir plus d'une, vous devez décider laquelle vous voulez.

Si vous voulez vraiment toujours supprimer un nombre exact de caractères, voici quelques options.

Vous avez spécifié bash spécifiquement, nous allons donc commencer avec les commandes bash. Celui qui a fonctionné le plus longtemps est la même syntaxe de suppression de suffixe que j'ai utilisée ci-dessus: pour supprimer quatre caractères, utilisezvar2=${var%????} . Vous avez besoin d'un point d'interrogation par caractère supprimé, donc cela devient difficile à gérer pour des longueurs de sous-chaîne plus importantes.

Un peu plus récent est sousChaîne extraction: var2=${var::${#var}-4}. Ici, vous pouvez mettre n'importe quel nombre à la place de 4pour supprimer un nombre différent de caractères. (Le ${#var}est remplacé par la longueur de la chaîne, donc cela demande en fait de conserver les premiers (longueur - 4) caractères.)

Les versions plus récentes de bash (en particulier 4+, ce qui signifie que celle livrée avec MacOS ne fonctionnera pas) vous permettent de simplifier var2=${var::-4} .

Si vous n'utilisez pas bash mais un autre shell de type POSIX, la suppression du suffixe fonctionnera toujours, même dans un simple tiret ancien (où aucun des autres ne le fera). En zsh, ils fonctionnent tous mais vous devez mettre un 0entre les deux points: var2=${var:0:-4}etc. En ksh, vous avez besoin du 0 et vous devez également utiliser l'expression explicite de longueur 4:var2=${var:0:${#var}-4} .

Vous pouvez bien sûr utiliser la substitution de commandes pour le faire à l'aide d'un programme utilitaire; il y en a beaucoup qui fonctionneront, mais quelque chose comme var2=$(cut -c -4 <<<"$var")est probablement l'option la plus courte.

Mark Reed
la source
241

Vous pouvez faire comme ça:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"
phe
la source
9
bash 4+ je pense.
Etan Reisner
69
C'est bash4+, mais dans la version précédente, vous pouvez utiliser le légèrement plus long ${v::${#v}-4}.
chepner
8
Ça n'a pas fonctionné pour moi, bash dit '' Mauvaise substitution ''
Ivan Marjanovic
15
pour une raison quelconque, dans zsh ${v::-4}croaks "zsh: accolade fermante attendue". Mais la réponse de @fredtantini ci-dessous ${v:0:-4}fonctionne très bien.
Pierre D
6
Erreur avec: -2: expression de sous-chaîne <0
Edward
174

Pour supprimer quatre caractères de la fin de la chaîne, utilisez ${var%????}.

Pour tout supprimer après la dernière .utilisation ${var%.*}.

Etan Reisner
la source
12
Réponse pure shell et fonctionnera dans BASH 3.x que l'on trouve sur de nombreux systèmes qui ont refusé d'implémenter BASH 4.x en raison de problèmes de licence.
David W.
1
C'est cool car c'est robuste contre les cordes plus courtes; les variantes de décalage d'index échouent alors.
Raphael
fonctionne en bash 3.2.25 - la meilleure réponse possible - exactement ce que je cherchais
capser
Excellente réponse. Que diriez-vous d'un retrait à l'avant de la chaîne?
user2023370
3
@ user2023370 La consultation de la documentation devrait révéler qu'il existe une substitution de paramètre correspondante ${var#????}pour cela.
tripleee
48

Ce qui a fonctionné pour moi, c'est:

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

Mais je l'ai utilisé pour découper des lignes dans un fichier, c'est pourquoi cela semble gênant. La véritable utilisation était:

cat somefile | rev | cut -c5- | rev

cutvous permet uniquement de rogner à partir d'une position de départ, ce qui est mauvais si vous avez besoin de lignes de longueur variable. Donc, cette solution inverse ( rev) la chaîne et maintenant nous nous rapportons à sa position de fin, puis utilise cutcomme mentionné, et la retourne (encore une fois rev) à son ordre d'origine.

Reut Sharabani
la source
Ce n'est pas correct et ce n'est pas une réponse à ce qui est demandé. revinverse les lignes, pas les chaînes. echo $SOME_VAR | rev | ...ne se comportera probablement pas comme on pourrait s'y attendre.
Mohammad Nasirifar
2
@Mohammad En raison de la citation brisée, qui est une seule ligne.
tripleee
38

Utilisation de l' extension variable / remplacement de sous-chaîne :

$ {var /% Pattern / Replacement}

Si le suffixe de var correspond au modèle, remplacez le remplacement par le modèle.

Vous pouvez donc faire:

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

Alternativement,

Si vous avez toujours les mêmes 4 lettres

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

Si cela se termine toujours par .xyz:

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

Vous pouvez également utiliser la longueur de la chaîne:

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

ou simplement echo ${var::-4}

fredtantini
la source
len=${#var}; echo ${var::len-4}pourrait être raccourci à echo ${var:0:-4}:-) EDIT: ou comme l'a souligné @iyonizer, juste echo ${var::-4}...
anishsane
26

Vous pouvez utiliser sed,

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

Exemple:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string
Avinash Raj
la source
1
et comment attribuer le résultat var2?
Becko
c'est bien car cela fonctionne sur une seule ligne comme celle-ci ... | sed 's /. \ {4 \} $ //'. Peut être utilisé sans utiliser de variables
Sam
3

J'ai essayé ce qui suit et cela a fonctionné pour moi:

#! /bin/bash

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

Production: hel

Priya Jain
la source
1

J'espère que l'exemple ci-dessous vous aidera,

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

  • Dans la commande ci-dessus, le nom est la variable.
  • start est le point de départ de la chaîne
  • len est la longueur de la chaîne à supprimer.

Exemple:

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

Production:

    Enter:Siddharth Murugan
    Siddhar

Remarque: Bash 4.2 a ajouté la prise en charge de la sous-chaîne négative

Siddharth Murugan
la source
C'est beaucoup plus verbeux qu'une substitution de modèle simple compatible avec Bourne comme dans la réponse d'Etan Reisner.
tripleee
0

Cela a fonctionné pour moi en calculant la taille de la chaîne.
Il est facile de répéter la valeur que vous devez renvoyer, puis de la stocker comme ci-dessous

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

une chaîne

Saurabh
la source
0

Dans ce cas, vous pouvez utiliser le nom de base en supposant que vous ayez le même suffixe sur les fichiers que vous souhaitez supprimer.

Exemple:

basename -s .rtf "some string.rtf"

Cela renverra "une chaîne"

Si vous ne connaissez pas le suffixe et souhaitez qu'il supprime tout après et y compris le dernier point:

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

sorties "fichier"

% signifie couper,. est ce que vous coupez, * est un caractère générique

Greta
la source