Hàm Bash để tìm mẫu phù hợp với tệp mới nhất


141

Trong Bash, tôi muốn tạo một hàm trả về tên tệp của tệp mới nhất khớp với một mẫu nhất định. Ví dụ: tôi có một thư mục chứa các tệp như:

Directory/
   a1.1_5_1
   a1.2_1_4
   b2.1_0
   b2.2_3_4
   b2.3_2_0

Tôi muốn tập tin mới nhất bắt đầu bằng 'b2'. Làm thế nào để tôi làm điều này trong bash? Tôi cần phải có điều này trong ~/.bash_profilekịch bản của tôi .


4
xem superuser.com/questions/294161/ trên để biết thêm gợi ý trả lời. Sắp xếp là bước quan trọng để có được tệp mới nhất của bạn
Wolfgang Fahl

Câu trả lời:


229

Các lslệnh có một tham số -tđể sắp xếp theo thời gian. Sau đó, bạn có thể lấy đầu tiên (mới nhất) với head -1.

ls -t b2* | head -1

Nhưng hãy cẩn thận: Tại sao bạn không nên phân tích đầu ra của ls

Ý kiến ​​cá nhân của tôi: phân tích cú pháp lschỉ nguy hiểm khi tên tệp có thể chứa các ký tự vui nhộn như dấu cách hoặc dòng mới. Nếu bạn có thể đảm bảo rằng tên tệp sẽ không chứa các ký tự vui thì phân tích cú pháp lskhá an toàn.

Nếu bạn đang phát triển một kịch bản có nghĩa là được điều hành bởi nhiều người trên nhiều hệ thống trong nhiều tình huống khác nhau thì tôi khuyên bạn không nên phân tích cú pháp ls.

Đây là cách thực hiện "đúng": Làm cách nào tôi có thể tìm thấy tệp mới nhất (mới nhất, sớm nhất, cũ nhất) trong một thư mục?

