Bagaimana cara mendapatkan karakter terakhir dari string di shell?

125

Saya telah menulis baris berikut untuk mendapatkan karakter terakhir dari sebuah string:

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

Ini bekerja untuk abcd/:

$ bash last_ch.sh abcd/
/

Ini tidak bekerja untukabcd* :

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

Ini mencantumkan file di folder saat ini .

pemikir3
sumber

Jawaban:

97

Itulah salah satu alasan mengapa Anda perlu mengutip variabel Anda:

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

Jika tidak, bash memperluas variabel dan dalam hal ini melakukan globbing sebelum mencetak. Lebih baik juga mengutip parameter ke skrip (jika Anda memiliki nama file yang cocok):

sh lash_ch.sh 'abcde*'

Lihat juga urutan ekspansi di manual referensi bash . Variabel diperluas sebelum perluasan nama file.

Untuk mendapatkan karakter terakhir, Anda hanya perlu menggunakan -1sebagai indeks karena indeks negatif dihitung dari akhir string:

echo "${str: -1}"

Spasi setelah titik dua ( :) DIPERLUKAN.

Pendekatan ini tidak akan berhasil tanpa ruang.

nyata
sumber
62
BTW, indeks negatif dihitung dari kanan, jadi "${1: -1}"sudah cukup.
choroba
7
Anda harus membalas sebagai solusi formal, bukan di sini dalam komentar.
BMW
FYI: Anda juga dapat melakukan ini dalam "satu baris" sebagai echo "${str:$((${#str}-1)):1}". Ini memungkinkan Anda untuk memvariasikan karakter trailing yang dikembalikan juga. Ini bekerja untuk saya di bash v4.1.0 (1)
SaxDaddy
16
@ Jawaban choroba: Catat ruangnya! Ini selalu membuat saya "${1:-1}
tersandung
192

Menurut @perreal, mengutip variabel itu penting, tetapi karena saya membaca posting ini seperti 5 kali sebelum menemukan pendekatan yang lebih sederhana untuk pertanyaan yang ada di komentar ...

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

Keluaran: /

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

Keluaran: *

Terima kasih kepada semua orang yang berpartisipasi dalam hal ini di atas; Saya telah menambahkan +1 dengan tepat di seluruh utas!

quickshiftin
sumber
25
NB: perhatikan ruang di dalam ${std: -1}, tanpa spasi ini tidak akan berfungsi. Saya menemukan ini dan menemukan itu ${std:~0}juga berfungsi (dengan atau tanpa ruang). Tidak yakin apakah ini adalah perilaku yang terdokumentasi, saya tidak repot-repot mencarinya.
Bart
4
itu didokumentasikan. man bash mengatakan: Ekspresi aritmatika yang dimulai dengan a - harus dipisahkan dengan spasi dari sebelumnya: untuk dibedakan dari ekspansi Use Default Values
mighq
3
Itu luar biasa, terima kasih telah berbagi. Halaman manual BASH hampir luar biasa untuk dibaca, saya pernah ke sana beberapa kali. Saya pikir mereka bisa membuat kursus perguruan tinggi di sekitar shell scripting lol.
quickshiftin
3
Solusi ini tidak berfungsi dalam .shskrip saat mencoba menetapkan karakter yang diambil ke variabel. Misalnya declare LAST=$(echo "${str: -1}") Ini akan menghasilkan bilah merah jahat yang menunjukkan kesalahan sintaks mulai dari:
krb686
3
jika pemeriksaan sintaks editor tidak menyukai ruang itu coba${str:0-1}
ttt
36

Saya tahu ini adalah utas yang sangat lama, tetapi tidak ada yang menyebutkan yang bagi saya adalah jawaban terbersih:

echo -n $str | tail -c 1

Perhatikan bahwa -ngema tidak menyertakan baris baru di akhir.

pengguna5394399
sumber
15
Bahkan tidak mendekati lebih bersih dari $ {str: -1}
shrewmouse
8
Ini lebih bersih karena tidak bergantung pada bash-isme, jadi ini akan bekerja dengan shell Posix.
John Eikenberry
Apa yang paling "bashist"? "$ {str: -1}" Dan ... Apa yang paling bersih? "$ {str: -1}" Bash adalah yang terbaik! :-)
Roger
Bagi siapa pun yang mencoba mencetak karakter terakhir pada file besar, ini berhasil untuk saya: cat user / folder / file.json | tail -c 1 Anda dapat mengganti 1 dengan jumlah karakter yang ingin Anda cetak.
Diego Serrano
5

solusi lain menggunakan skrip awk:

1 karakter terakhir:

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

5 karakter terakhir:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
sumber
1
Bukan apa yang diminta OP, tapi ini adalah solusi terbaik yang saya temukan untuk digunakan dengan file. Sebagian besar pendekatan lain tidak akan berhasil memasukkan file ke dalamnya, seperti dalam: cat file | awk '{print substr($0,length,1)}'Terima kasih!
xmar
4

Garis tunggal:

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

Sekarang:

echo "${str:${#str}-1:1}"
Eduardo Cuomo
sumber
1
Mengapa Anda menggunakan dua pasang tanda kurung? Juga, sejak 4.2, Anda dapat menggunakan offset negatif, seperti yang ditunjukkan dalam jawaban quickshiftin: echo "${str: -1}"cukup (perhatikan jarak antara :dan -).
gniourf_gniourf
Dua pasang tanda kurung digunakan untuk operasi aritmatika
Eduardo Cuomo
Tidak. Silakan lihat bagian yang relevan dari manual di mana Anda akan membaca: panjang dan offset adalah ekspresi aritmatika (lihat Aritmatika Shell ); karenanya Anda sudah berada dalam aritmatika shell dan tidak memerlukan tanda kurung sama sekali. Selain itu, jika apa yang Anda katakan itu benar, itu akan menjadi lebih logis untuk memiliki ekspansi aritmatika dengan $((...)).
gniourf_gniourf
@gniourf_gniourf Anda benar! Sekarang saya mengerti pertanyaan Anda sebelumnya. Jawaban diperbarui
Eduardo Cuomo
4

Setiap jawaban sejauh ini menyiratkan kata "shell" dalam pertanyaan tersebut sama dengan Bash.

Beginilah cara seseorang dapat melakukannya di shell Bourne standar:

printf $str | tail -c 1
phep
sumber
1
echo -nseperti yang dikemukakan oleh user5394399 menghindari masalah ketika $strberisi karakter format khusus.
Conrad Meyer
The -nswitch adalah bashism a. Itu tidak akan bekerja dalam standar Bourne shell sebagai tanda hubung, dll ... Yang merupakan inti dari jawaban saya.
phep
1
Bukan bashism, tetapi POSIX mengenalinya sebagai implementasi yang ditentukan ("Jika operan pertama adalah -n, atau jika salah satu operand berisi karakter <backslash>, hasilnya adalah implementasi-ditentukan"). Jawaban Anda bisa diperbaiki dengan printf "%s" "$str" | ...:-).
Conrad Meyer
4

Mencoba:

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

Misalnya:

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
sumber
-2
echo $str | cut -c $((${#str}))

adalah pendekatan yang bagus

pradeep
sumber