Come ottenere l'ultimo carattere di una stringa in una shell?


125

Ho scritto le seguenti righe per ottenere l'ultimo carattere di una stringa:

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

Funziona per abcd/:

$ bash last_ch.sh abcd/
/

Non funziona perabcd* :

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

Essa elenca i file nella cartella corrente .

Risposte:


97

Questo è uno dei motivi per cui devi citare le tue variabili:

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

Altrimenti, bash espande la variabile e in questo caso esegue il globbing prima di stampare. È anche meglio citare il parametro allo script (nel caso in cui tu abbia un nome di file corrispondente):

sh lash_ch.sh 'abcde*'

Vedi anche l'ordine delle espansioni nel manuale di riferimento di bash . Le variabili vengono espanse prima dell'espansione del nome del file.

Per ottenere l'ultimo carattere dovresti semplicemente usare -1come indice poiché gli indici negativi contano dalla fine della stringa:

echo "${str: -1}"

Lo spazio dopo i due punti ( :) è RICHIESTO.

Questo approccio non funzionerà senza lo spazio.


62
A proposito, gli indici negativi contano da destra, quindi "${1: -1}"è sufficiente.
choroba

7
Dovresti rispondere come soluzione formale, non qui nei commenti.
BMW

Cordiali saluti: puoi anche farlo in una "battuta" come echo "${str:$((${#str}-1)):1}". Ciò consente di variare anche i caratteri finali restituiti. Questo funziona per me in bash v4.1.0 (1)
SaxDaddy

16
Risposta di @ choroba: nota il maledetto spazio! Questo mi "${1:-1}fa sempre inciampare: non funziona "!
mxmlnkn

192

Per @perreal, citare le variabili è importante, ma poiché ho letto questo post come 5 volte prima di trovare un approccio più semplice alla domanda in questione nei commenti ...

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

Produzione: /

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

Produzione: *

Grazie a tutti coloro che hanno partecipato a questo sopra; Ho opportunamente aggiunto i +1 in tutto il thread!


25
NB: nota lo spazio all'interno ${std: -1}, senza lo spazio non funzionerà. Mi sono imbattuto in questo e ho scoperto che ${std:~0}funziona anche (con o senza spazio). Non sono sicuro che questo sia un comportamento documentato, però, non mi sono preoccupato di cercarlo.
Bart il

4
è documentato. man bash dice: Le espressioni aritmetiche che iniziano con a - devono essere separate da spazi dalle precedenti: per essere distinte dall'espansione Usa valori predefiniti
mighq

3
È fantastico, grazie per la condivisione. La manpage BASH è quasi travolgente da leggere, ci sono stato diverse volte. Penso che potrebbero fare un corso universitario sullo scripting di shell lol.
cambio rapido:

3
Questa soluzione non funziona in uno .shscript quando si tenta di assegnare il carattere recuperato a una variabile. Ad esempio, declare LAST=$(echo "${str: -1}") questo risulterà nella brutta barra rossa che mostra un errore di sintassi a partire da:
krb686

3
se il controllo della sintassi dell'editor non piace quello spazio prova${str:0-1}
ttt

36

So che questo è un thread molto vecchio, ma nessuno ha menzionato quale per me è la risposta più pulita:

echo -n $str | tail -c 1

Nota che -nè solo così l'eco non include una nuova riga alla fine.


15
Nemmeno vicino ad essere più pulito di $ {str: -1}
toporagno

8
È più pulito in quanto non si basa su un bash-ism, quindi funzionerà con qualsiasi shell Posix.
John Eikenberry

Qual è il più "bashist"? "$ {str: -1}" E ... Qual è il più pulito? "$ {str: -1}" Bash è il besht! :-)
Roger l'

Per chiunque provi a stampare gli ultimi caratteri su un file di grandi dimensioni, questo ha fatto il lavoro per me: cat user / folder / file.json | tail -c 1 È possibile sostituire 1 con il numero di caratteri che si desidera stampare.
Diego Serrano

5

un'altra soluzione usando awk script:

ultimo 1 carattere:

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

ultimi 5 caratteri:

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

1
Non è quello che chiedeva l'OP, ma questa è la migliore soluzione che ho trovato da utilizzare con un file. La maggior parte degli altri approcci non funzionerà inserendovi un file, come in: cat file | awk '{print substr($0,length,1)}'Grazie!
xmar

4

Linea singola:

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

Adesso:

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

1
Perché usi due coppie di parentesi? Inoltre, dalla 4.2, puoi usare offset negativi, come mostrato nella risposta di quickshiftin: echo "${str: -1}"è sufficiente ( fai attenzione allo spazio tra :e -).
gniourf_gniourf

Due coppie di parentesi vengono utilizzate per le operazioni aritmetiche
Eduardo Cuomo

No. Si prega di consultare la parte rilevante del manuale dove leggerete: length e offset sono espressioni aritmetiche (vedere Shell Arithmetic ); quindi sei già in aritmetica della shell e non hai bisogno di parentesi. Inoltre, se quello che hai detto fosse vero, sarebbe più logico avere un'espansione aritmetica con $((...)).
gniourf_gniourf

@gniourf_gniourf hai ragione! Ora capisco la tua domanda precedente. Risposta aggiornata
Eduardo Cuomo

4

Ogni risposta finora implica che la parola "shell" nella domanda sia uguale a Bash.

Ecco come si potrebbe farlo in una shell Bourne standard:

printf $str | tail -c 1

1
echo -ncome proposto da user5394399 evita problemi quando $strcontiene caratteri di formato speciale.
Conrad Meyer

L' -ninterruttore è un bashismo. Non funzionerà nella shell Bourne standard come trattino, ecc ... Che era il punto della mia risposta.
phep

1
Non proprio un bashismo, ma POSIX riconosce che è definita dall'implementazione ("Se il primo operando è -n, o se uno qualsiasi degli operandi contiene un carattere <backslash>, i risultati sono definiti dall'implementazione"). La tua risposta potrebbe invece essere corretta con printf "%s" "$str" | ...:-).
Conrad Meyer

4

Provare:

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

Ad esempio:

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

-2
echo $str | cut -c $((${#str}))

è un buon approccio

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.