Xóa ký tự cuối cùng của chuỗi bằng thao tác chuỗi trong shell script


187

Tôi muốn xóa ký tự cuối cùng của chuỗi, tôi đã thử đoạn script nhỏ này:

#! /bin/sh 

t="lkj"
t=${t:-2}
echo $t

Nhưng nó in chữ "lkj", tôi đang làm gì sai?

Câu trả lời:


115

Trong hệ vỏ POSIX, cú pháp ${t:-2}có nghĩa là một cái gì đó khác - nó mở rộng thành giá trị tif tđược đặt và không null, và ngược lại với giá trị 2. Để cắt một ký tự bằng cách mở rộng tham số, cú pháp bạn có thể muốn là${t%?}

Lưu ý rằng trong ksh93, bashhoặc zsh, ${t:(-2)}hoặc ${t: -2}(lưu ý khoảng trắng) hợp pháp dưới dạng mở rộng chuỗi con nhưng có thể không phải là điều bạn muốn, vì chúng trả lại chuỗi con bắt đầu từ vị trí 2 ký tự ở cuối (nghĩa là nó loại bỏ ký tự đầu tiêni của ký tự chuỗi ijk).

Xem phần Mở rộng tham số Shell của Hướng dẫn tham khảo Bash để biết thêm thông tin:


4
Bạn có quan tâm để giải thích điều kỳ diệu đằng sau '% không?' ?
afraisse

8
@afraisse ${parameter%word}loại bỏ khớp mẫu hậu tố ngắn nhất word- xem phần Mở rộng tham số củaman bash
Steeldo

3
Điều này hoạt động tốt cho Bash 4.1.2: $ {t%?} Cho những người bị mắc kẹt với CentOS / RHEL 6.x
Joey T

185

Với bash4.2 trở lên, bạn có thể làm:

${var::-1}

Thí dụ:

$ a=123
$ echo "${a::-1}"
12

Lưu ý rằng đối với bản cũ hơn bash(ví dụ: bash 3.2.5trên OS X), bạn nên chừa khoảng trắng giữa và sau dấu hai chấm:

${var: : -1}

13
Điều này hoạt động cho bashphiên bản 4.2-alpha trở lên, quá tệ là phiên bản tôi có quyền truy cập trước đó. : - /
hjk

2
@iamaziz: Từ bash changelog, độ dài âm trong chỉ ${var:offset:lenght}được thêm vào bash 4.2. Có lẽ OSX thêm bản vá của riêng mình cho bash.
cuonglm

1
@cuonglm không hoạt động: /
iamaziz

1
Không hoạt động trên mac.
shinzou

1
MACsters, nhìn xuống câu trả lời của Nga
P i

67

để xóa các nký tự cuối cùng khỏi dòng không sử dụng sedOR awk:

> echo lkj | rev | cut -c (n+1)- | rev

vì vậy, ví dụ bạn có thể xóa ký tự cuối cùng one characterbằng cách này:

> echo lkj | rev | cut -c 2- | rev

> lk

từ revtrang web:

MÔ TẢ
Tiện ích rev sao chép các tệp được chỉ định vào đầu ra tiêu chuẩn, đảo ngược thứ tự các ký tự trong mỗi dòng. Nếu không có tệp nào được chỉ định, đầu vào tiêu chuẩn sẽ được đọc.

CẬP NHẬT:

nếu bạn không biết độ dài của chuỗi, hãy thử:

$ x="lkj"
$ echo "${x%?}"
lk

62

Sử dụng sed nên nhanh như

sed 's/.$//'

Tiếng vang duy nhất của bạn là sau đó echo ljk | sed 's/.$//'.
Sử dụng điều này, chuỗi 1 dòng có thể có kích thước bất kỳ.


10
Lưu ý rằng trong trường hợp chung, nó không xóa ký tự cuối cùng của chuỗi , mà là ký tự cuối cùng của mỗi dòng trong chuỗi .
Stéphane Chazelas

44

