Làm cách nào để 'thả' / xóa các ký tự từ trước chuỗi?


12

Tôi có một chuỗi mà tôi muốn thao tác. Chuỗi là H08W2345678làm thế nào tôi có thể thao tác nó để đầu ra chỉ là W2345678?

Tương tự như vậy nếu tôi muốn thả 4 ký tự cuối cùng H08W2345678để tôi có H08W234thể làm điều này như thế nào?


1
Có nhiều cách để thao tác chuỗi. Có một lý do cụ thể để sử dụng sed?
don_crissti

@don_crissti Không có lý do, ngoài việc thiếu kinh nghiệm. Mọi sự thay thế đều được chào đón ...
3kstc

@don_crissti, câu chuyện: từ một tệp CSV đã được lọc, tôi lấy một trong các tham số từ một dòng H08W2345678và cần thao tác với W2345678giá trị này với dữ liệu khác sẽ được gửi vào một email được gửi đi. Gửi email sẽ được thực hiện với cron.
3kstc

@don_crissti awking nó. Tôi tạo một mảng và sau đó sửa đổi từng thành phần trong mảng (tất cả đều khác nhau - tức là thay đổi thời gian biểu của Epoch trong vài giây thành một ngày, v.v.)
3kstc

2
Bạn có thể làm những việc như thế với awk:printf %s\\n "XX,H08W2345678,YY" | awk -F, '{print substr($2, 4); print substr($2, 1, length($2)-4)}'
don_crissti

Câu trả lời:


18

Chỉ sử dụng bash (hoặc ksh93cú pháp đó đến từ hoặc zsh):

string="H08W2345678"

echo "${string:3}"
W2345678

echo "${string:0:-4}"
H08W234

Xem wiki Wooledge để biết thêm về thao tác chuỗi .


