Thực hiện kiểm tra -nt / -ot trong một POSIX sh


11

Các tiện ích tích hợp test[tiện ích có các bài kiểm tra -nt("mới hơn") và -ot("cũ hơn") trong hầu hết các trình bao, ngay cả khi trình bao đang chạy trong "chế độ POSIX" (cũng đúng với các tiện ích bên ngoài có cùng tên trên hệ thống mà tôi có quyền truy cập). Các thử nghiệm này là để so sánh dấu thời gian sửa đổi trên hai tệp. Ngữ nghĩa tài liệu của họ hơi khác nhau giữa các lần triển khai (liên quan đến những gì xảy ra nếu một hoặc tệp khác không tồn tại), nhưng chúng không được bao gồm trong thông số POSIX. cho các testtiện ích .

Chúng không được chuyển tiếp vào testtiện ích khi lệnh điều kiện được gỡ bỏ khỏi trình bao [KornShell] vì chúng không được bao gồm trong testtiện ích được tích hợp trong các triển khai lịch sử của shtiện ích.

Giả sử tôi muốn so sánh dấu thời gian sửa đổi giữa các tệp trong /bin/shtập lệnh shell và sau đó thực hiện hành động tùy thuộc vào việc một tệp có mới hơn tệp kia hay không, như trong

if [ "$sigfile"     -nt "$timestamp" ] ||
   [ "$sigfile.tmp" -nt "$timestamp" ]
then
    return
fi

... Tôi có thể sử dụng tiện ích nào khác, ngoài make(điều này sẽ khiến phần còn lại của tập lệnh khó sử dụng nhất)? Hay tôi chỉ nên cho rằng không ai sẽ chạy kịch bản trên một "triển khai lịch sử sh", hoặc từ chức để viết cho một trình bao cụ thể như thế bashnào?


Như bạn có thể thấy trong câu trả lời của tôi, bash không triển khai -nttính năng testtheo cách được mong đợi kể từ khoảng. 1995. Bạn nên sử dụng findbiểu thức cơ sở ngay cả bashkhi bạn thích hành vi đúng.
schily

Câu trả lời:


10

VỊ TRÍ:

f1=/path/to/file_1
f2=/path/to/file_2

if [ -n "$(find -L "$f1" -prune -newer "$f2")" ]; then
    printf '%s is newer than %s\n' "$f1" "$f2"
fi

Sử dụng đường dẫn tuyệt đối đến các tệp ngăn chặn một dương tính giả với tên tệp chỉ chứa các dòng mới.

Trong trường hợp sử dụng đường dẫn tương đối, sau đó thay đổi findlệnh thành:

find -L "$f1" -prune -newer "$f2" -exec echo . \;

về mặt kỹ thuật, tôi nghĩ nó thất bại nếu $f1chỉ chứa các dòng mới;)
ilkkachu

1
@ilkkachu có thể được làm việc xung quanh với -exec echo x \;hoặc tương tự?
muru

@muru, vâng. -printfsẽ dễ dàng nếu nó là tiêu chuẩn
ilkkachu

3
@Kusalananda AFAICT, findtrạng thái thoát của độc lập với những gì các bài kiểm tra findtrả lại riêng lẻ - ngoại trừ có lẽ có lỗi khi thực sự chạy các bài kiểm tra đó. findthoát 0 ngay cả khi không tìm thấy tập tin.
muru

1
Lưu ý rằng hành vi [ / -nt /nofile ]thay đổi theo cách thực hiện (nhưng không bao giờ xuất hiện bất kỳ lỗi nào).
Stéphane Chazelas

7

Đây có thể là trường hợp sử dụng một trong những lệnh Unix lâu đời nhất , ls.

x=$(ls -tdL -- "$a" "$b")
[ "$x" = "$a
$b" ]

Kết quả là đúng nếu a mới hơn b.


3
Điều đó không hoạt động nếu tên tệp bắt đầu bằng -(thiếu --). Bạn cần -Lnó tương đương với -nt. Điều đó không làm việc để so sánh x$'x\nx'ví dụ.
Stéphane Chazelas

1
Ôi! Nhưng cũng hả.
Kusalananda

4

Bạn đưa ra một câu hỏi thú vị và đưa ra yêu cầu cần được xác minh trước tiên.

Tôi đã kiểm tra hành vi của:

$shell -c '[ Makefile -nt SCCS/s.Makefile ] && echo newer'

với nhiều loại vỏ khác nhau. Đây là kết quả:

  • bash Không hoạt động - in không có gì.

  • bosh làm việc

  • dash không hoạt động - in không có gì.

  • ksh88 Không hoạt động - không in gì cả.

  • ksh93 hoạt động

  • mksh Không hoạt động - không in gì cả.

  • posh print: posh: [: -nt: toán tử / toán hạng không mong muốn

  • yash hoạt động

  • zsh hoạt động trong các phiên bản mới hơn , phiên bản cũ hơn không in gì

Vì vậy, bốn trong số chín shell hỗ trợ tính năng -nt và thực hiện chính xác. Chính xác trong trường hợp này có nghĩa là: có thể so sánh tem thời gian trên các nền tảng gần đây hỗ trợ mức độ chi tiết của tem thời gian thứ hai phụ . Lưu ý rằng các tệp tôi chọn khác nhau thường chỉ có một vài micro giây trong dấu thời gian của chúng.

Vì việc tìm kiếm findtriển khai làm việc dễ dàng hơn , tôi khuyên bạn nên thay thế

if [ "$file1" -nt "$file2" ] ; then
    echo newer
fi

bởi một findbiểu thức dựa trên.

if [ "$( find "$file1" -newer "$file2" )" ]; then
    echo newer
fi

hoạt động ít nhất miễn là $file1không chỉ chứa các dòng mới.

if [ "$( find -L "$file1" -newer "$file2" -exec echo newer \; )" ]; then
    echo newer
fi

chậm hơn một chút nhưng hoạt động chính xác.

BTW: Về việc làm cho tôi không thể nói cho tất cả các lần thực hiện, nhưng SunPro Makehỗ trợ so sánh thời gian với độ chi tiết nano giây kể từ khoảng. 20 năm, trong khi smakegmakethêm tính năng này gần đây.


1
Các thử nghiệm "không hoạt động" không hoạt động do không so sánh được dấu thời gian của giây phụ (chắc chắn?), Hay cái gì khác? Dấu thời gian thực tế trên các tệp liên quan đến bài kiểm tra của bạn là gì? +1 để thử nghiệm!
Kusalananda

2
Tôi nghĩ rằng downvote có lẽ là về từ ngữ đối đầu. Ở đây, sẽ công bằng hơn khi gọi nó là một giới hạn (có ý nghĩa trong một số bối cảnh). Một số findtriển khai như busybox hoặc gia truyền-toolchest sẽ có cùng giới hạn.
Stéphane Chazelas

3
Bạn sẽ cần phải -Lcho các findphiên bản là tương đương với -nt. Nó cũng sẽ thất bại với tên tệp bắt đầu bằng -hoặc !, (...
Stéphane Chazelas

2
Như một vấn đề của thực tế, tôi thường nhận được downvote khi tôi sử dụng một từ ngữ đối đầu. Ở đây sẽ hữu ích nếu bạn nêu bối cảnh (các tệp được sửa đổi trong cùng dấu thời gian khi bị cắt bớt thành độ phân giải thứ hai) khi bắt đầu câu trả lời.
Stéphane Chazelas

1
@schily, vâng, BSD findfind -f "$file"cho điều đó nhưng nó không phải là di động.
Stéphane Chazelas
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.