unset -v latest
for file in "$dir"/*; do
  [[ $file -nt $latest ]] && latest=$file
done

8
Lưu ý cho người khác: nếu bạn đang làm điều này cho một thư mục, bạn sẽ thêm tùy chọn -d vào ls, như thế này 'ls -td <mẫu> | đầu -1 '
ken.ganong

5
Các phân tích LS liên kết nói không để làm điều này và đề xuất các phương pháp trong BashFAQ 99 . Tôi đang tìm kiếm một lớp lót thay vì một thứ chống đạn để đưa vào kịch bản, vì vậy tôi sẽ tiếp tục phân tích ls một cách không an toàn như @lesmana.
Eponymous

1
@Eponymous: Nếu bạn đang tìm kiếm một lớp lót mà không sử dụng loại dễ vỡ ls, printf "%s\n" b2* | head -1sẽ làm điều đó cho bạn.
David Ongaro

2
@DavidOngaro Câu hỏi không nói rằng tên tệp là số phiên bản. Đây là về thời gian sửa đổi. Ngay cả với giả định tên tệp b2.10_5_2giết chết giải pháp này.
Eponymous

1
Một lớp lót của bạn đang cho tôi câu trả lời đúng, nhưng cách "đúng" thực sự mang lại cho tôi tệp cũ nhất . Bất cứ ý tưởng tại sao?
NewNameStat

15

Sự kết hợp findlshoạt động tốt cho

  • tên tập tin không có dòng mới
  • số lượng tệp không quá lớn
  • tên tập tin không dài

Giải pháp:

find . -name "my-pattern" -print0 |
    xargs -r -0 ls -1 -t |
    head -1

Hãy phá vỡ nó:

Với findchúng tôi có thể phù hợp với tất cả các tệp thú vị như thế này:

find . -name "my-pattern" ...

sau đó sử dụng -print0chúng ta có thể chuyển tất cả tên tệp một cách an toàn lsnhư thế này:

find . -name "my-pattern" -print0 | xargs -r -0 ls -1 -t

findtham số tìm kiếm bổ sung và các mẫu có thể được thêm vào đây

find . -name "my-pattern" ... -print0 | xargs -r -0 ls -1 -t

ls -tsẽ sắp xếp các tệp theo thời gian sửa đổi (mới nhất trước tiên) và in nó tại một dòng. Bạn có thể sử dụng -cđể sắp xếp theo thời gian tạo. Lưu ý : điều này sẽ phá vỡ với tên tệp có chứa dòng mới.

Cuối cùng, head -1cho chúng tôi tập tin đầu tiên trong danh sách được sắp xếp.

Lưu ý: xargs sử dụng giới hạn hệ thống theo kích thước của danh sách đối số. Nếu kích thước này vượt quá, xargssẽ gọi lsnhiều lần. Điều này sẽ phá vỡ sự sắp xếp và có lẽ cũng là đầu ra cuối cùng. Chạy

xargs  --show-limits

để kiểm tra các giới hạn trên hệ thống của bạn.

Lưu ý 2: sử dụng find . -maxdepth 1 -name "my-pattern" -print0nếu bạn không muốn tìm kiếm tệp thông qua các thư mục con.

Lưu ý 3: Như được chỉ ra bởi @starfry - -rđối số cho xargsviệc ngăn chặn cuộc gọi của ls -1 -t, nếu không có tệp nào được khớp bởi find. Cảm ơn bạn đã suggesion.


2
Điều này tốt hơn các giải pháp dựa trên ls, vì nó hoạt động cho các thư mục có rất nhiều tệp, trong đó ls cuộn cảm.
Marcin Zukowski

find . -name "my-pattern" ... -print0cho tôifind: paths must precede expression: `...'
Jaakko

Oh! ...là viết tắt của "nhiều tham số". Chỉ cần bỏ qua nó, nếu bạn không cần nó.
Boris Brodski

2
Tôi thấy rằng điều này có thể trả về một tệp không khớp với mẫu nếu không có tệp nào khớp với mẫu đó. Nó xảy ra bởi vì find không chuyển gì cho xargs mà sau đó gọi ls không có danh sách tệp, khiến nó hoạt động trên tất cả các tệp. Giải pháp là thêm -rvào dòng lệnh xargs thông báo cho xargs không chạy dòng lệnh của nó nếu nó không nhận được gì trên đầu vào tiêu chuẩn của nó.
starfry

@starfry cảm ơn bạn! Bắt đẹp. Tôi thêm vào -rcâu trả lời.
Boris Brodski

7

Đây là một triển khai có thể của hàm Bash được yêu cầu:

# Print the newest file, if any, matching the given pattern
# Example usage:
#   newest_matching_file 'b2*'
# WARNING: Files whose names begin with a dot will not be checked
function newest_matching_file
{
    # Use ${1-} instead of $1 in case 'nounset' is set
    local -r glob_pattern=${1-}

    if (( $# != 1 )) ; then
        echo 'usage: newest_matching_file GLOB_PATTERN' >&2
        return 1
    fi

    # To avoid printing garbage if no files match the pattern, set
    # 'nullglob' if necessary
    local -i need_to_unset_nullglob=0
    if [[ ":$BASHOPTS:" != *:nullglob:* ]] ; then
        shopt -s nullglob
        need_to_unset_nullglob=1
    fi

    newest_file=
    for file in $glob_pattern ; do
        [[ -z $newest_file || $file -nt $newest_file ]] \
            && newest_file=$file
    done

    # To avoid unexpected behaviour elsewhere, unset nullglob if it was
    # set by this function
    (( need_to_unset_nullglob )) && shopt -u nullglob

    # Use printf instead of echo in case the file name begins with '-'
    [[ -n $newest_file ]] && printf '%s\n' "$newest_file"

    return 0
}

Nó chỉ sử dụng các nội dung Bash và nên xử lý các tệp có tên chứa dòng mới hoặc các ký tự bất thường khác.


1
Bạn có thể sử dụng nullglob_shopt=$(shopt -p nullglob)và sau đó $nullglobđể đặt lại nullglobnhư trước đây.
gniourf_gniourf

Đề xuất của @gniourf_gniourf để sử dụng $ (shopt -p nullglob) là một gợi ý hay. Tôi thường cố gắng tránh sử dụng thay thế lệnh ( $()hoặc backticks) vì nó chậm, đặc biệt là theo Cygwin, ngay cả khi lệnh chỉ sử dụng nội trang. Ngoài ra, bối cảnh subshell trong đó các lệnh được chạy đôi khi có thể khiến chúng hành xử theo những cách không mong muốn. Tôi cũng cố gắng tránh lưu trữ các lệnh trong các biến (như nullglob_shopt) bởi vì những điều rất xấu có thể xảy ra nếu bạn nhận được giá trị của biến sai.
pjh

Tôi đánh giá cao sự chú ý đến các chi tiết có thể dẫn đến thất bại tối nghĩa khi bị bỏ qua. Cảm ơn!
Ron Burk

Tôi thích rằng bạn đã đi cho một cách độc đáo hơn để giải quyết vấn đề! Có một điều chắc chắn rằng trong Unix / Linux có nhiều hơn một cách để 'lột da cat!'. Ngay cả khi điều này đòi hỏi nhiều công việc hơn, nó vẫn có lợi ích cho mọi người thấy khái niệm. Có +1!
Pryftan

3

Tên tệp không bình thường (chẳng hạn như tệp chứa \nký tự hợp lệ có thể tàn phá loại phân tích cú pháp này. Đây là một cách để làm điều đó trong Perl:

perl -le '@sorted = map {$_->[0]} 
                    sort {$a->[1] <=> $b->[1]} 
                    map {[$_, -M $_]} 
                    @ARGV;
          print $sorted[0]
' b2*

Đó là một biến đổi Schwartzian được sử dụng ở đó.


1
Có thể các schwartz được với bạn!
Nathan Monteleone

câu trả lời này có thể hoạt động nhưng tôi sẽ không tin nó được cung cấp tài liệu kém.
Wolfgang Fahl

1

Bạn có thể sử dụng statvới một tập tin toàn cầu và sắp xếp trang trí-không trang trí với thời gian tập tin được thêm ở mặt trước:

$ stat -f "%m%t%N" b2* | sort -rn | head -1 | cut -f2-

không "stat: không thể đọc thông tin hệ thống tệp cho '% m% t% N': Không có tệp hoặc thư mục như vậy"
Ken Ingram

Tôi nghĩ rằng điều này có thể dành cho phiên bản Mac / FreeBSD stat, nếu tôi nhớ chính xác các tùy chọn của nó. Để có được đầu ra tương tự trên các nền tảng khác, bạn có thể sử dụngstat -c $'%Y\t%n' b2* | sort -rn | head -n1 | cut -f2-
Jeffrey Cash

1

Phép thuật chức năng bóng tối cho những người muốn find ... xargs ... head ...giải pháp ở trên, nhưng ở dạng chức năng dễ sử dụng để bạn không phải suy nghĩ:

#define the function
find_newest_file_matching_pattern_under_directory(){
    echo $(find $1 -name $2 -print0 | xargs -0 ls -1 -t | head -1)
}

#setup:
#mkdir /tmp/files_to_move
#cd /tmp/files_to_move
#touch file1.txt
#touch file2.txt

#invoke the function:
newest_file=$( find_newest_file_matching_pattern_under_directory /tmp/files_to_move/ bc* )
echo $newest_file

Bản in:

file2.txt

Đó là:

Tên tệp có dấu thời gian sửa đổi cũ nhất của tệp trong thư mục đã cho khớp với mẫu đã cho.


1

Sử dụng lệnh find.

Giả sử bạn đang sử dụng Bash 4.2+, hãy sử dụng -printf '%T+ %p\n'cho giá trị dấu thời gian của tệp.

find $DIR -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

Thí dụ:

find ~/Downloads -type f -printf '%T+ %p\n' | sort -r | head -n 1 | cut -d' ' -f2

Để biết tập lệnh hữu ích hơn, hãy xem tập lệnh tìm mới nhất tại đây: https://github.com/l3x/helpers


để làm việc với tên tệp chứa khoảng trắng thay đổi cut -d '' -f2,3,4,5,6,7,8,9 ...
valodzka

0

Có một cách hiệu quả hơn nhiều để đạt được điều này. Hãy xem xét các lệnh sau:

find . -cmin 1 -name "b2*"

Lệnh này tìm tệp mới nhất được tạo chính xác một phút trước với tìm kiếm ký tự đại diện trên "b2 *". Nếu bạn muốn các tệp từ hai ngày trước thì bạn nên sử dụng lệnh dưới đây:

find . -mtime 2 -name "b2*"

Các "." đại diện cho thư mục hiện tại. Hi vọng điêu nay co ich.


9
Điều này thực sự không tìm thấy "mẫu khớp tệp mới nhất" ... nó chỉ tìm thấy tất cả các mẫu khớp với tệp được tạo một phút trước hoặc sửa đổi hai ngày trước.
GnP

Câu trả lời này dựa trên câu hỏi được đặt ra. Ngoài ra, bạn có thể điều chỉnh lệnh để xem tệp mới nhất xuất hiện trong một ngày trước đó. Nó phụ thuộc vào những gì bạn đang cố gắng làm.
Naufal

"tinh chỉnh" không phải là câu trả lời. nó giống như đăng bài này dưới dạng câu trả lời: "Chỉ cần điều chỉnh lệnh find và tìm câu trả lời tùy thuộc vào những gì bạn muốn làm".
Kennet Celeste

Không chắc chắn về những bình luận không cần thiết. Nếu bạn cảm thấy câu trả lời của tôi không chứng minh được, thì vui lòng cung cấp lý do chính đáng cho lý do tại sao câu trả lời của tôi không có ý nghĩa với BÀI TẬP. Nếu không thể làm như vậy, thì xin vui lòng không bình luận thêm.
Naufal

1
Giải pháp của bạn yêu cầu bạn biết khi nào tệp mới nhất được tạo. Điều đó không có trong câu hỏi nên không, câu trả lời của bạn không dựa trên câu hỏi được đặt ra.
Bloke Down The Pub
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.