Chuỗi Bash thay thế nhiều ký tự bằng một


8

Tôi đang thay thế, từ một tiêu đề nguồn cấp dữ liệu, tất cả các ký tự ngoại trừ chữ cái và chữ số bằng dấu gạch ngang để sử dụng kết quả làm tên tệp an toàn cho bất kỳ hệ thống tệp nào:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ echo ${t//[^A-Za-z0-9]/-}
Episodie-06--No-hope-of-riding-home--NEW----Advanced-grammar

Tuy nhiên, tôi muốn ngưng tụ tất cả các dấu gạch ngang lặp lại với một dấu như Episodie-06-No-hope-of-riding-home-NEW-Advanced-grammar

Tôi thấy tôi có thể đạt được nó bằng cách sử dụng thay thế hai lần:

$ t="Episodie 06: No hope of riding home (NEW) - Advanced grammar"
$ tmp=${t//[^A-Za-z0-9]/-}
$ echo ${tmp//--/-}
Episodie-06-No-hope-of-riding-home-NEW--Advanced-grammar

Tôi nghĩ rằng tôi có thể làm điều đó trong một lần như:

$ echo ${t//[^A-Za-z0-9]+/-}

nhưng nó không hoạt động.

Có manh mối nào không?

Lưu ý: Tôi không muốn đi cùng sedhoặc các công cụ khác

Câu trả lời:


8

Bạn cần một cái gì đó mạnh mẽ hơn so với các ký tự đại diện vỏ truyền thống. Trong bash, đặt extglobtùy chọn, cho phép bạn truy cập vào các biểu thức thông thường trong các mẫu toàn cục thông qua một cú pháp bất thường được kế thừa từ ksh.

shopt -s extglob
sanitized=${raw//+([^A-Za-z0-9])/-}

Cảm ơn, đã có một nhận xét từ fered dưới câu trả lời jw013 với giải pháp này. Một số thông tin về khả năng tương thích với các shell khác của cú pháp này? Tôi không quan tâm đến nó nhiều lắm, chỉ để biết thêm về shoptvà vỏ nào hỗ trợ nó.
neurino

@neurino shoptlà đặc trưng cho bash. Cú pháp mẫu mà nó kích hoạt luôn có sẵn trong tất cả các biến thể ksh. Trong zsh, cú pháp này phải được kích hoạt với setopt ksh_glob. POSIX không có tính năng như vậy, các ký tự đại diện của nó kém mạnh hơn regexps. Các loại vỏ khác ngoài bash / ksh / zsh, trong thực tế hầu hết có nghĩa là tro ngày nay, có xu hướng dính vào các ký tự đại diện POSIX.
Gilles 'SO- ngừng trở nên xấu xa'

tốt, tại thời điểm này tôi thích khả năng tương thích và linh hoạt hơn với chi phí cao hơn một chút : echo "$t" | sed -r 's/[^[:alnum:]]+/-/g; s/^-|-$//'. Tôi chấp nhận câu trả lời của bạn vì nó chính xác làm những gì được hỏi trong câu hỏi.
neurino

@neurino Nếu bạn muốn tính di động cho các shell khác, thì bạn có thể đi với câu trả lời của glenn jackman . Nhân tiện, lưu ý rằng ${var/PATTERN/REPLACEMENT}cấu trúc cũng đặc trưng cho ksh / bash / zsh.
Gilles 'SO- ngừng trở nên xấu xa'

Tôi thích sedvì tôi biết rõ hơn cú pháp và hành vi của nó, tôi có thể dễ dàng thêm một câu lệnh để loại bỏ dấu gạch ngang bắt đầu / dấu, tôi không cần quan tâm đến \nchar. Là sedcách ít có sẵn hơn tr?
neurino

6

tr là một công cụ tốt cho công việc này

new=$( printf "%s" "$t" | tr -cs 'a-zA-Z0-9' '-' )
new=${new#-}; new=${new%-}

Cảm ơn bạn, +1, tôi không bao giờ nhớ về tr... Tuy nhiên tôi đã cố gắng hoàn thành nó ở Bash, nếu không tôi sẽ đi với sed:echo "$t" | sed -r 's/[^A-Za-z0-9]+/-/g'
neurino

Bỏ phiếu vì nó mâu thuẫn vớiNote: I don't want to go with sed or other tools
Paul Calabro

3

Nếu bạn muốn ở lại với bash thuần túy, bạn sẽ phải giải quyết cho giải pháp hai lượt. Thay thế chuỗi Bash sử dụng những đống , như trong việc mở rộng tên đường dẫn, và không biểu thức thông thường. Các ký tự đặc biệt trong những đống là *, ?[], có thô tương đương trong biểu thức thông thường là .*, ., và []. Hãy xem wiki Wooledge và các phần trang nam trên và để biết thêm thông tin.bash(1)Parameter ExpansionPathname Expansion

Giống như một nhận xét, việc mở rộng hai lượt trong bash thuần vẫn có khả năng nhanh hơn so với cố gắng làm điều tương tự bằng cách gọi một chương trình bên ngoài, vì vậy tôi sẽ không lo lắng về điều đó quá nhiều.


Cảm ơn, tôi sẽ kiểm tra liên kết. Điều lo lắng của tôi là tôi phải thực hiện công việc này nhiều hơn một lần trong toàn bộ kịch bản nên mối quan tâm duy nhất của tôi là về việc có cùng một mã được lặp đi lặp lại và làm ảnh hưởng đến khả năng đọc. Dù sao tôi cũng đang đưa ra một giải pháp lịch sự mà tôi sẽ đăng. Chúc mừng
neurino

Bạn có thể đặt mã đó vào một hàm để tránh lặp lại mã.
jw013

Đó là những gì tôi đang làm nhưng, như bạn biết, các hàm bash không thể trả về chuỗi ... hoặc, ít nhất, đó là những gì tôi nghĩ trước 10 phút trước :)
neurino

4
Dưới đây là một số ví dụ với do-s và-đừng-s - Bash Extended Globbing .. Đối với ví dụ trên, nó sẽ là:shopt -s extglob; t="${t//+([^A-Za-z0-9])/-}"
Peter.O

1
@fered: cảm ơn bạn, rất thú vị, tôi sẽ kiểm tra nó. Url liên kết của bạn có thêm char và trả về 404, cái đang hoạt động là Bash Extended Globbing
neurino
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.