Áp dụng mở rộng cú đúp theo thứ tự đảo ngược


21

Ví dụ {a..c}{1..3}mở rộng đến a1 a2 a3 b1 b2 b3 c1 c2 c3.

Nếu tôi muốn in a1 b1 c1 a2 b2 c2 a3 b3 c3, có một cách tương tự để làm điều đó? Cách đơn giản nhất là gì?

Câu trả lời:


30

Bạn có thể làm:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Mà sau đó nói với vỏ để đánh giá:

echo {a..c}1 {a..c}2 {a..c}3

10

Đối với trường hợp cụ thể này, tôi nghĩ rằng tùy chọn được đưa ra bởi Stéphane Chazelas là lựa chọn tốt nhất.

Mặt khác, khi bạn mở rộng những thứ phức tạp hơn, tùy chọn này không mở rộng tốt. Vì vậy, bạn có thể đạt được điều tương tự với điều này:

$ printf '%s\0' {a..c}{1..3} | sort -zk 1.2,1.2 | tr '\0' ' '

Trả về:

a1 b1 c1 a2 b2 c2 a3 b3 c3

Có vẻ hơi lộn xộn, nhưng bây giờ, tôi có một sự kiểm soát lớn theo thứ tự, chỉ cần thay đổi hai ký tự trong lệnh trên; ví dụ:

$ echo {a..b}{1..2}{a..b}{1..2}

điều này sẽ mở rộng đến:

a1a1 a1a2 a1b1 a1b2 a2a1 a2a2 a2b1 a2b2 b1a1 b1a2 b1b1 b1b2 b2a1 b2a2 b2b1 b2b2

Giả sử tôi muốn tất cả 1trong bản mở rộng thứ hai, sau đó 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.2 | tr '\0' ' '
a1a1 a1a2 a1b1 a1b2 b1a1 b1a2 b1b1 b1b2 a2a1 a2a2 a2b1 a2b2 b2a1 b2a2 b2b1 b2b2

Giả sử tôi muốn tất cả atrong bản mở rộng thứ ba, sau đó b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.3,1.3 | tr '\0' ' '
a1a1 a1a2 a2a1 a2a2 b1a1 b1a2 b2a1 b2a2 a1b1 a1b2 a2b1 a2b2 b1b1 b1b2 b2b1 b2b2

Giả sử tôi muốn tất cả các 1bản mở rộng thứ tư, sau đó 2:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.4,1.4 | tr '\0' ' '
a1a1 a1b1 a2a1 a2b1 b1a1 b1b1 b2a1 b2b1 a1a2 a1b2 a2a2 a2b2 b1a2 b1b2 b2a2 b2b2

Giả sử tôi muốn tất cả 1aở giữa, sau 1bđó 2a, sau đó 2b:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -zk 1.2,1.3 | tr '\0' ' '
a1a1 a1a2 b1a1 b1a2 a1b1 a1b2 b1b1 b1b2 a2a1 a2a2 b2a1 b2a2 a2b1 a2b2 b2b1 b2b2

Bạn thậm chí có thể dễ dàng, đảo ngược mọi thứ tự trong các mở rộng ở trên, chỉ cần thêm một rlệnh vào trước đó; ví dụ: cái cuối cùng:

$ printf '%s\0' {a..b}{1..2}{a..b}{1..2} | sort -rzk 1.2,1.3 | tr '\0' ' '
b2b2 b2b1 a2b2 a2b1 b2a2 b2a1 a2a2 a2a1 b1b2 b1b1 a1b2 a1b1 b1a2 b1a1 a1a2 a1a1

Lưu ý_1 : thông thường, nếu bản mở rộng cuối cùng này sẽ được sử dụng làm danh sách các đối số, không gian dấu không phải là vấn đề; nhưng nếu bạn muốn loại bỏ nó, bạn có thể thêm vào bất kỳ lệnh nào ở trên| sed 's/ $//' ; hoặc thậm chí| sed 's/ $/\n/', để thay đổi không gian đó cho mộtnewline

Lưu ý_2 : Trong các ví dụ ở trên, tôi đã sử dụng các tập hợp con của hai phần tử (ví dụ: {a, b} {1,2} ) chỉ để đơn giản trong chứng minh khái niệm: bạn có thể sử dụng các tập hợp con có độ dài hữu hạn và lệnh tương ứng, sẽ được so sánh.


5

bash, ksh, zsh

Một lớp lót hoạt động trong (bash, ksh, zsh) (không phải tất cả các shell đều có thể thực hiện "Mở rộng cú đúp" theo thứ tự ngược lại):

$ echo {3..1}{c..a} | rev
a1 b1 c1 a2 b2 c2 a3 b3 c3

Một thay thế sử dụng eval(vẫn còn cho bash, ksh, zsh và có thể khó hiểu hơn) là:

$ eval echo '{a..c}'{1..3}
a1 b1 c1 a2 b2 c2 a3 b3 c3

Để hiểu những gì xảy ra, thay thế evalbằng echo:

$ echo echo '{a..c}'{1..3}
echo {a..c}1 {a..c}2 {a..c}3

Lệnh được thực thi (sau khi mở rộng eval) là thực sự echo {a..c}1 {a..c}2 {a..c}3. Mà mở rộng như bạn muốn / cần.

tất cả vỏ

Có một số vỏ mà không có "mở rộng nẹp", vì vậy, không thể sử dụng nó cho "tất cả các vỏ". Chúng ta cần một vòng lặp (với một khoảng trắng ở cuối):

