Giữ nguyên cấu trúc thư mục khi di chuyển tệp bằng find


22

Tôi đã tạo tập lệnh sau để di chuyển các tệp ngày cũ như được xác định từ thư mục nguồn sang thư mục đích. Nó đang hoạt động hoàn hảo.

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

Kịch bản lệnh này di chuyển các tệp tuyệt vời, Nó cũng di chuyển các tệp của thư mục con nguồn, nhưng nó không tạo thư mục con vào thư mục đích. Tôi muốn thực hiện tính năng bổ sung này trong đó.

với ví dụ

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

Khi tôi chạy tập lệnh này, nó cũng di chuyển các tệp hex trong thư mục maxi, nhưng tôi cần tạo hex đó vào thư mục maxi và di chuyển các tệp của nó trong cùng một hex ở đó.

Câu trả lời:


13

Thay vì chạy mv /home/ketan/hex/foo /home/maxi, bạn sẽ cần thay đổi thư mục đích dựa trên đường dẫn được tạo bởi find. Điều này dễ dàng hơn nếu bạn thay đổi thư mục nguồn trước và chạy find .. Bây giờ bạn chỉ có thể thêm vào thư mục đích cho mỗi mục được sản xuất bởi find. Bạn sẽ cần chạy shell trong find … -execlệnh để thực hiện nối và tạo thư mục đích nếu cần.

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

Lưu ý rằng để tránh trích dẫn các vấn đề nếu $destinationcó chứa các ký tự đặc biệt, bạn không thể chỉ thay thế nó trong tập lệnh shell. Bạn có thể xuất nó ra môi trường để nó đến được lớp vỏ bên trong hoặc bạn có thể chuyển nó dưới dạng đối số (đó là những gì tôi đã làm). Bạn có thể tiết kiệm một chút thời gian thực hiện bằng cách nhóm shcác cuộc gọi:

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

Ngoài ra, trong zsh, bạn có thể sử dụng các zmvchức năng , và .m vòng loại glob để chỉ phù hợp với các file thường xuyên trong phạm vi ngày phải. Bạn sẽ cần phải vượt qua một mvchức năng thay thế đầu tiên tạo thư mục đích nếu cần thiết.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'

for x do, bạn đã mất tích ;ở đó :). Ngoài ra, tôi không biết bạn muốn đạt được điều gì $0nhưng tôi hoàn toàn tin rằng nó sẽ như vậy sh:).
Michał Górny

@ MichałGórny for x; dovề mặt kỹ thuật không tuân thủ POSIX (kiểm tra ngữ pháp ), nhưng các trình bao hiện đại cho phép cả hai for x dofor x; do; một số vỏ Bourne cũ không mò mẫm for x; do. Trên vỏ hiện đại, với sh -c '…' arg0 arg1 arg2 arg3, arg0trở nên $0, arg1trở thành $1, vv Nếu bạn muốn $0được sh, bạn cần phải ghi sh -c '…' sh arg1 arg2 arg3. Một lần nữa, một số vỏ Bourne hoạt động khác nhau, nhưng POSIX chỉ định điều này.
Gilles 'SO- ngừng trở nên xấu xa'

pushdcó vẻ như là một lựa chọn tốt hơn cd, vì nó ít xâm nhập vào môi trường hiện tại.
jpmc26

16

Tôi biết findđã được chỉ định, nhưng điều này nghe có vẻ như một công việc cho rsync.

Tôi thường sử dụng như sau:

rsync -axuv --delete-after --progress Source/ Target/

Dưới đây là một ví dụ tốt nếu bạn muốn chỉ di chuyển các tệp thuộc loại tệp cụ thể ( ví dụ ):

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/

sạch hơn nhiều so với các giải pháp khác :)
Guillaume

2
Tôi thấy rằng nó --remove-source-filesrất hữu ích, điều này có hiệu quả làm cho các tập tin được di chuyển thay vì sao chép. Đây là một cách sử dụng tuyệt vời của rsync.
Tom

3

bạn có thể làm điều đó bằng hai trường hợp tìm (1)

Luôn có cpio (1)

(cd "$soure" && find  | cpio -pdVmu "$destination")

Kiểm tra các đối số cho cpio. Những cái tôi đã cho


1
Điều này sẽ phá vỡ bất kỳ tên tập tin với khoảng trắng.
Chris Xuống

1
Điều này sao chép các tập tin thay vì di chuyển chúng.
Gilles 'SO- ngừng trở nên xấu xa'

Có thể không phải là một câu trả lời hoàn hảo, nhưng nó đã giúp tôi di chuyển các tệp lưu giữ các đường dẫn theo cách thẳng hơn hoặc ít hơn (tôi có đủ không gian để sao chép các tệp sau đó xóa). Được nâng cấp
AhHHR

