Chỉ trong bash, không có lệnh bên ngoài:
str="foobarbazblargblurg"
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
hoặc là phiên bản ống một dòng:
echo foobarbazblargblurg |
{ IFS= read -r str; [[ $str =~ ${str//?/(.)} ]]; \
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"; }
Cách thức hoạt động này là chuyển đổi từng ký tự của chuỗi thành "(.)" Để khớp regex và chụp với =~
, sau đó chỉ xuất các biểu thức đã chụp từ BASH_REMATCH[]
mảng, được nhóm theo yêu cầu. Không gian hàng đầu / dấu / trung gian được giữ nguyên, loại bỏ các trích dẫn xung quanh "${BASH_REMATCH[@]:1}"
để bỏ qua chúng.
Ở đây, nó được gói trong một hàm, cái này sẽ xử lý các đối số của nó hoặc đọc stdin nếu không có đối số:
function fmt4() {
while IFS= read -r str; do
[[ $str =~ ${str//?/(.)} ]]
printf "%s%s%s%s " "${BASH_REMATCH[@]:1}"
done < <( (( $# )) && printf '%s\n' "$@" || printf '%s\n' $(< /dev/stdin) )
}
$ echo foobarbazblargblurg | fmt4
foob arba zbla rgbl urg
Bạn có thể dễ dàng tham số đếm để điều chỉnh chuỗi định dạng cho phù hợp.
Một không gian dấu được thêm vào, sử dụng hai printf
s thay vì một nếu đó là một vấn đề:
printf "%s%s%s%s" "${BASH_REMATCH[@]:1:4}"
(( ${#BASH_REMATCH[@]} > 5 )) && printf " %s%s%s%s" "${BASH_REMATCH[@]:5}"
Bản printf
in đầu tiên (tối đa) 4 ký tự đầu tiên, bản thứ hai có điều kiện in tất cả phần còn lại (nếu có) với khoảng trắng ở đầu để phân tách các nhóm. Bài kiểm tra dành cho 5 phần tử chứ không phải 4 để tính phần tử zeroth.
Ghi chú:
- vỏ
printf
's %c
có thể được sử dụng thay cho %s
, %c
(có thể) làm cho mục đích rõ ràng hơn, nhưng nó không phải là đa byte nhân vật an toàn. Nếu phiên bản bash của bạn có khả năng, thì trên đây là tất cả các ký tự nhiều byte an toàn.
- shell
printf
sử dụng lại chuỗi định dạng của nó cho đến khi hết các đối số, do đó, nó chỉ cần xử lý 4 đối số cùng một lúc và xử lý các đối số theo sau (vì vậy không có trường hợp cạnh nào cần thiết, không giống như một số câu trả lời khác ở đây có thể được cho là sai)
BASH_REMATCH[0]
là toàn bộ chuỗi khớp, vì vậy chỉ có đầu ra bắt đầu từ chỉ mục 1
printf -v myvar ...
thay vào đó sử dụng để lưu trữ vào một biến myvar
(theo hành vi vòng lặp đọc / chuỗi con thông thường)
- thêm
printf "\n"
nếu cần
Bạn có thể làm cho các công việc trên hoạt động zsh
nếu bạn sử dụng mảng match[]
thay vì BASH_REMATCH[]
và trừ 1 khỏi tất cả các chỉ mục vì zsh
không giữ nguyên phần tử 0 với toàn bộ kết quả khớp.
sed
tôi đã thử trước tiên tôi có thể tự đá mình.