Bash di chuyển và đổi tên bằng cách phân tích đầu ra của find


4

Tôi có các thư mục lồng nhau của các tệp pdf và tôi muốn trích xuất chúng sang thư mục cấp cao hơn đổi tên chúng như sau:

Các tập tin của tôi là một cái gì đó như:

./path1/pathA/fileI.pdf
./path1/pathB/fileII.pdf

Tôi muốn đạt được:

./path1_pathA_fileI.pdf    
./path1_pathB_fileII.pdf

Tôi biết tôi có thể tạo một danh sách các tập tin bằng cách làm

find . -type f -name "*.pdf"

Và tôi có thể tưởng tượng một giải pháp bằng cách sử dụng

find . -type f -name "*.pdf" | mv -t ...

Nhưng tôi không biết làm thế nào để điền vào ... bởi vì tôi không hiểu phân tích cú pháp và gán biến trong bash. Làm thế nào để phân chia đường dẫn tại '/' và tạo thành một đường dẫn và tên tệp mới như trên?

Rất cám ơn trước!

Câu trả lời:


6

Thử:

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'f=${1#./}; mv "$1" "./${f//\//_}"' None {} \;

Điều này an toàn cho tất cả các tên tệp, ngay cả những tên có dòng mới trong tên của chúng.

Làm thế nào nó hoạt động

  • -mindepth 2

    Điều này cho biết tìm thấy không xử lý bất kỳ tập tin đã có trong thư mục hiện tại.

  • -type f -name '*.pdf'

    Điều này hạn chế tìm kiếm các tệp thông thường với pdf sự mở rộng.

  • -exec bash -c '...' None {} \;

    Điều này chạy lệnh trong chuỗi trích dẫn cung cấp tên tệp làm đối số đầu tiên, $1.

    Đối với mục đích của chúng tôi, chuỗi None chỉ đơn giản là một giữ chỗ. Nó được gán cho $0, theo các quy ước bash, là tên của lệnh mà chúng ta đang chạy.

  • f=${1#./}; mv "$1" "./${f//\//_}"

    Điều này (a) loại bỏ tiền tố ./ từ tên tệp và (b) di chuyển tệp đến vị trí mong muốn với tên mới.

    ${1#./} là một ví dụ về bash's loại bỏ tiền tố . Nó trả về chuỗi $1 với ./ loại bỏ từ đầu. ${f//\//_} là một ví dụ về bash's thay thế mẫu . Nó trả về chuỗi $f với tất cả / thay thế bằng _. Để đọc thêm về các tính năng này, xem phần trong man bash được phép Mở rộng tham số .

Phiên bản hiệu quả hơn

Phiên bản trên gọi bash cho mọi tệp được tìm thấy. Ngoài ra, chúng ta có thể gọi bash chỉ một lần cho một số tệp được tìm thấy. Để làm điều này, chúng tôi gói lệnh của chúng tôi trong một for vòng lặp:

find . -mindepth 2 -type f -name '*.pdf' -exec bash -c 'for f in "$@"; do f=${f#./}; echo mv "$f" "./${f//\//_}"; done' None {} +

Vấn đề thay thế

Giả sử rằng tất cả các tệp chúng ta muốn nằm trong một thư mục cấp hai và chúng ta muốn các tệp được di chuyển có thứ tự các tên thư mục được đảo ngược để thay vì ./path1_pathA_fileI.pdf, chúng tôi kết thúc với ./pathA_path1_fileI.pdf. Trong trường hợp này:

for d1 in */; do d1=${d1%/}; for d2 in "$d1"/*/; do d2=${d2%/}; p="${d2#$d1/}_$d1"; for f in "./$d2"/*.pdf; do echo mv "$f" "./${p}_${f#./$d2/}"; done; done; done

Hoặc, đối với những người thích các lệnh của họ trải rộng trên nhiều dòng:

for d1 in */
do
    d1=${d1%/}
    for d2 in "$d1"/*/
    do
         d2=${d2%/}
         p="${d2#$d1/}_$d1"
         for f in "./$d2"/*.pdf
         do
             echo mv "$f" "./${p}_${f#./$d2/}"
         done
    done
done

Cảm ơn cho sự cố từng bước! Điều gì xảy ra nếu tôi cũng muốn thay đổi thứ tự của hai chuỗi dir để đường dẫn cuối cùng là ./pathA_path1_fileI.pdf Trong trường hợp này, việc thay thế mẫu $ {f // \ // _} không hoạt động. Làm thế nào điều này có thể được thực hiện?
Canaryyellow

@Canaryyellow OK. Tôi đã thêm mã cho điều đó.
John1024
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.