3

Nó không hiệu quả, nhưng theo tôi, mã này dễ đọc và dễ hiểu hơn, nếu bạn chỉ sao chép các tệp và sau đó xóa.

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

Lưu ý: Lỗ hổng được phát hiện bởi @MV cho các hoạt động tự động:

Sử dụng hai hoạt động riêng biệt là rủi ro. Nếu một số tệp trở nên cũ hơn 7 ngày trong khi thao tác sao chép được thực hiện, chúng sẽ không được sao chép nhưng chúng sẽ bị xóa bởi thao tác xóa. Đối với một cái gì đó được thực hiện thủ công một khi điều này có thể không phải là một vấn đề, nhưng đối với các tập lệnh tự động, điều này có thể dẫn đến mất dữ liệu


2
Sử dụng hai hoạt động riêng biệt là rủi ro. Nếu một số tệp trở nên cũ hơn 7 ngày trong khi thao tác sao chép được thực hiện, chúng sẽ không được sao chép nhưng chúng sẽ bị xóa bởi thao tác xóa. Đối với một cái gì đó được thực hiện thủ công một khi điều này có thể không phải là một vấn đề, nhưng đối với các tập lệnh tự động, điều này có thể dẫn đến mất dữ liệu.
MV.

2
Giải pháp dễ dàng cho lỗ hổng này là chạy find một lần, lưu danh sách dưới dạng tệp văn bản, sau đó sử dụng xargs hai lần để thực hiện sao chép và sau đó xóa.
David M. Perlman

1

Bạn có thể làm điều này bằng cách nối thêm đường dẫn tuyệt đối của tệp được trả về findvào đường dẫn đích của bạn:

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'

Bây giờ Chris chuyển toàn bộ ngôi nhà thành maxi
Ketan Patel

@ K.KPatel Không, không. Nó chỉ giữ cấu trúc thư mục của bạn.
Chris Xuống

Điều này phá vỡ nếu $destinationchứa các ký tự đặc biệt, bởi vì nó trải qua sự mở rộng trong lớp vỏ bên trong. Có lẽ bạn có ý nghĩa destination='\'"$destination"\''? Điều đó vẫn còn phá vỡ '. Ngoài ra, điều này tạo ra các tập tin như /home/maxi/home/ketan/hex/foothay vì /home/maxi/hex/foo.
Gilles 'SO- ngừng trở nên xấu xa'

0

Tốt hơn (nhanh nhất & không tốn dung lượng lưu trữ bằng cách sao chép thay vì di chuyển), cũng không bị ảnh hưởng bởi tên tệp nếu chúng chứa các ký tự đặc biệt trong tên của chúng:

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
  mkdir -p "$destination"/`dirname "$file"`
  \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
done'

Hoặc nhanh hơn, di chuyển một loạt các tệp cùng lúc cho nhiều CPU, sử dụng lệnh "song song":

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "`parallel --number-of-cores`" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"`dirname "$file"`" ... "
    mkdir -p "$destination"/`dirname "$file"`
    \mv -f "$file" "$destination"/`dirname "$file"`/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

PS: Bạn có một lỗi đánh máy, "soure" phải là "nguồn". Tôi giữ tên biến.


0

Điều này kém thanh lịch nhưng dễ dàng nếu số lượng / kích thước tệp không quá lớn

Nén các tệp của bạn lại với nhau vào một zipkho lưu trữ, và sau đó giải nén tại đích mà không có -jtùy chọn. Theo mặc định, zip sẽ tạo cấu trúc thư mục tương đối.


0

Hãy thử cách này:

IFS=$'\n'
for f in `find "$soure" -type f -mtime "-$days"`;
do
  mkdir -p "$destination"/`dirname $f`;
  mv $f "$destination"/`dirname $f`;
done

0

Vì dường như không có giải pháp thực sự dễ dàng nào cho việc này và tôi rất cần nó, tôi đã tạo tiện ích nguồn mở này cho linux (yêu cầu python): https://github.com/benapetr/smv

Có nhiều cách bạn có thể sử dụng nó để đạt được những gì bạn cần nhưng có lẽ đơn giản nhất sẽ là một cái gì đó như thế này:

 # -vf = verbose + force (doesn't stop on errors)
smv -vf `find some_folder -type f -mtime "-$days"` target_folder

Bạn cũng có thể chạy nó ở chế độ khô để nó không làm gì ngoài việc in những gì nó sẽ làm

smv -rvf `find some_folder -type f -mtime "-$days"` target_folder

Hoặc trong trường hợp danh sách các tệp quá dài để phù hợp với dòng đối số và bạn không ngại thực thi python cho mỗi tệp duy nhất, sau đó

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;
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.