Làm thế nào để lấy ký tự cuối cùng của một chuỗi trong một trình bao?


125

Tôi đã viết những dòng sau để lấy ký tự cuối cùng của một chuỗi:

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

Nó hoạt động cho abcd/:

$ bash last_ch.sh abcd/
/

không hoạt động choabcd* :

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

liệt kê các tệp trong thư mục hiện tại .

Câu trả lời:


97

Đó là một trong những lý do tại sao bạn cần trích dẫn các biến của mình:

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

Nếu không, bash sẽ mở rộng biến và trong trường hợp này, nó sẽ nhấp nháy trước khi in ra. Tốt hơn là nên trích dẫn tham số vào tập lệnh (trong trường hợp bạn có tên tệp phù hợp):

sh lash_ch.sh 'abcde*'

Ngoài ra, hãy xem thứ tự mở rộng trong sổ tay tham khảo bash . Các biến được mở rộng trước khi mở rộng tên tệp.

Để lấy ký tự cuối cùng, bạn chỉ nên sử dụng -1làm chỉ mục vì các chỉ số âm tính từ cuối chuỗi:

echo "${str: -1}"

Khoảng trắng sau dấu hai chấm ( :) là BẮT BUỘC.

Cách tiếp cận này sẽ không hoạt động nếu không có không gian.


62
BTW, các chỉ số âm tính từ bên phải, như vậy "${1: -1}"là đủ.
choroba

7
Bạn nên trả lời như một giải pháp chính thức, không phải ở đây trong nhận xét.
BMW

FYI: bạn cũng có thể làm điều này trong "một lớp lót" như echo "${str:$((${#str}-1)):1}". Điều này cũng cho phép bạn thay đổi các ký tự theo sau được trả về. Công việc này đối với tôi trong bash v4.1.0 (1)
SaxDaddy

16
Câu trả lời của @ choroba: Lưu ý khoảng trống chết tiệt! Điều này luôn khiến tôi gặp khó khăn: "${1:-1}không hoạt động "!
mxmlnkn

192

Theo @perreal, trích dẫn các biến là quan trọng, nhưng vì tôi đã đọc bài đăng này 5 lần trước khi tìm ra cách tiếp cận đơn giản hơn cho câu hỏi trong phần nhận xét ...

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

Đầu ra: /

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

Đầu ra: *

Cảm ơn tất cả những người đã tham gia vào điều này ở trên; Tôi đã thêm +1 một cách thích hợp trong suốt chuỗi!


25
NB: lưu ý không gian bên trong ${std: -1}, không có không gian điều này sẽ không hoạt động. Tôi chạy vào điều này và thấy rằng nó ${std:~0}cũng hoạt động (có hoặc không có khoảng trống). Tuy nhiên, không chắc liệu đây có phải là hành vi được ghi lại hay không, tôi không buồn tra cứu nó.
Bart

4
nó được ghi lại. man bash nói: biểu thức số học bắt đầu với một - phải được ngăn cách bởi khoảng trắng từ trước: để được phân biệt với việc sử dụng Giá trị mặc định mở rộng
mighq

3
Thật tuyệt vời, cảm ơn vì đã chia sẻ. Mặc dù vậy, trang web BASH gần như quá tải để đọc, tôi đã vào đó vài lần. Tôi nghĩ rằng họ có thể thực hiện một khóa học đại học về shell scripting lol.
quickshiftin

3
Giải pháp này không hoạt động trong một .shtập lệnh khi cố gắng gán ký tự được truy xuất cho một biến. Ví dụ: declare LAST=$(echo "${str: -1}") Điều này sẽ dẫn đến thanh màu đỏ khó chịu hiển thị lỗi cú pháp bắt đầu tại:
krb686

3
nếu kiểm tra cú pháp của trình soạn thảo không thích khoảng trống đó, hãy thử${str:0-1}
ttt

36

Tôi biết đây là một chủ đề rất cũ, nhưng không ai đề cập đến chủ đề nào với tôi là câu trả lời rõ ràng nhất:

echo -n $str | tail -c 1

Lưu ý -nchỉ là để tiếng vọng không bao gồm dòng mới ở cuối.


15
Thậm chí không gần đến mức sạch hơn $ {str: -1}
shrewmouse

8
Nó sạch hơn vì nó không dựa trên bash-ism, vì vậy nó sẽ hoạt động với bất kỳ trình bao Posix nào.
John Eikenberry

"Bashist" nhất là gì? "$ {str: -1}" Và ... Cái nào sạch nhất? "$ {str: -1}" Bash là món hời! :-)
Roger

Đối với bất kỳ ai đang cố gắng in các ký tự cuối cùng trên một tệp lớn, điều này đã thực hiện công việc đối với tôi: cat user / folder / file.json | tail -c 1 Bạn có thể thay thế số 1 bằng số ký tự bạn muốn in.
Diego Serrano,

5

một giải pháp khác sử dụng tập lệnh awk:

1 ký tự cuối cùng:

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

5 ký tự cuối cùng:

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

1
Không phải những gì OP yêu cầu, nhưng đây là giải pháp tốt nhất mà tôi thấy được sử dụng với một tệp. Hầu hết các cách tiếp cận khác sẽ không hiệu quả với việc nhập tệp vào đó, như trong: cat file | awk '{print substr($0,length,1)}'Cảm ơn!
xmar

4

Dòng đơn:

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

Hiện nay:

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

1
Tại sao bạn sử dụng hai cặp dấu ngoặc đơn? Ngoài ra, kể từ 4.2, bạn có thể sử dụng hiệu số âm, như được hiển thị trong câu trả lời của quickshiftin: echo "${str: -1}"là đủ (lưu ý khoảng cách giữa :-).
gniourf_gniourf

Hai cặp ngoặc đơn được sử dụng cho hoạt động số học
Eduardo Cuomo

Không. Vui lòng xem phần liên quan của sách hướng dẫn mà bạn sẽ đọc: độ dàiđộ lệch là các biểu thức số học (xem Số học Shell ); do đó bạn đã ở trong số học shell và không cần bất kỳ dấu ngoặc đơn nào cả. Ngoài ra, nếu điều bạn nói là đúng, sẽ hợp lý hơn nếu có một khai triển số học với $((...)).
gniourf_gniourf 14/09/15

@gniourf_gniourf, bạn nói đúng! Bây giờ tôi hiểu câu hỏi trước của bạn. Câu trả lời được cập nhật
Eduardo Cuomo

4

Mọi câu trả lời cho đến nay đều ngụ ý từ "shell" trong câu hỏi tương đương với Bash.

Đây là cách người ta có thể làm điều đó trong một vỏ Bourne tiêu chuẩn:

printf $str | tail -c 1

1
echo -nnhư được đề xuất bởi người dùng5394399 tránh sự cố khi $strchứa các ký tự định dạng đặc biệt.
Conrad Meyer

Việc -nchuyển đổi là một chủ nghĩa cơ bản. Nó sẽ không hoạt động trong trình bao Bourne tiêu chuẩn dưới dạng dấu gạch ngang, v.v ... Đó là điểm câu trả lời của tôi.
phep

1
Không hoàn toàn là cơ sở, nhưng POSIX nhận ra nó được xác định thực thi ("Nếu toán hạng đầu tiên là -n, hoặc nếu bất kỳ toán hạng nào chứa ký tự <backslash>, thì kết quả được xác định thực thi"). Thay vào đó, câu trả lời của bạn có thể được sửa bằng printf "%s" "$str" | ...:-).
Conrad Meyer

4

Thử:

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

Ví dụ:

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}))

là một cách tiếp cận tốt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.