Làm thế nào để loại bỏ n ký tự cuối cùng khỏi một chuỗi trong Bash?


202

Tôi có một biến vartrong tập lệnh Bash chứa một chuỗi, như:

echo $var
"some string.rtf"

Tôi muốn xóa 4 ký tự cuối của chuỗi này và gán kết quả cho một biến mới var2, để

echo $var2
"some string"

Tôi có thể làm cái này như thế nào?


Câu trả lời:


15

Đầu tiên, bạn nên rõ ràng về những gì bạn muốn. Nếu bạn biết chuỗi kết thúc .rtfvà bạn muốn loại bỏ .rtf, bạn có thể sử dụng var2=${var%.rtf}. Nếu bạn không biết hậu tố là gì nhưng bạn muốn xóa mọi thứ bắt đầu bằng cái cuối cùng ., thì bạn có thể sử dụng var2=${var%.*}. Nếu bạn chỉ muốn giữ mọi thứ cho đến đầu tiên ., bạn có thể sử dụng var2=${var%%.*}. Hai tùy chọn cuối cùng có cùng kết quả nếu chỉ có một khoảng thời gian, nhưng nếu có thể có nhiều hơn một, bạn nên quyết định xem bạn muốn gì.

Nếu bạn thực sự muốn luôn loại bỏ một số lượng ký tự chính xác, đây là một số tùy chọn.

Bạn đã chỉ định bash cụ thể, vì vậy chúng tôi sẽ bắt đầu với các nội dung bash. Cái đã làm việc lâu nhất là cú pháp loại bỏ hậu tố giống như tôi đã sử dụng ở trên: để loại bỏ bốn ký tự, sử dụng var2=${var%????}. Bạn cần xóa một dấu hỏi cho mỗi ký tự, vì vậy điều này trở nên khó sử dụng cho độ dài chuỗi con lớn hơn.

