Tập lệnh shell UNIX: làm thế nào để di chuyển đệ quy các tập tin lên một thư mục?


8

Tôi có một số lượng lớn các tệp nhỏ, f, được sắp xếp trong một cấu trúc thư mục như vậy:

/A/B/C/f

Có 11 thư mục ở cấp độ A, mỗi thư mục có khoảng 100 thư mục ở cấp độ B, mỗi thư mục có khoảng 30 thư mục ở cấp độ C, mỗi thư mục có một tệp f.

Làm cách nào để di chuyển tất cả các tệp lên một cấp? Ví dụ: đưa ra tập hợp tệp này ...

/ A / B / C / F1
/ A / B / C / f2
/ A / B / C / f3
/ A / B / C / f4

Tôi muốn thư /A/B/mục chứa 4 tệp, từ F1 đến f4. Xóa thư mục C là không cần thiết.

Tôi hy vọng đây là một vấn đề được giải quyết, có thể liên quan đến find, xargswhatnot. Có ý kiến ​​gì không?

Chúc mừng

James

Câu trả lời:


9

Nó khá đơn giản với GNU find (như được tìm thấy trên Linux) hoặc bất kỳ tìm thấy nào khác hỗ trợ -execdir:

find A -type f -execdir mv -i {} .. \;

Với một tiêu chuẩn find:

find A -type f -exec sh -c 'mv -i "$1" "${1%/*}/.."' sh {} \;

Với zsh:

zmv -Q -o-i 'A/(**/)*/(*)(.)' 'A/$1$2'

Nếu cấu trúc thư mục luôn có cùng mức lồng nhau, bạn không cần bất kỳ giao thức đệ quy nào (nhưng trước tiên hãy xóa các thư mục trống):

for x in */*; do; echo mv -i "$x"/*/* "$x"/..; done

1

Đối với tập hợp các tệp đó, điều này sẽ làm:

$ cd /A/B/C/
$ mv ./* ../

Nhưng tôi hy vọng rằng vấn đề của bạn có phần phức tạp hơn ... Tôi không thể trả lời vấn đề này ... Tôi không chắc chắn cấu trúc thư mục của bạn như thế nào ... Bạn có thể làm rõ không?


Điều này rõ ràng sẽ hoạt động cho bất kỳ một thư mục / A / B / C nào, nhưng vì tôi có khoảng 30000 thư mục như vậy, tôi hy vọng một lệnh có thể được thực thi ở đầu phân cấp để xử lý tất cả chúng cùng một lúc.
jl6

1
@ jl6 Tôi thấy, và chỉ các tập tin cần di chuyển? Vì vậy, C không phải di chuyển đến B, và B không phải A, v.v ...
Stack Overflow đã chết vào

Điều đó đúng - thao tác chỉ nên được thực hiện trên các tệp, ở mức thấp nhất của cấu trúc phân cấp và chúng chỉ nên được di chuyển lên một cấp.
jl6

0

Tôi đoán đầu tiên là

$ find A -type f -exec mv {} .. \;  

miễn là bạn không chỉ định -depthnó sẽ ổn thôi. Tôi KHÔNG thử nó vì vậy hãy kiểm tra nó trước khi bạn cam kết với nó.


Không tìm thấy làm gì khi có hai tập tin cùng tên?

Tìm không phải là vấn đề, lệnh "mv" được nhúng. Tên Dupe sẽ không tốt vì mv sẽ không đổi tên, nó sẽ ghi đè lên. Bạn có thể sử dụng mv -i để giúp đỡ một chút, nhưng với hơn 30.000 tệp có thể gây đau đớn. Nếu bạn cần kiểm soát nhiều tình huống, bạn có thể cần một chương trình được viết bằng ngôn ngữ kịch bản (lựa chọn của tôi sẽ là python, YMMV).
hotei

Tôi chưa sử dụng "bản sao lưu" với mv, nhưng nhìn vào trang người đàn ông có vẻ như đó có thể là một giải pháp cho vấn đề tên lừa của bạn. "tìm A -type f -exec mv - dự phòng được đánh số {} .. \;" có thể hoạt động (lưu ý đó là dấu gạch ngang kép trong - backup)
hotei

Ví dụ tìm được đưa ra sẽ di chuyển tất cả các tệp ở mức thấp nhất sang thư mục một ở trên A (Tôi tin rằng ".." đang được trình diễn bởi trình bao trước khi được chuyển sang find / mv?). Tôi cần các tập tin để di chuyển chỉ một cấp lên trong hệ thống phân cấp. Một điều rõ ràng nữa: sẽ không có tên tệp trùng lặp, miễn là các tệp chỉ di chuyển MỘT cấp độ lên.
jl6

@hotei: Các mvlệnh được thực thi trong thư mục hiện tại, vì vậy ..không làm những gì bạn hy vọng. Xem câu trả lời của tôi cho các giải pháp. @ jl6: shell không liên quan gì .., được xử lý bởi kernel.
Gilles 'SO- ngừng trở nên xấu xa'

0

Nếu bạn chỉ muốn di chuyển các tệp trong thư mục lá (tức là bạn không muốn di chuyển /A/B/fileđến /Anếu Bcó thư mục con) thì đây là một vài cách để làm điều đó:

Cả hai đều yêu cầu điều này

leaf ()
{
    find $1 -depth -type d | sed 'h; :b; $b; N; /^\(.*\)\/.*\n\1$/ { g; bb }; $ {x; b}; P; D'
}
shopt -s nullglob

Cái này hoạt động:

leaf A | while read -r dir
do
    for file in "$dir"/*
    do
        parent=${dir%/*}
        if [[ -e "$parent/${file##*/}" ]]
        then
            echo "not moved: $file"
        else
            mv "$file" "$parent"
        fi
    done
done

Điều này sẽ nhanh hơn, nhưng nó không giống như các thư mục nguồn trống:

leaf A | while read -r dir
do
    mv -n "${dir}"/* "${dir%/*}"
    remains=$(echo "$dir"/*)
    [[ -n "$remains" ]] && echo "not moved: $remains"
done
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.