$ for i in 1 2 3; do for j in a b c; do printf "%s%s " "$j" "$i"; done; done; echo
a1 b1 c1 a2 b2 c2 a3 b3 c3 

Nếu bạn không có dấu cách được thêm vào:

s=""
for i in 1 2 3; do
    for j in a b c; do
        printf "%s%s%s" "$s" "$j" "$i"
        s=" "
    done
done
echo

Bản in

a1 b1 c1 a2 b2 c2 a3 b3 c3

NẾU bạn cần làm điều này cho nhiều giá trị, chúng ta cần sử dụng một cái gì đó tương tự như mở rộng dấu ngoặc để tạo danh sách các số $(seq 10). Và, vì seq không thể tạo danh sách các chữ cái, chúng tôi cần chuyển đổi thành ascii các số được tạo:

s=""
for i in $(seq 4); do
    for j in $(seq 5); do
        printf "%s\\$(printf %03o $((96+j)))%s" "$s" "$i"
        s=" "
    done
done
echo

in:

a1 b1 c1 d1 e1 a2 b2 c2 d2 e2 a3 b3 c3 d3 e3 a4 b4 c4 d4 e4

Bạn cũng có thể thêm yash -o braceexpandvào danh sách.
Stéphane Chazelas

@ StéphaneChazelas Tôi không chắc là tôi nên. Lệnh yash -o braceexpand -c 'echo {3..1}{c..a}'in 3{c..a} 2{c..a} 1{c..a}trong linux. Không phải là một "mở rộng cú đúp" đầy đủ.
Isaac

3
{a..c}1 {a..c}2 {a..c}3

Các mở rộng dấu ngoặc trong {a..c}{1..3}được mở rộng từ trái sang phải, vì vậy trước tiên bạn nhận được a{1..3} b{1..3} c{1..3}và sau đó các chữ cái được kết hợp với các số thành a1 a2 a3 b1 b2 b3 c1 c2 c3. Để có được thứ tự bạn muốn, bạn sẽ phải sử dụng biểu thức dài hơn một chút ở trên.


Nếu bạn muốn làm điều đó cho một số lượng lớn "số" thì nó sẽ không còn thực tế nữa.
RUBEN GONÇALO MOROUÇO

3
@ RUBENGONÇALOMOROUÇO Không, và nếu bạn đang thực hiện nó cho một số lượng lớn, tôi sẽ đề nghị sử dụng một phương pháp thay thế, như một vòng lặp kép. Điều đó sẽ làm việc cho hàng ngàn kết hợp, trong khi một cú đúp mở rộng "danh sách đối số quá dài" của tôi trong các ngữ cảnh nhất định.
Kusalananda

2

Sử dụng một vòng lặp:

for n in {1..3}; do printf '%s\n' {a..c}"$n"; done

Điều này sẽ lặp qua lần mở rộng đầu tiên của bạn và sau đó mở rộng từng ký tự với lần thứ hai.

Nếu bạn cần tất cả đầu ra trên một dòng, bạn có thể xóa \n:

for n in {1..3}; do printf '%s ' {a..c}"$n"; done

Điều này sẽ không cung cấp cho bạn một dòng mới nhưng nếu bạn chuyển nó đến một lệnh hoặc biến không phải là một vấn đề.


1
Tại sao rất nhiều phiếu giảm cho một giải pháp vòng lặp?
Isaac

Duh tôi phải đọc câu hỏi sai. Đã cập nhật
Jesse_b

2

Điều này làm việc cho trường hợp đơn giản của bạn và có thể được mở rộng, nhưng nó sẽ nhanh chóng ra khỏi tầm tay. Các trường hợp phức tạp hơn mà điều này sẽ không hoạt động dễ dàng để xây dựng.

Đảo ngược thứ tự mở rộng của nẹp, sau đó hoán đổi các ký tự:

echo {1..3}{a..c} | sed -E 's/(.)(.)( ?)/\2\1\3/g'

@muru: Rất tiếc. Đã sửa. Cảm ơn.
Tạm dừng cho đến khi có thông báo mới.

@Isaac: Tôi đã sửa lỗi dấu cách.
Tạm dừng cho đến khi có thông báo mới.

0

Một phương pháp đơn giản sẽ là sử dụng sort (1.2,1.2 có nghĩa là bạn lấy một ký tự ở vị trí thứ hai và kết thúc tại cùng một vị trí).

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2
a1
b1
c1
a2
b2
c2
a3
b3
c3

Nếu bạn muốn chúng trong một dòng, bạn có thể sử dụng tr như vậy:

$ for i in {a..c}{1..3}; do echo $i; done|sort -n -k1.2,1.2|tr '\n' ' '
a1 b1 c1 a2 b2 c2 a3 b3 c3

-2

Thực hiện theo phương pháp dưới đây

for i in {1..10}; do for j in {a..c}; do echo $j$i; done; done| perl -pne "s/\n/ /g"

đầu ra

a1 b1 c1 a2 b2 c2 a3 b3 c3 a4 b4 c4 a5 b5 c5 a6 b6 c6 a7 b7 c7 a8 b8 c8 a9 b9 c9 a10 b10 c10

cũng xem xétfor i in {1..10}; do for j in {a..c}; do printf '%s ' "$j$i"; done; done;echo
Jeff Schaller
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.