Bash sao chép tất cả các tệp không khớp với các phần mở rộng đã cho


7

Khi tôi nhập ảnh từ máy ảnh của mình trong Shotwell, nó cũng nhập các video clip. Điều này hơi khó chịu, vì tôi muốn lưu trữ video của mình trong một thư mục khác. Tôi đã cố gắng viết một lệnh bash để làm điều này, nhưng không thành công.

Tôi cần một lệnh đáp ứng các yêu cầu sau:

  • Định vị tất cả các tệp trong cấu trúc thư mục không có phần mở rộng .jpg, .png, .gif hoặc .xcf (không phân biệt chữ hoa chữ thường).
  • Di chuyển tất cả các tệp này vào một thư mục đích, bất kể tên tệp hoặc đường dẫn thư mục có chứa khoảng trắng hoặc ký tự đặc biệt.

Bất kỳ trợ giúp sẽ được đánh giá cao!

EDIT: Tôi đang sử dụng shell mặc định trong Ubuntu, có nghĩa là một số lệnh được đặt bí danh, v.v.

EDIT 2: Tôi đã thử bản thân mình (không phải phần sao chép, chỉ là phần liệt kê các phần của tệp). Tôi đã bật extglob và chạy lệnh sau:

$ ls -R /path | awk '
  /:$/&&f{s=$0;f=0}
  /:$/&&!f{sub(/:$/,"");s=$0;f=1;next}
  NF&&f{ print s"/"$0 }'

Điều này liệt kê tất cả mọi thứ. Tôi đã thử sử dụng grep ở phần cuối của nó, nhưng không có ý tưởng rõ ràng nhất về cách làm cho nó không khớp với mẫu tôi đưa ra. Công tắc extglob không giúp được gì nhiều với grep, mặc dù nó giúp với các lệnh khác.


Bạn sẽ may mắn hơn khi tự mình thử điều này và đặt câu hỏi cụ thể về những gì sai.
DRS

Câu trả lời:


12

Bạn có thể sử dụng findđể tìm tất cả các tệp trong cây thư mục khớp (hoặc không khớp) một số thử nghiệm cụ thể và sau đó thực hiện một số thứ với chúng. Đối với vấn đề cụ thể này, bạn có thể sử dụng:

find -type f ! \( -iname '*.png' -o -iname '*.gif' -o -iname '*.jpg' -o -iname '*.xcf' \) -exec echo mv {} /new/path \;

Điều này giới hạn tìm kiếm đối với các tệp thông thường ( -type f) và sau đó đối với các tệp có tên không ( !) có phần mở rộng *.pngtrong bất kỳ vỏ ( -iname '*.png') hoặc ( -o) *.gif, v.v. Tất cả các phần mở rộng được nhóm thành một điều kiện duy nhất giữa \( ... \). Đối với mỗi tệp phù hợp, nó chạy một lệnh ( -exec) để di chuyển tệp, tên của tệp được chèn thay cho {}, vào thư mục /new/path. Các lệnh \;cho biết findrằng lệnh đã kết thúc.

Việc thay thế tên xảy ra bên trong mã thực thi chương trình, vì vậy khoảng trắng và các ký tự đặc biệt khác không quan trọng.


Nếu bạn muốn làm điều này ngay bên trong Bash, bạn có thể sử dụng các tính năng khớp mẫu mở rộng của Bash . Những yêu cầu shopt extglobnày là trên, và globstarquá. Trong trường hợp này, sử dụng:

mv **/!(*.[gG][iI][fF]|*.[pP][nN][gG]|*.[xX][cC][fF]|*.[jJ][pP][gG]) /new/path

Này phù hợp với tất cả các file trong thư mục con ( **) không phù hợp *.gif, *.png, vv, trong bất kỳ sự kết hợp của các kiểu chữ, và di chuyển chúng vào con đường mới. Việc mở rộng được thực hiện bởi shell, vì vậy khoảng trắng và ký tự đặc biệt không quan trọng nữa.

