Khi đầu ra của cal
lệnh không phải là thiết bị đầu cuối, nó sẽ áp dụng gạch chân của người nghèo vào số ngày cho ngày hôm nay, bao gồm đặt dấu gạch dưới và ký tự lùi trước mỗi ký tự để gạch chân. Bạn có thể thấy điều đó bằng cách hiển thị các ký tự một cách trực quan ( ^H
có nghĩa là control-H , là ký tự backspace):
cal | cat -A
cal | cat -vet
hoặc bằng cách nhìn vào một bãi chứa hex:
cal | hd
cal | od -t x1
Vì vậy, những gì bạn cần là phát hiện các ký tự được gạch chân và xuất chúng.
Với GNU grep, có một cách dễ dàng để in tất cả các kết quả khớp của biểu thức chính quy: sử dụng -o
tùy chọn. Một nhân vật gạch dưới được kết hợp bởi các biểu thức chính quy mở rộng _^H.
nơi ^H
là một nhân vật xóa lùi theo nghĩa đen, không phải là hai nhân vật ^
và H
, và .
là nhân vật để in. Thay vì gõ ký tự backspace, bạn có thể dựa vào thực tế rằng đây là cách duy nhất cal sử dụng dấu gạch dưới trong đầu ra của nó. Vậy là đủ để phát hiện các dấu gạch dưới và để lại các khoảng trống dưới dạng các ký tự chưa từng có.
cal | grep -o '_..'
Chúng tôi đang đóng, nhưng đầu ra chứa chuỗi dấu gạch dưới gạch dưới và các chữ số nằm trên các dòng riêng biệt. Bạn có thể loại bỏ tất cả các ký tự không có chữ số (và thêm lại một dòng mới):
cal | grep -o '_..' | tr -d 0-9; echo
Ngoài ra, bạn có thể lặp lại mẫu _..
để khớp với nhiều chữ số được gạch chân. Điều này để lại phần gạch chân trong đầu ra, bạn có thể sử dụng tr hoặc sed để loại bỏ nó.
cal | grep -E -o '(_..)*'
cal | grep -E -o '(_..)*' | tr -d '\b_'
cal | grep -E -o '(_..)*' | sed 's/_.//g'
Bạn có thể làm điều này với sed, nhưng nó không hoàn toàn đơn giản. Sed cung cấp một cách dễ dàng để chỉ in các dòng khớp (sử dụng -n
tùy chọn để chỉ nhận các dòng được in rõ ràng), nhưng không có cách trực tiếp để in nhiều lần xuất hiện của một trận đấu trên một dòng. Một cách để giải quyết điều này là tận dụng thực tế là có nhiều nhất hai ký tự được gạch chân và có một s
lệnh để chuyển đổi và xuất các dòng chứa một ký tự được gạch chân và một ký tự khác cho hai dòng. Như trước đây, tôi sẽ không khớp với các không gian rõ ràng.
cal | sed -n 's/.*_.\(.\)_.\(.\).*/\1\2/p; s/.*_.\(.\).*/\1/p'
Một cách tiếp cận khác với sed, giả sử rằng chỉ có một phân đoạn được gạch chân trên một dòng, là loại bỏ mọi thứ trước nó và mọi thứ sau nó.
cal | sed -n 's/^[^_]*_/_/; s/\(_..\)[^_]*$/\1/p'
Điều này để lại các dấu gạch dưới; chúng ta có thể loại bỏ chúng với sự thay thế thứ ba.
cal | sed -n 's/^[^_]*_/_/; s/\(_..\)[^_]*$/\1/; s/_.//gp'