Điều này đòi hỏi bash 4.2 trở lên. Xem bản sao cũ này của Tài liệu tham khảo Bash, Phần 3.5.3, '' Mở rộng tham số Shell '' hoặc câu trả lời của gà con ở đây để xem ràng buộc cũ ( Độ dài đường truyền phải đánh giá một số lớn hơn hoặc bằng 0..) Tiết (Cont'd)
Scott

(Tiếp theo) ... thấy những thay đổi Bash (tại Bash Hacker Wiki) (di chuyển xuống phía dưới cùng của phần này) hoặc tin tức bash tại tổ chức dịch vụ cơ sở hạ tầng công nghệ tại Case Western Reserve University (tìm kiếm “thêm vào bash-4.2” và sau đó cuộn xuống xuống q .vv) để xem bản sửa đổi. Sự khó khăn khi chơi trò chơi  "${string:0:${#string}-4}" trong phiên bản bash 4.1 miễn là độ dài $stringít nhất là 4.
Scott

PS Điều này cũng sẽ gây nghẹt thở cho các chuỗi như abc-e, trong đó, khi bạn thả ba ký tự đầu tiên, bạn sẽ bị bỏ lại -e(vì echo -ekhông làm những gì bạn muốn).
Scott

8
$ echo "H08W2345678" | sed 's/^.\{3\}//'
W2345678

sed 's/^.\{3\}//'sẽ tìm thấy ba ký tự đầu tiên bằng cách ^.\{3\}thay thế bằng khoảng trống. Ở đây ^.sẽ khớp với bất kỳ ký tự nào ở đầu chuỗi ( ^cho biết bắt đầu chuỗi) và \{3\}sẽ khớp với mẫu trước đó chính xác 3 lần. Vì vậy, ^.\{3\}sẽ phù hợp với ba nhân vật đầu tiên.

$ echo "H08W2345678" | sed 's/.\{4\}$//'
H08W234

Tương tự, sed 's/.\{4\}$//'sẽ thay thế bốn ký tự cuối cùng bằng khoảng trống ( $biểu thị phần cuối của chuỗi).


1
Bạn có thể vui lòng giải thích 's/^.\{3\}//''s/.\{4\}$//'vì tôi vẫn đang học sed, cảm ơn rất nhiều
3kstc

@ 3kstc: Vui lòng kiểm tra các chỉnh sửa
heemayl

1
Đối với chỉ một vài ký tự, tôi sẽ sử dụng ...thay .\{3\}vì (đối với tôi) nó dễ đọc hơn: sed -e 's/^...//' -e 's/....$//' hoặc trong một biểu thức có xen kẽ : sed -r 's/^...|....$//g'. Nếu có nhiều hơn một vài ký tự để xóa, thì tôi sẽ sử dụng /.\{17}\/biểu thức thay vì /.............../.
Johnny

Điều này sẽ hành xử xấu nếu chuỗi là -ehoặc -n. Tất nhiên, ý nghĩa của “thả 4 ký tự cuối cùng” là undefined cho một chuỗi ngắn hơn 4 ký tự, nhưng, nếu ai đó muốn thích ứng này để thả người đầu tiên hoặc cuối cùng một nhân vật, nó có thể nổ tung.
Scott

2

Nếu bạn có một tệp trong đó mỗi dòng là một chuỗi mười một ký tự (hoặc bất cứ thứ gì) mà bạn muốn cắt, sedlà công cụ để sử dụng. Nó tốt cho việc thao tác một chuỗi, nhưng nó quá mức cần thiết. Đối với một chuỗi, câu trả lời của Jason có lẽ là tốt nhất, nếu bạn có quyền truy cập vào bash phiên bản 4.2 trở lên. Tuy nhiên, các cú pháp và dường như là duy nhất đối với bash (tốt, bash, ksh93, mksh và zsh) - Tôi không thấy chúng trong Thông số kỹ thuật cơ sở nhóm mở cho Ngôn ngữ lệnh Shell . Nếu bạn bị mắc kẹt với lớp vỏ tuân thủ POSIX không hỗ trợ mở rộng chuỗi con (trích xuất), bạn có thể sử dụng${parameter:offset}${parameter:offset:length}

$ printf "%s\n" "${string#???}"
W2345678

$ printf "%s\n" "${string%????}"
H08W234

sử dụng printfthay vì echođể bảo vệ chống lại các chuỗi như abc-e, trong đó, khi bạn thả ba ký tự đầu tiên, bạn sẽ bị bỏ lại -e (và echo -ekhông làm những gì bạn muốn).

Và, nếu bạn hoàn toàn không sử dụng vỏ gia đình Bourne (hoặc bạn đang sử dụng hệ thống POSIX cổ xưa), thì chúng vẫn hoạt động:

$ expr " $string" : ' ...\(.*\)'
W2345678

$ expr " $string" : ' \(.*\)....'
H08W234

Các không gian hàng đầu thêm là để tránh các vấn đề với các giá trị của $string điều đó là thực tế exprkhai thác (ví dụ +,  /,  indexhoặc match) hoặc tùy chọn (ví dụ  --, --helphoặc  --version).


@ Stéphane Chazelas: (1) Cảm ơn bạn đã nhắc nhở tôi về một cạm bẫy mà tôi biết khoảng 40 năm trước và bằng cách nào đó đã quên đi. (2) Tôi luôn luôn sử dụng để giải quyết điều này với X; ví dụ expr "X$string" : 'X...\(.*\)'. IMO, dễ đọc và dễ hiểu hơn. Có bất kỳ vấn đề với điều đó, hoặc bất kỳ lý do để thích một không gian? (3) Hôm nay tôi đã học được rằng expr + "$string" : '...\(.*\)'bây giờ làm việc. Tôi không nhớ điều đó từ 40 năm trước; nó có đủ được sử dụng rộng rãi để an toàn để giới thiệu không? (4) Bạn đã bỏ lỡ một ghi chú về câu trả lời của jasonwryan và một câu trả lời về câu trả lời của heemayl.
Scott

AFAIK, đó chỉ expr +là GNU (sẽ không hoạt động trên Solaris hay FreeBSD AFAICS). Tôi sử dụng không gian thay vì x vì ít có khả năng một số exprtriển khai sẽ có các toán tử bắt đầu bằng không gian hơn xvà cũng vì ít có khả năng đối chiếu bắt đầu với không gian hơn so với x. Nhưng sau đó tôi nhận ra rằng nó có thể không phải là một lựa chọn tốt expr " $a" "<" " $b"để so sánh chuỗi vì một số triển khai cuối cùng thực hiện so sánh số khi $a/ $btrông giống như số. Có thể expr "@@$a"...hoặc expr "x $a"có thể an toàn hơn.
Stéphane Chazelas

0

Với:

string="H08W2345678"

Ghép 3 hoặc 4 ký tự có vẻ đơn giản (đối với hầu hết các shell):

$ printf '%s\t%s\n' "${string#???}" "${string%????}"
W2345678      H08W234

Đối với các vỏ cũ hơn (như vỏ Bourne), hãy sử dụng:

$ string=H08W2345678

$ expr " ${string}" : " ...\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\)...." '
H08W234

Nếu cần số lượng ký tự, hãy sử dụng:

$ expr " ${string}" : " .\{3\}\(.*\)"
W2345678

$ expr " ${string}" : " \(.*\).\{4\}" '
H08W234

Tất nhiên, những regex đó cũng hoạt động với sed, awk và bash 3.0+:

$ echo "$string" | sed 's/^.\{3\}//'
W2345678

$ echo "$string" | sed 's/.\{4\}$//'
H08W234

$ echo "$string" | awk '{sub(/^.{3}/,"")}1'
W2345678

$ echo "$string" | awk '{sub(/.{4}$/,"")}1'
H08W234

$ r='^.{3}(.*)$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
W2345678

$ r='^(.*).{4}$'; [[ $a =~ $r ]] && echo "${BASH_REMATCH[1]}"
H08W234

-1

Làm cách nào để 'thả' / xóa các ký tự từ trước chuỗi?

Tôi có một chuỗi mà tôi muốn thao tác. Chuỗi là H08W2345678 làm thế nào tôi có thể thao tác nó để đầu ra chỉ là W2345678?

echo "H08W2345678" | cut -c 4-

Điều này chỉ trả lời một nửa câu hỏi.
Kusalananda

Tôi tin rằng downvote của bạn là không công bằng. Nửa này trả lời câu hỏi tôi có khi tôi googled posix loại bỏ các ký tự đầu tiên và trang này hiển thị trong kết quả tìm kiếm. Hơn nữa, tiêu đề trang này chỉ bao gồm một nửa chính xác của câu hỏi. Tôi đã trở lại và đóng góp khi tôi tìm thấy giải pháp tôi thích - Tôi nghĩ rằng công việc đó cutthanh lịch hơn nhiều so với bất kỳ điều gì khác trên trang này.
aexl
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.