Giả định ở trên tất cả các tệp nằm trong thư mục con. Nếu không, bạn có thể lặp lại phần sau **/để bao gồm cả thư mục hiện tại.

Có các tính năng tương tự trong zshvà các trình bao khác, nhưng bạn đã cho biết bạn đang sử dụng Bash.


(Một lưu ý nữa: phân tích cú pháp lskhông bao giờ là một ý tưởng hay - chỉ cần đừng thử nó.)


1
Điều này chỉ dành cho tìm GNU, không phải tìm POSIX.
cuonglm

Tôi nghĩ rằng bạn sẽ tốt hơn với -exec mv {} +. Ngoài ra - bạn đã sai về ls. Nó làm điều ngược lại mà findlàm. find tìm ls danh sách. Nhân tiện, nó phân tích cú pháp. Và ngay cả khi những gì tôi nói là không đúng - bạn chắc chắn không thực hiện bất kỳ nỗ lực nào để sao lưu tuyên bố bị xóa của mình. Và xin vui lòng không liên kết đến điều wiki wiki - đó là một nguồn thông tin khủng khiếp - và không chỉ trên trang đó. Về phần tôi, đây là một ví dụ điển hình về cách dễ dàng lsphân tích cú pháp. Và nhân tiện - nó sẽ hoạt động tương tự ở đó nếu chúng là dòng mới không phải là không gian
mikeerv

Bạn đã viết rằng trên câu trả lời sai?
Michael Homer

Nó có thể sử dụng +, nhưng nó thực sự không quan trọng và nó không rõ ràng lắm. Tôi nghĩ rằng đó có thể là câu trả lời sai vì tôi đã không liên kết đến lenedge ở đây (mặc dù tôi có câu trả lời khác chắc chắn bạn đã đọc), vì vậy "trang đó" có vẻ như là một tài liệu tham khảo lơ lửng.
Michael Homer

3
Có một ngày khác về việc trích dẫn các mở rộng biến (nhân tiện là vì nó); Tôi đồng ý rằng nó thường là một chút nhiệt tình. Nhân tiện, tôi cũng đồng ý rằng câu trả lời được liên kết cho thấy cách dễ dàng lsđược phân tích cú pháp, nhưng tôi nghĩ chúng ta khác nhau về mức độ dễ dàng của nó - đó là rất nhiều công việc thông minh. Vì vậy, "không bao giờ làm điều đó" vẫn là lời khuyên chung tốt cho bất cứ ai cần lời khuyên
Michael Homer

2

Bạn sử dụng Ubuntu, vì vậy bạn sẽ có GNU find, hãy thử:

find . -maxdepth 1 ! -iregex ".*\.\(jpg\|png\|gif\|xcf\)$" -exec mv -- -t /path/to/newdir "{}" +
  • -iregexsử dụng regex để tìm tên tệp, nhưng trường hợp không nhạy cảm, !phủ nhận regex.

  • -exec command +chạy lệnh cho tập tin phù hợp. Nó giống như sử dụng find -print0với xargs -0. Sử dụng điều này, chúng ta có thể di chuyển nhiều tệp được tìm thấy bằng một mvlệnh, vì vậy tổng số lệnh gọi ít hơn số lượng tệp được khớp.

  • -maxdepth 1giới hạn findchỉ tìm kiếm trong thư mục hiện tại, nếu bạn muốn đệ quy, loại bỏ nó.


0

Không cần phải quá phức tạp này! Tại sao không chỉ:

mkdir ../temp
mv  *.{jpg,png,gif,xcf} ../temp/
mv * /desired/target/directory/
mv ../temp/* .
rmdir ../temp

Điều này sẽ không làm việc cho toàn bộ cây thư mục, tất nhiên chỉ là một thư mục phẳng của các tệp.

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.