Một vài tùy chọn tùy thuộc vào vỏ:

  • MỘT BỘ MÔ TẢ CHÍNH THỨC CUNG CẤP MỘT TIÊU CHUẨN CHO THIẾT KẾ HỆ ĐIỀU HÀNH, ĐẶC BIỆT LÀ CÁC MÔ TẢ TƯƠNG THÍCH VỚI UNIX: t=${t%?}
  • Bourne: t=`expr " $t" : ' \(.*\).'`
  • zsh / yash: t=${t[1,-2]}
  • bash / zsh: t=${t:0:-1}
  • ksh93 / bash / zsh / mksh: t=${t:0:${#t}-1}
  • ksh93 / bash / zsh / mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
  • es: @ {t=$1} ~~ $t *?

Lưu ý rằng trong khi tất cả được cho là loại bỏ ký tự cuối cùng , bạn sẽ thấy rằng một số triển khai (những thứ không hỗ trợ các ký tự nhiều byte) thay vào đó là byte cuối cùng (do đó có thể sẽ làm hỏng ký tự cuối cùng nếu nó là nhiều byte ).

Các exprbiến thể giả định $tkhông kết thúc với nhiều hơn một ký tự dòng mới. Nó cũng sẽ trả về trạng thái thoát khác không nếu chuỗi kết quả kết thúc bằng 0( 000hoặc thậm chí -0với một số triển khai). Nó cũng có thể cho kết quả bất ngờ nếu chuỗi chứa các ký tự không hợp lệ.


Đẹp và kỹ lưỡng! Nhưng ... tôi cho rằng tất cả các vỏ đó đều hỗ trợ POSIX, vì vậy mọi người chỉ nên sử dụng loại vỏ đó để có thể di động nhất. Số lượng nhân vật nhỏ nhất, quá!
Nga

@Russ, t=${t%?}không phải là Bourne nhưng hiện tại bạn không có khả năng bắt gặp một vỏ Bourne. ${t%?}không làm việc trong tất cả những người khác mặc dù.
Stéphane Chazelas

Không có tùy chọn vỏ cá đưa ra! Có lẽ phổ biến hơn những ngày này hơn ksh93 ...
rien333

@ rien333. Tôi sẽ đợi giao diện ổn định một chút. fishlà công việc đang tiến triển. 2.3.0 giới thiệu stringnội dung không được phát hành tại thời điểm hỏi đáp. Với phiên bản tôi đang thử nghiệm, bạn cần string replace -r '(?s).\z' '' -- $t(và tôi mong họ muốn thay đổi điều đó, họ nên thay đổi các cờ họ chuyển sang PCRE) hoặc nhiều hơn nữa. Nó cũng xử lý kém với các nhân vật dòng mới và tôi biết họ cũng đang có kế hoạch thay đổi điều đó.
Stéphane Chazelas

Nâng cao cho câu trả lời POSIX. xác nhận làm việc trên Bash 3.2.57 (1)
Avindra Goolcharan

26

Câu trả lời ngắn gọn và di động nhất gần như chắc chắn:

${t%?}

Điều này hoạt động trong bash, sh, tro, dash, busybox / ash, zsh, ksh, v.v.

Nó hoạt động bằng cách sử dụng mở rộng tham số shell trường học cũ. Cụ thể, %chỉ định loại bỏ hậu tố phù hợp nhỏ nhất của tham số tkhớp với mẫu hình cầu ?(nghĩa là: bất kỳ ký tự nào).

Xem "Xóa mẫu hình hậu tố nhỏ nhất" tại đây để được giải thích chi tiết hơn (nhiều hơn) và có thêm thông tin cơ bản. Đồng thời xem tài liệu cho trình bao của bạn (ví dụ man bash:) trong phần "mở rộng tham số".


Là một lưu ý phụ, nếu bạn muốn xóa ký tự đầu tiên thay vào đó, bạn sẽ sử dụng ${t#?}, vì #khớp từ phía trước của chuỗi (tiền tố) thay vì phía sau (hậu tố).

Cũng đáng chú ý là cả hai %#%%##các phiên bản, phù hợp với phiên bản dài nhất của mẫu đã cho thay vì ngắn nhất. Tuy nhiên, cả hai ${t%%?}${t##?}sẽ làm giống như toán tử đơn của chúng trong trường hợp này (vì vậy đừng thêm ký tự phụ vô dụng). Điều này là do ?mẫu đã cho chỉ khớp với một ký tự. Kết hợp *với một số ký tự không phải là ký tự đại diện và mọi thứ trở nên thú vị hơn với %%##.

Hiểu các mở rộng tham số, hoặc ít nhất là biết về sự tồn tại của chúng và biết cách tìm kiếm chúng, cực kỳ hữu ích để viết và giải mã các tập lệnh shell của nhiều hương vị. Mở rộng tham số thường trông giống như voodoo shell arcane đối với nhiều người bởi vì ... ờ ... chúng voodoo shell arcane (mặc dù tài liệu khá tốt nếu bạn biết tìm "mở rộng tham số"). Mặc dù vậy, chắc chắn là tốt để có trong vành đai công cụ khi bạn bị mắc kẹt trong vỏ.


Ngắn gọn và ngọt ngào, và hoạt động trên cả MacOS và Linux!
dbernard

18
t=lkj
echo ${t:0:${#t}-1}

Bạn nhận được một chuỗi con từ 0 đến độ dài chuỗi -1. Tuy nhiên, lưu ý rằng chất nền này là đặc trưng bash và sẽ không hoạt động trên các shell khác.

Chẳng hạn, dashkhông thể phân tích cú pháp

echo ${t:0:$(expr ${#t} - 1)}

Ví dụ, trên Ubuntu, /bin/shdash


15

Bạn cũng có thể sử dụng headđể in ra tất cả trừ ký tự cuối cùng.

$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin

Nhưng thật không may, một số phiên bản headkhông bao gồm -tùy chọn hàng đầu . Đây là trường hợp headđi kèm với OS X.


5

Thật dễ dàng để làm bằng cách sử dụng biểu thức thông thường:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"

5

Một số tinh chỉnh. Để xóa nhiều hơn một ký tự, bạn có thể thêm nhiều dấu hỏi. Ví dụ: để xóa hai ký tự cuối cùng khỏi biến : $SRC_IP_MSG, bạn có thể sử dụng:

SRC_IP_MSG=${SRC_IP_MSG%??}

4

Chỉ cần hoàn thành một số cách sử dụng có thể của bash tinh khiết:

#!/bin/bash

# Testing substring removal
STR="Exemple string with trailing whitespace "
echo "'$STR'"
echo "Removed trailing whitespace: '${STR:0:${#STR}-1}'"
echo "Removed trailing whitespace: '${STR/%\ /}'"

Cú pháp đầu tiên lấy một chuỗi con từ một chuỗi, cú pháp là Đối với chuỗi thứ hai, hãy chú ý dấu hiệu, có nghĩa là 'từ cuối dòng' và cú pháp là
${STRING:OFFSET:LENGTH}
%
${STRING/PATTERN/SUBSTITUTION}

Và đây là hai hình thức ngắn hơn của các đề cập ở trên

echo "Removed trailing whitespace: '${STR::-1}'"
echo "Removed trailing whitespace: '${STR%\ }'"

Ở đây thông báo lại %dấu hiệu, có nghĩa là 'Xóa (nghĩa là thay thế bằng' ') mẫu phù hợp ngắn nhất (ở đây được biểu thị bằng không gian thoát ' \ ' từ cuối PARAMETER - ở đây có tên là STR


1

Vì chúng ta cũng có thể sử dụng php trong dòng lệnh hoặc shell script. Nó đôi khi hữu ích cho phân tích phẫu thuật.

php -r "echo substr('Hello', 0, -1);" 
// Output hell

Với đường ống:

echo "hello" | php -r "echo substr(trim(fgets(STDIN)), 0, -1);"
// Output hell

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.