2013-09-04 80 views
5

Tôi đang cố sắp xếp số lượng tệp theo đầu ra bằng lệnh ls khớp với mẫu ABCDE1234A1789.RST.txt hoặc ABCDE12345A1789.RST.txt bởi trường '789'.Sử dụng lệnh sắp xếp bash trong tên tệp có độ dài biến đổi

Trong ví dụ mẫu ở trên, ABCDE giống nhau cho tất cả các tệp, 1234 hoặc 12345 là các chữ số thay đổi nhưng luôn có 4 hoặc 5 chữ số. A1 có cùng độ dài cho tất cả các tệp, nhưng giá trị có thể khác nhau do đó rất tiếc, không thể sử dụng giá trị đó làm dấu phân cách. Mọi thứ sau . đầu tiên đều giống nhau cho tất cả các tệp. Một cái gì đó như:

ls -l *.RST.txt | sort -k +9.13 | awk '{print $9} ' > file-list.txt 

sẽ phù hợp với tên tập tin ngắn hơn nhưng không phải là những lâu hơn vì độ dài biến của nhân vật trước khi lĩnh vực này tôi muốn sắp xếp theo.

Có cách nào để thực hiện sắp xếp tất cả các tệp mà không đệm các tệp có độ dài ngắn hơn để làm cho chúng có cùng độ dài không?

+1

FYI - lệnh 'sắp xếp' không phải là một phần của bash, mà là tiện ích UNIX chuẩn. Như vậy, nó có sẵn cho bất kỳ chương trình (và bất kỳ trình bao). –

Trả lời

4

Perl để giải cứu!

perl -e 'print "$_\n" for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"' 

Nếu perl của bạn là gần đây (5.10 hoặc mới hơn), bạn có thể rút ngắn nó để

perl -E 'say for sort { substr($a, -11, 3) cmp substr($b, -11, 3) } glob "*.RST.txt"' 
+0

Cảm ơn bạn choroba (và cảm ơn bạn, perl). Sử dụng phiên bản trước 5.10, do đó, dòng trên cùng hoạt động hoàn hảo cho việc này. –

2

Cách thông thường để làm điều này trong bash là để trích xuất lĩnh vực sắp xếp của bạn. Ngoại trừ các lệnh loại, sau đây được thực hiện trong bash tinh khiết một mình:

sort_names_by_first_num() { 
    shopt -s extglob 
    for f; do 
    first_num="${f##+([^0-9])}"; 
    first_num=${first_num%[^0-9]*}; 
    [[ $first_num ]] && printf '%s\t%s\n' "$first_num" "$f" 
    done | sort -n | while IFS='' read -r name; do name=${name#*$'\t'}; printf '%s\n' "$name"; done 
} 

sort_names_by_first_num *.RST.txt 

Điều đó nói rằng, newline-phân định tên tập tin (như câu hỏi này dường như để kêu gọi) là một thói quen xấu: Tên tập tin trên hệ thống tập tin UNIX được phép chứa các dòng mới trong tên của chúng, vì vậy việc tách chúng bằng các dòng mới trong một danh sách có nghĩa là danh sách của bạn không thể chứa một tập hợp con đáng kể của phạm vi các tên hợp lệ. Thực hành tốt hơn là NUL-delimit danh sách của bạn. Làm điều đó sẽ trông giống như vậy:

sort_names_by_first_num() { 
    shopt -s extglob 
    for f; do 
    first_num="${f##+([^0-9])}"; 
    first_num=${first_num%[^0-9]*}; 
    [[ $first_num ]] && printf '%s\t%s\0' "$first_num" "$f" 
    done | sort -n -z | while IFS='' read -r -d '' name; do name=${name#*$'\t'}; printf '%s\0' "$name"; done 
} 

sort_names_by_first_num *.RST.txt 
+0

Cảm ơn, Charles, vì một giải pháp thay thế toàn diện. Trong trường hợp sử dụng này, perl có sẵn cho người dùng và nó có thể dễ dàng hơn để thực hiện hơn so với hàm bash, nhưng tôi rất nhiều đánh giá cao tùy chọn! –

3

Bởi vì các bộ phận của tên tập tin mà bạn đã xác định là không thay đổi, bạn thực sự có thể xây dựng một chìa khóa mà loại sẽ sử dụng:

$ echo ABCDE{99999,8765,9876,345,654,23,21,2,3}A1789.RST.txt \ 
    | fmt -w1 \ 
    | sort -tE -k2,2n --debug 
ABCDE2A1789.RST.txt 
    _ 
___________________ 
ABCDE3A1789.RST.txt 
    _ 
___________________ 
ABCDE21A1789.RST.txt 
    __ 
etc. 

Điều này không được phân loại để tách các trường trên ký tự E, sau đó sử dụng trường số 2 theo số lượng. --debug đến trong coreutils 8.6, và có thể rất hữu ích trong việc xem chính xác những gì sắp xếp đang làm.