Comment obtenir le dernier caractère d'une chaîne dans un shell?

125

J'ai écrit les lignes suivantes pour obtenir le dernier caractère d'une chaîne:

str=$1
i=$((${#str}-1))
echo ${str:$i:1}

Cela fonctionne pour abcd/:

$ bash last_ch.sh abcd/
/

Cela ne fonctionne pas pourabcd* :

$ bash last_ch.sh abcd*
array.sh assign.sh date.sh dict.sh full_path.sh last_ch.sh

Il répertorie les fichiers dans le dossier actuel .

penseur3
la source

Réponses:

97

C'est l'une des raisons pour lesquelles vous devez citer vos variables:

echo "${str:$i:1}"

Sinon, bash développe la variable et, dans ce cas, effectue un glissement avant l'impression. Il est également préférable de citer le paramètre dans le script (au cas où vous auriez un nom de fichier correspondant):

sh lash_ch.sh 'abcde*'

Voir également l'ordre des extensions dans le manuel de référence bash . Les variables sont développées avant l'extension du nom de fichier.

Pour obtenir le dernier caractère, vous devez simplement l'utiliser -1comme index car les indices négatifs comptent à partir de la fin de la chaîne:

echo "${str: -1}"

L'espace après les deux points ( :) est OBLIGATOIRE.

Cette approche ne fonctionnera pas sans l'espace.

perréel
la source
62
BTW, les indices négatifs comptent à partir de la droite, il "${1: -1}"suffit donc.
choroba
7
Vous devez répondre en tant que solution formelle, pas ici dans les commentaires.
BMW
FYI: vous pouvez également le faire dans un "one-liner" comme echo "${str:$((${#str}-1)):1}". Cela vous permet également de faire varier les caractères de fin renvoyés. Ce travail pour moi dans bash v4.1.0 (1)
SaxDaddy
16
Réponse de @ choroba: Notez l'espace foutu! Cela me "${1:-1}
trébuche
192

Par @perreal, citer des variables est important, mais parce que j'ai lu ce post comme 5 fois avant de trouver une approche plus simple à la question à portée de main dans les commentaires ...

str='abcd/'
echo "${str: -1}"

Production: /

str='abcd*'
echo "${str: -1}"

Production: *

Merci à tous ceux qui ont participé à cela ci-dessus; J'ai correctement ajouté des +1 tout au long du fil!

quickshiftin
la source
25
NB: notez l'espace à l'intérieur ${std: -1}, sans l'espace cela ne fonctionnera pas. Je suis tombé sur ceci et j'ai trouvé que cela ${std:~0}fonctionne également (avec ou sans espace). Je ne sais pas s'il s'agit d'un comportement documenté, je n'ai pas pris la peine de le rechercher.
Bart
4
il est documenté. man bash dit: Les expressions arithmétiques commençant par un - doivent être séparées par des espaces de la précédente: à distinguer de l'expansion Utiliser les valeurs par défaut
mighq
3
C'est génial, merci pour le partage. La page de manuel BASH est presque écrasante à lire, j'y suis allé plusieurs fois. Je pense qu'ils pourraient faire un cours universitaire sur les scripts shell lol.
quickshift du
3
Cette solution ne fonctionne pas dans un .shscript lorsque vous essayez d'affecter le caractère récupéré à une variable. Par exemple, declare LAST=$(echo "${str: -1}") cela entraînera la mauvaise barre rouge montrant une erreur de syntaxe commençant à:
krb686
3
si la vérification de la syntaxe de l'éditeur n'aime pas cet espace, essayez${str:0-1}
ttt
36

Je sais que c'est un fil très ancien, mais personne n'a mentionné laquelle pour moi est la réponse la plus claire:

echo -n $str | tail -c 1

Notez que -nc'est juste pour que l'écho n'inclue pas de nouvelle ligne à la fin.

user5394399
la source
15
Pas même près d'être plus propre que $ {str: -1}
shrewmouse
8
Il est plus propre car il ne repose pas sur un bash-ism, il fonctionnera donc avec n'importe quel shell Posix.
John Eikenberry
Quel est le plus "bashist"? "$ {str: -1}" Et ... Quel est le plus propre? "$ {str: -1}" Bash est la besht! :-)
Roger le
Pour quiconque essaie d'imprimer les derniers caractères sur un gros fichier, cela a fait le travail pour moi: cat user / folder / file.json | tail -c 1 Vous pouvez remplacer le 1 par le nombre de caractères que vous souhaitez imprimer.
Diego Serrano
5

une autre solution utilisant le script awk:

1 dernier caractère:

echo $str | awk '{print substr($0,length,1)}'

5 derniers caractères:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
la source
1
Pas ce que l'OP a demandé, mais c'est la meilleure solution que j'ai trouvée pour être utilisée avec un fichier. La plupart des autres approches ne fonctionneront pas avec un fichier, comme dans: cat file | awk '{print substr($0,length,1)}'Merci!
xmar
4

Une seule ligne:

${str:${#str}-1:1}

Maintenant:

echo "${str:${#str}-1:1}"
Eduardo Cuomo
la source
1
Pourquoi utilisez-vous deux paires de parenthèses? De plus, depuis 4.2, vous pouvez utiliser des décalages négatifs, comme indiqué dans la réponse de quickshiftin: echo "${str: -1}"est suffisant (attention à l'espace entre :et -).
gniourf_gniourf
Deux paires de parenthèses sont utilisées pour les opérations arithmétiques
Eduardo Cuomo
Non. Veuillez consulter la partie appropriée du manuel où vous lirez: la longueur et le décalage sont des expressions arithmétiques (voir Shell Arithmetic ); par conséquent, vous êtes déjà dans l'arithmétique du shell et n'avez pas du tout besoin de parenthèses. D'ailleurs, si ce que vous avez dit était vrai, il serait plus logique d'avoir une extension arithmétique avec $((...)).
gniourf_gniourf
@gniourf_gniourf vous avez raison! Je comprends maintenant votre question précédente. Réponse mise à jour
Eduardo Cuomo
4

Chaque réponse jusqu'à présent implique le mot «shell» dans la question équivaut à Bash.

Voici comment on pourrait faire cela dans un shell Bourne standard:

printf $str | tail -c 1
phep
la source
1
echo -ncomme proposé par user5394399 évite les problèmes lorsqu'il $strcontient des caractères de format spécial.
Conrad Meyer
L' -ninterrupteur est un bashisme. Cela ne fonctionnera pas dans le shell Bourne standard comme tiret, etc ... Ce qui était le point de ma réponse.
phep
1
Ce n'est pas tout à fait un bashisme, mais POSIX reconnaît qu'il est défini par l'implémentation ("Si le premier opérande est -n, ou si l'un des opérandes contient un caractère <backslash>, les résultats sont définis par l'implémentation"). Votre réponse pourrait plutôt être corrigée avec printf "%s" "$str" | ...:-).
Conrad Meyer
4

Essayer:

"${str:$((${#str}-1)):1}"

Par exemple:

someone@mypc:~$ str="A random string*"; echo "$str"
A random string*
someone@mypc:~$ echo "${str:$((${#str}-1)):1}"
*
someone@mypc:~$ echo "${str:$((${#str}-2)):1}"
g
mark_infinite
la source
-2
echo $str | cut -c $((${#str}))

est une bonne approche

pradeep
la source