Tên tệp tăng tự động


7

Tôi có nhu cầu thu thập một số tệp trùng lặp và muốn tránh xung đột tên. Vấn đề là bộ sưu tập các tập tin này có thể được thêm vào bằng cách thực thi tập lệnh khác của tôi trước khi các tập tin được xóa sạch và chỉ muốn tiếp tục tăng số lượng.

Tôi quyết định một vòng lặp đơn giản cho đến khi tăng số lượng như vậy.

until [[ ! -f ${PWD}/DUPES/${num}-$1 ]]; do
num=$((num +1))
done
mv --no-clobber $1 ${PWD}/DUPES/${num}-$1

Tôi không thể tưởng tượng được bao giờ vượt quá 1k tệp trong một ví dụ cực đoan, vì vậy câu hỏi của tôi là ...

Đây có phải là một cách không hiệu quả khủng khiếp để đạt được điều này? Tôi có nên phân tích cú pháp các tệp hiện có để có được số dẫn đầu cao nhất để bắt đầu tăng từ đó không, hay điều này có ổn với ví dụ cực đoan mà tôi đưa ra không? Hoặc có một cách tốt hơn tất cả cùng nhau?


Hãy xem xét hai trường hợp này chạy đồng thời, đã bắt đầu đồng thời. Cả hai sẽ thấy rằng (giả sử) num = 4 có sẵn và cả hai sẽ cố gắng sử dụng cùng một tên mục tiêu.
roaima

Nếu bạn không cần số liên tiếp, chỉ cần không va chạm, bạn có thể sử dụng mktemp, có thể với tên tệp làm cơ sở. (Sau đó chúng tôi sẽ cho rằng mktempthực hiện công việc của mình một cách hiệu quả.)
Ulrich Schwarz

Có lẽ bạn muốn có một hệ thống kiểm soát phiên bản như git
Basile Starynkevitch

Câu trả lời:


5

Bạn có thể làm như thế:

set -C; num=0                                  ### set -o noclobber; init $num
[ -e "$1" ] &&                                 ### this needs to be true
until 2>&3 >"./DUPES/$((num+=1))-$1" &&        ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do :; done 3>/dev/null                         ### do nothing

Bạn sẽ đảm bảo rằng nhiều trường hợp không thể bảo mật cùng tên cho bất kỳ tệp đã cho nào và tăng biến của bạn.

Các /dev/null<stderr chỉ giảm khiếu nại của vỏ về một file đã tồn tại khi nó cố gắng để làm sản lượng truncate / chuyển hướng và tìm thấy một mục tiêu hiện có. Trong khi noclobber được bật, nó sẽ không ghi đè lên một tệp khác - nó sẽ chỉ open()là một tệp mới trừ khi bạn sử dụng >|. Và vì vậy, bạn không cần khiếu nại vì toàn bộ vấn đề là tăng dần các tệp hiện có cho đến khi tìm thấy tên không tồn tại.

Về khía cạnh hiệu suất - sẽ tốt hơn nếu bạn không bắt đầu từ con số không. Hoặc, nếu bạn đã cố gắng để tạo ra sự khác biệt. Tôi đoán những điều trên có thể được cải thiện phần nào như:

set -C; num=0                                  ### set -o noclobber; init $num
until 2>&3 >"./DUPES/$((num+=1))-$1"  &&       ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do    [ -e  "./DUPES/$((num*2))-$1" ] &&       ### halve fail distance
      num=$((num*2))                           ### up to a point of course
done  3>/dev/null                              ### done

... nhưng có thể lên tới 1000 bạn không phải lo lắng về điều đó. Tôi đã nhận được tới 65 nghìn tên ngẫu nhiên trong vài giây.

Nhân tiện - bạn có thể nghĩ rằng bạn chỉ có thể:

>"./DUPES/$((num+=1))-$1" mv -- "$1" "./DUPES/$num-$1"

... nhưng nó không hoạt động trong bashvỏ.

num=0; echo >"/tmp/$((num+=1))" >&2 "$num"; echo "$num" /tmp/[01]

0
1 /tmp/1

Vì bất kỳ lý do gì bash, việc chuyển nhượng trong một số bối cảnh khác để chuyển hướng - và do đó, việc mở rộng xảy ra theo một thứ tự lạ. Vì vậy, bạn cần một lệnh đơn giản riêng biệt để mở rộng $numgiá trị chính xác khi tôi đến đây &&. Mặt khác, mặc dù:

num=0; echo "$((num+=1))" "$num"

1 1

Tôi không nghĩ rằng tôi phải lo lắng về nhiều trường hợp, nhưng tôi cũng không bao giờ nghĩ đến việc cho phép điều đó. Là người mới bắt đầu, tôi phải mất một vài điều để hiểu điều này và tôi đã học được rất nhiều chỉ khi cố gắng tìm ra nó. Vì không ai đề cập đến bất kỳ vấn đề hiệu suất nào khi chỉ tăng cho đến khi viết có thể được thực hiện so với phân tích ra nơi bắt đầu tăng, tôi sẽ cho rằng câu hỏi đó cũng được trả lời. Cảm ơn đã chỉnh sửa với một lời giải thích rõ ràng hơn là tốt.
akovia

@akovia - cảm ơn vì lời cảm ơn! và xin lỗi tôi đã không nói về bit hiệu suất ở nơi đầu tiên - đó là một câu hỏi hay.
mikeserv

5

GNU mv có một --backuptùy chọn có thể hữu ích. Các tương tác sau đây cho thấy cách các tệp được đổi tên khi mục tiêu tồn tại:

$ touch a b c o
$ mv --backup=numbered --verbose a o
`a' -> `o' (backup: `o.~1~')
$ mv --backup=numbered --verbose b o
`b' -> `o' (backup: `o.~2~')
$ mv --backup=numbered --verbose c o
`c' -> `o' (backup: `o.~3~')

Trong ví dụ này, file gốc ođã được đổi tên o.~1~, ađến o.~2~, bđể o.~3~, và cđể o. Vì vậy, điều này không đổi tên theo cùng cách mà mã bạn đã đăng, nhưng điều này có thể được chấp nhận, tùy thuộc vào nhu cầu chính xác của bạn.


Thanh lịch hơn nhiều so với giải pháp của tôi (tất nhiên là giả sử GNU mv)
roaima

1

Xem xét hai trường hợp đề xuất của bạn chạy đồng thời, đã bắt đầu đồng thời. Cả hai sẽ thấy rằng (giả sử) num = 4 có sẵn và cả hai sẽ cố gắng sử dụng cùng một tên mục tiêu.

Tôi không thể kiểm tra sự thay thế này ngay bây giờ, nhưng một cái gì đó như thế này có thể là đủ

num=1 attempt=10
while ! mv --no-clobber "$1" "${PWD}/DUPES/${num}-$1" && [[ 0 -lt $attempt ]]
do
    num=$((num+1))
    attempt=$((attempt-1))
done
[[ 0 -eq $attempt ]] && echo "ERROR: Too many attempts to handle $1" >&2
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.