Làm cách nào để thay đổi phần mở rộng của nhiều tệp đệ quy từ dòng lệnh?


Câu trả lời:


85

Một cách di động (sẽ hoạt động trên mọi hệ thống tuân thủ POSIX):

find /the/path -depth -name "*.abc" -exec sh -c 'mv "$1" "${1%.abc}.edefg"' _ {} \;

Trong bash4, bạn có thể sử dụng globalstar để nhận được các thông số đệ quy (**):

shopt -s globstar
for file in /the/path/**/*.abc; do
  mv "$file" "${file%.abc}.edefg"
done

Lệnh (perl) renametrong Ubuntu có thể đổi tên các tệp bằng cú pháp biểu thức chính quy perl, mà bạn có thể kết hợp với globalstar hoặc find:

# Using globstar
shopt -s globstar
files=(/the/path/**/*.abc)  

# Best to process the files in chunks to avoid exceeding the maximum argument 
# length. 100 at a time is probably good enough. 
# See http://mywiki.wooledge.org/BashFAQ/095
for ((i = 0; i < ${#files[@]}; i += 100)); do
  rename 's/\.abc$/.edefg/' "${files[@]:i:100}"
done

# Using find:
find /the/path -depth -name "*.abc" -exec rename 's/\.abc$/.edefg/' {} +

Đồng thời xem http://mywiki.wooledge.org/BashFAQ/030


tiện ích đầu tiên chỉ đơn giản gắn thêm tiện ích mở rộng mới vào tiện ích mở rộng cũ, nó không thay thế ...
user2757729

3
@ user2757729, nó không thay thế nó. "${1%.abc}"mở rộng đến giá trị $1ngoại trừ mà không có .abcở cuối. Xem faq 100 để biết thêm về các thao tác chuỗi vỏ.
geirha

Tôi đã chạy nó trên cấu trúc tệp ~ 70k tệp ... ít nhất là trên vỏ của tôi, nó chắc chắn không thay thế nó.
dùng2757729

1
@ user2757729 Có lẽ bạn đã để lại "abc" trong${1%.abc}...
kvnn

1
Trong trường hợp bất kỳ ai khác đang tự hỏi những gì gạch dưới đang làm trong lệnh đầu tiên, điều đó là cần thiết bởi vì với sh -c đối số đầu tiên được gán cho $ 0 và mọi đối số còn lại được gán cho các tham số vị trí . Vì vậy, _đối số giả là và '{}' là đối số vị trí đầu tiên sh -c.
hellobenallan

48

Điều này sẽ thực hiện nhiệm vụ được yêu cầu nếu tất cả các tệp trong cùng một thư mục

rename 's/.abc$/.edefg/' *.abc

Để đổi tên các tệp đệ quy sử dụng này:

find /path/to/root/folder -type f -name '*.abc' -print0 | xargs -0 rename 's/.abc$/.edefg/'

8
Hoặc rename 's/.abc$/.edefg/' /path/to/root/folder/**/*.abctrong một phiên bản hiện đại của Bash.
Adam Byrtek

Rất cảm ơn Adam đã cho tôi một mẹo về cách sử dụng * .abc trong các thư mục theo cách đệ quy!
Rafał Cieślak

Mẹo hay, cảm ơn! Tôi có thể tìm thêm tài liệu về cú pháp của regex ở đâu? Giống như những gì slúc ban đầu, và những lựa chọn khác tôi có thể sử dụng trong đó. Cảm ơn !
Anto

@AdamByrtek Tôi vừa thất bại với đề xuất của bạn trên Utopic. ('không có tệp nào như vậy' hoặc một số tệp như vậy.)
Jonathan Y.

14

Một vấn đề với việc đổi tên đệ quy là bất kỳ phương thức nào bạn sử dụng để định vị các tệp, nó đều chuyển toàn bộ đường dẫn tới rename, không chỉ tên tệp. Điều đó làm cho việc đổi tên phức tạp trong các thư mục lồng nhau trở nên khó khăn.

Tôi sử dụng hành động findcủa mình -execdirđể giải quyết vấn đề này. Nếu bạn sử dụng -execdirthay vì -exec, lệnh được chỉ định sẽ được chạy từ thư mục con chứa tệp phù hợp. Vì vậy, thay vì vượt qua toàn bộ con đường đến rename, nó chỉ đi qua ./filename. Điều đó làm cho nó dễ dàng hơn để viết regex.

find /the/path -type f \
               -name '*.abc' \
               -execdir rename 's/\.\/(.+)\.abc$/version1_$1.abc/' '{}' \;

Chi tiết:

  • -type f có nghĩa là chỉ tìm tập tin, không phải thư mục
  • -name '*.abc' có nghĩa là chỉ khớp tên tệp kết thúc bằng .abc
  • '{}'là trình giữ chỗ đánh dấu nơi -execdirsẽ chèn đường dẫn tìm thấy. Các dấu ngoặc đơn là bắt buộc, để cho phép nó xử lý tên tệp có dấu cách và ký tự shell.
  • Dấu gạch chéo ngược sau -type-namelà ký tự tiếp tục dòng bash. Tôi sử dụng chúng để làm cho ví dụ này dễ đọc hơn, nhưng chúng không cần thiết nếu bạn đặt tất cả lệnh của mình trên một dòng.
  • Tuy nhiên, dấu gạch chéo ngược ở cuối -execdirdòng là bắt buộc. Nó ở đó để thoát dấu chấm phẩy, chấm dứt lệnh chạy bởi -execdir. Vui vẻ!

Giải thích về regex:

  • s/ bắt đầu của regex
  • \.\/phù hợp với hàng đầu ./ mà -execdir đi vào. Sử dụng \ để thoát. và / metachar character (lưu ý: phần này thay đổi tùy theo phiên bản của bạn find. Xem bình luận từ người dùng @apollo)
  • (.+)khớp với tên tệp. Các dấu ngoặc đơn nắm bắt trận đấu để sử dụng sau
  • \.abc thoát khỏi dấu chấm, khớp với abc
  • $ neo trận đấu ở cuối chuỗi

  • / đánh dấu sự kết thúc của phần "khớp" của biểu thức chính quy và phần bắt đầu của phần "thay thế"

  • version1_ thêm văn bản này vào mỗi tên tập tin

  • $1tham chiếu tên tệp hiện có, bởi vì chúng tôi đã bắt nó bằng dấu ngoặc đơn. Nếu bạn sử dụng nhiều bộ dấu ngoặc trong phần "khớp", bạn có thể tham khảo chúng ở đây bằng cách sử dụng $ 2, $ 3, v.v.
  • .abctên tệp mới sẽ kết thúc bằng .abc. Không cần phải thoát vi khuẩn chấm ở đây trong phần "thay thế"
  • / kết thúc của regex

Trước

tree --charset=ascii

|-- a_file.abc
|-- Another.abc
|-- Not_this.def
`-- dir1
    `-- nested_file.abc

Sau

tree --charset=ascii

|-- version1_a_file.abc
|-- version1_Another.abc
|-- Not_this.def
`-- dir1
    `-- version1_nested_file.abc

Gợi ý: rename's -n tùy chọn là hữu ích. Nó chạy khô và cho bạn biết tên nó sẽ thay đổi, nhưng không thực hiện bất kỳ thay đổi nào.


Cảm ơn đã giải thích chi tiết, nhưng thật kỳ lạ, khi tôi thử điều này, phần --execdirkhông vượt qua trong phần dẫn đầu ./để \.\/phần trong regex có thể được bỏ qua. May mà là vì tôi là trên OSX không ubuntu
apollo

5

Một cách di động khác:

find /the/path -depth -type f -name "*.abc" -exec sh -c 'mv -- "$1" "$(dirname "$1")/$(basename "$1" .abc).edefg"' _ '{}' \;

4
Chào mừng bạn đến hỏi Ubuntu! Mặc dù đây là một câu trả lời có giá trị, tôi khuyên bạn nên mở rộng nó (bằng cách chỉnh sửa) để giải thích cách thức và lý do lệnh đó hoạt động.
Eliah Kagan


0

Đây là những gì tôi đã làm và làm việc khá theo cách tôi muốn. Tôi đã sử dụng mvlệnh. Tôi có nhiều .3gptệp và tôi muốn đổi tên tất cả thành.mp4

Đây là một oneliner ngắn cho nó:

for i in *.3gp; do mv -- "$i" "ren-$i.mp4"; done

Mà chỉ cần quét qua thư mục hiện tại, chọn tất cả các tệp .3gp, sau đó đổi tên (sử dụng mv) thành ren-name_of_file.mp4


Điều đó sẽ đổi tên file.3gpthành file.3gp.mp4, đó có thể không phải là điều mà hầu hết mọi người muốn.
muru

@muru nhưng nguyên tắc vẫn tồn tại. Chỉ cần chọn bất kỳ tiện ích mở rộng nào sau đó chỉ định tiện ích mở rộng đích để đổi tên chúng. Các .3gphoặc .mp4ở đây chỉ là cho mục đích minh họa.
KhoPhi

Cú pháp là thích hợp hơn, một lót và dễ hiểu. Tuy nhiên, tôi sẽ sử dụng basename "$i" .mp4để xóa tiện ích mở rộng trước đó thay vì "ren- $ i.mp4".
TaoPR

Thật. Điểm tốt.
KhoPhi

0

Tôi tìm thấy một cách dễ dàng để đạt được điều này. Để thay đổi phần mở rộng của nhiều tệp từ jpg sang pdf, hãy sử dụng:

for file in /path/to; do mv $file $(basename -s jpg $file)pdf ; done

0

Tôi sẽ sử dụng lệnh mmv từ gói cùng tên :

mmv ';*.abc' '#1#2.edefg'

Các ;trận đấu bằng 0 hoặc nhiều hơn */và tương ứng với #1trong thay thế. Các *tương ứng với #2. Phiên bản không đệ quy sẽ là

mmv '*.abc' '#1.edefg'

0

Đổi tên tập tin và thư mục với find -execdir | rename

Nếu bạn định đổi tên cả tệp và thư mục không chỉ đơn giản bằng hậu tố, thì đây là một mẫu tốt:

PATH="$(echo "$PATH" | sed -E 's/(^|:)[^\/][^:]*//g')" \
  find . -depth -execdir rename 's/findme/replaceme/' '{}' \;

-execdirTùy chọn tuyệt vời thực hiện một cdvào thư mục trước khi thực hiện renamelệnh, không giống như -exec.

-depth đảm bảo rằng việc đổi tên xảy ra trước tiên đối với trẻ em và sau đó là cha mẹ để ngăn ngừa các vấn đề tiềm ẩn với các thư mục cha mẹ bị thiếu.

-execdir là bắt buộc vì đổi tên không chơi tốt với các đường dẫn đầu vào không phải là tên cơ sở, ví dụ như các lỗi sau:

rename 's/findme/replaceme/g' acc/acc

Việc PATHhack là bắt buộc vì -execdircó một nhược điểm rất khó chịu: findcực kỳ quan tâm và từ chối làm bất cứ điều gì -execdirnếu bạn có bất kỳ đường dẫn tương đối nào trong PATHbiến môi trường của mình , ví dụ: ./node_modules/.binthất bại với:

find: Đường dẫn tương đối './node_modules/.bin' được bao gồm trong biến môi trường PATH, không an toàn khi kết hợp với hành động -execdir của find. Vui lòng xóa mục nhập đó khỏi $ PATH

Xem thêm: Tại sao sử dụng hành động '-execdir' không an toàn cho thư mục nằm trong PATH?

-execdirlà một phần mở rộng tìm GNU cho POSIX . renamelà dựa trên Perl và đến từ renamegói. Đã thử nghiệm trong Ubuntu 18.10.

Đổi tên cách nhìn

Nếu đường dẫn đầu vào của bạn không xuất phát findhoặc nếu bạn đã có đủ sự khó chịu của đường dẫn tương đối, chúng ta có thể sử dụng một số giao diện Perl để đổi tên thư mục một cách an toàn như trong:

git ls-files | sort -r | xargs rename 's/findme(?!.*\/)\/?$/replaceme/g' '{}'

Tôi đã không tìm thấy một chất tương tự thuận tiện cho việc -execdirvới xargs: https://superuser.com/questions/893890/xargs-change-working-directory-to-file-path-before-executing/915686

Các sort -ryêu cầu để đảm bảo rằng các tập tin đến sau khi các thư mục tương ứng của họ, kể từ khi con đường còn đi sau những ngắn hơn với tiền tố giống nhau.

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.