Hơi mới hơn là trích xuất chuỗi con : var2=${var::${#var}-4}. Tại đây bạn có thể đặt bất kỳ số nào thay thế 4cho một số ký tự khác nhau. ( ${#var}Được thay thế bằng độ dài của chuỗi, vì vậy đây thực sự là yêu cầu giữ các ký tự (độ dài - 4) đầu tiên.)

Các phiên bản mới hơn của bash (cụ thể là 4+, có nghĩa là phiên bản được phát hành với MacOS sẽ không hoạt động) cho phép bạn đơn giản hóa điều đó var2=${var::-4}.

Nếu bạn không thực sự sử dụng bash nhưng một số loại vỏ POSIX khác, việc loại bỏ hậu tố sẽ vẫn hoạt động, ngay cả trong dấu gạch ngang cũ (nơi không có phần còn lại sẽ). Trong zsh, tất cả đều hoạt động nhưng bạn phải đặt một 0dấu hai chấm: var2=${var:0:-4}v.v ... Trong ksh, bạn cần 0 và cũng phải sử dụng biểu thức length-4 rõ ràng : var2=${var:0:${#var}-4}.

Tất nhiên bạn có thể sử dụng thay thế lệnh để làm điều đó với sự trợ giúp của một chương trình tiện ích; có rất nhiều thứ sẽ hoạt động, nhưng một cái gì đó giống như var2=$(cut -c -4 <<<"$var")có lẽ là lựa chọn ngắn nhất.


241

Bạn có thể làm như thế này:

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"

9
bash 4+ tôi nghĩ.
Etan Reisner

69
Nó là bash4+, nhưng trong phiên bản trước, bạn có thể sử dụng lâu hơn một chút ${v::${#v}-4}.
chepner

8
Không làm việc cho tôi, bash nói '' Thay thế xấu '
Ivan Marjanovic

15
vì một số lý do, trong zsh ${v::-4}croaks "zsh: đóng cú đúp dự kiến". Nhưng câu trả lời của @fredtantini dưới đây ${v:0:-4}hoạt động tốt.
Pierre D

6
Lỗi với: -2: biểu thức chuỗi con <0
Edward

173

Để loại bỏ bốn ký tự từ cuối chuỗi sử dụng ${var%????}.

Để loại bỏ mọi thứ sau khi .sử dụng cuối cùng ${var%.*}.


12
Câu trả lời thuần túy và sẽ hoạt động trong BASH 3.x được tìm thấy trên nhiều hệ thống đã từ chối triển khai BASH 4.x do vấn đề cấp phép.
David W.

1
Điều này thật tuyệt vì nó mạnh mẽ chống lại các chuỗi ngắn hơn; các biến thể bù chỉ số thất bại sau đó.
Raphael

hoạt động trong bash 3.2.25 - câu trả lời tốt nhất có thể - đúng như những gì tôi đang tìm kiếm
capser

Câu trả lời tuyệt vời. Làm thế nào về một loại bỏ ở phía trước của chuỗi?
2023370

3
@ user2023370 Tư vấn tài liệu sẽ tiết lộ rằng có một sự thay thế tham số tương ứng ${var#????}cho điều đó.
tripleee

48

Những gì làm việc cho tôi là:

echo "hello world" | rev | cut -c5- | rev
# hello w

Nhưng tôi đã sử dụng nó để cắt các dòng trong một tập tin vì vậy đó là lý do tại sao nó trông khó xử. Công dụng thực sự là:

cat somefile | rev | cut -c5- | rev

cutchỉ đưa bạn đến mức cắt tỉa từ một số vị trí bắt đầu, điều này là xấu nếu bạn cần các hàng có chiều dài thay đổi. Vì vậy, giải pháp này đảo ngược ( rev) chuỗi và bây giờ chúng ta liên quan đến vị trí kết thúc của nó, sau đó sử dụng cutnhư đã đề cập và đảo ngược (một lần nữa rev), trở lại trật tự ban đầu.


Điều này không chính xác và không phải là một câu trả lời cho những gì đang được hỏi. revđảo ngược dòng, không phải chuỗi. echo $SOME_VAR | rev | ...có lẽ sẽ không hành xử như người ta mong đợi
Mohammad Nasirifar

2
@Mohammad Vì trích dẫn bị hỏng, đó một dòng duy nhất.
tripleee

38

Sử dụng Mở rộng biến / Thay thế chuỗi con :

$ {var /% Mẫu / Thay thế}

Nếu hậu tố của var khớp với Mẫu, thì thay thế Thay thế cho Mẫu.

Vì vậy, bạn có thể làm:

~$ echo ${var/%????/}
some string

Ngoài ra,

Nếu bạn luôn có 4 chữ cái giống nhau

~$ echo ${var/.rtf/}
some string

Nếu nó luôn kết thúc bằng .xyz:

~$ echo ${var%.*}
some string

Bạn cũng có thể sử dụng độ dài của chuỗi:

~$ len=${#var}
~$ echo ${var::len-4}
some string

hoặc đơn giản echo ${var::-4}


len=${#var}; echo ${var::len-4}có thể rút ngắn thành echo ${var:0:-4}:-) EDIT: hoặc như @iyonizer đã chỉ ra, chỉ cần echo ${var::-4}...
anishsane

26

Bạn có thể sử dụng sed,

sed 's/.\{4\}$//' <<< "$var"

Thí dụ:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string

1
và làm thế nào để tôi gán kết quả cho var2?
vẫy gọi

Điều này là tốt bởi vì nó hoạt động trên một dòng như thế này ... | sed 's /. \ {4 \} $ //'. Có thể được sử dụng mà không cần sử dụng biến
Sam

3

Tôi đã thử những điều sau đây và nó đã làm việc cho tôi:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}

Đầu ra: hel


1

Hy vọng ví dụ dưới đây sẽ giúp ích,

echo ${name:0:$((${#name}-10))} -> ${name:start:len}

  • Trong lệnh trên, tên là biến.
  • start là điểm bắt đầu của chuỗi
  • len là độ dài của chuỗi phải được loại bỏ.

Thí dụ:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

Đầu ra:

    Enter:Siddharth Murugan
    Siddhar

Lưu ý: Bash 4.2 đã thêm hỗ trợ cho chuỗi con âm


Điều này dài dòng hơn nhiều so với thay thế mẫu đơn giản tương thích Bourne như trong câu trả lời của Etan Reisner.
tripleee

0

Điều này làm việc cho tôi bằng cách tính kích thước của chuỗi.
Thật dễ dàng bạn cần phải lặp lại giá trị bạn cần trả lại và sau đó lưu trữ nó như dưới đây

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?

một số chuỗi


0

Trong trường hợp này, bạn có thể sử dụng tên cơ sở giả sử bạn có cùng hậu tố trên các tệp bạn muốn xóa.

Thí dụ:

basename -s .rtf "some string.rtf"

Điều này sẽ trả về "một số chuỗi"

Nếu bạn không biết hậu tố và muốn xóa mọi thứ sau và kể cả dấu chấm cuối cùng:

f=file.whateverthisis
basename "${f%.*}"

đầu ra "tập tin"

% có nghĩa là chặt ,. là những gì bạn đang cắt, * là ký tự đại diện

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.