Đổi tên số lượng lớn tệp hình ảnh bằng bash


16

Tôi cần đổi tên khoảng. 70.000 tập tin. Ví dụ: Từ sb_606_HBO_DPM_0089000đến sb_606_dpm_0089000v.v.

Phạm vi số đi từ 0089000đến 0163022. Đây chỉ là phần đầu tiên của tên cần thay đổi. tất cả các tệp nằm trong một thư mục và được đánh số liên tục (một chuỗi hình ảnh). Các con số phải không thay đổi.

Khi tôi thử điều này trong bash, nó sẽ nói với tôi rằng 'Danh sách đối số quá dài'.

Biên tập:

Lần đầu tiên tôi thử đổi tên một tệp với mv:

mv sb_606_HBO_DPM_0089000.dpx sb_606_dpm_0089000.dpx

Sau đó, tôi đã thử đổi tên một phạm vi (tuần trước tôi đã học cách di chuyển tải tệp, vì vậy tôi nghĩ rằng cú pháp tương tự có thể hoạt động để đổi tên các tệp ...). Tôi nghĩ rằng tôi đã thử những điều sau đây (hoặc một cái gì đó tương tự):

mv sb_606_HBO_DPM_0{089000..163023}.dpx sb_606_dpm_0{089000..163023}.dpx

4
Đối với người đánh giá : Tôi không nghĩ đây là một bản sao; hầu hết các câu trả lời CLI cho câu hỏi khác sẽ không hoạt động ở đây vì số lượng lớn tệp bị va chạm với ARG_MAXgiới hạn của trình bao . Vì câu hỏi này yêu cầu một giải pháp dòng lệnh rõ ràng, các giải pháp GUI (có thể bằng nhau) như trong câu hỏi khác cũng không khớp.
tráng miệng

1
Tôi không nghĩ đây là bản sao vì có nhiều câu hỏi về việc đổi tên tập tin. Vui lòng không đóng các câu hỏi cụ thể đối với các tài nguyên chung không thực sự trả lời chúng ...
Zanna

1
@rich Nếu bạn có thể chỉnh sửa một cách rõ ràng lệnh nào bạn đã thử, thì rõ ràng đây không phải là bản sao. (Điều này cho chúng tôi thấy rằng bạn biết về phương pháp này.) Chúc mừng.
Sparhawk

2
giàu có, câu hỏi của bạn không phải là một bản sao, bởi vì đó là một câu hỏi cụ thể. Đừng lo lắng về điều đó. Quan trọng hơn, sau khi một câu hỏi đã nhận được một số câu trả lời nâng cao, chỉnh sửa nó có lẽ không phải là ý kiến ​​hay vì các chỉnh sửa của bạn có thể làm cho các câu trả lời hiện tại ít hợp lệ hơn. Bây giờ tôi cảm thấy câu trả lời của mình sẽ giải thích tại sao mv {1..2} {3..4}nó không hoạt động, đó là một vấn đề hoàn toàn khác với ARG_MAX... Những người khác trả lời có lẽ sẽ cảm thấy như vậy! Vì vậy, theo quan điểm của tôi, tôi ước bạn sẽ quay lại bản chỉnh sửa cuối cùng của mình và, nếu bạn muốn, hãy hỏi một câu hỏi hoàn toàn mới về mving với các phạm vi
Zanna

1
@Sparhawk OP đã viết khá rõ ràng, từ phiên bản đầu tiên của câu hỏi, vấn đề là argument list too longlỗi. Không cần phải làm rõ thêm, đây rõ ràng không phải là một bản sao vì chúng tôi cần một cách giải quyết để xử lý ARG_MAX và các câu trả lời trong bản sao được đề xuất không làm điều đó.
terdon

Câu trả lời:


25

Một cách là sử dụng findvới -exec, và +tùy chọn. Điều này xây dựng một danh sách đối số, nhưng chia danh sách thành nhiều cuộc gọi cần thiết để hoạt động trên tất cả các tệp mà không vượt quá danh sách đối số tối đa. Nó phù hợp khi tất cả các đối số sẽ được đối xử như nhau. Đây là trường hợp với rename, mặc dù không với mv.

Bạn có thể cần phải cài đặt đổi tên Perl:

sudo apt install rename

Sau đó, bạn có thể sử dụng, ví dụ:

find . -maxdepth 1 -exec rename -n 's/_HBO_DPM_/_dpm_/' {} +

Loại bỏ -nsau khi thử nghiệm, để thực sự đổi tên các tập tin.


11

Tôi sẽ đề xuất ba phương án. Mỗi lệnh là một dòng đơn giản, nhưng tôi sẽ cung cấp các biến thể cho các trường hợp phức tạp hơn, chủ yếu trong trường hợp các tệp cần xử lý được trộn với các tệp khác trong cùng một lệnh.

mmv

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

mmv '*HBO_DPM*' '#1dpm#2'

Lưu ý rằng các đối số được truyền dưới dạng chuỗi, vì vậy việc mở rộng toàn cục không xảy ra trong trình bao. Lệnh nhận chính xác hai đối số và sau đó tìm các tệp tương ứng bên trong, không giới hạn chặt chẽ về số lượng tệp. Cũng lưu ý rằng lệnh trên giả định rằng tất cả các tệp khớp với quả cầu đầu tiên sẽ được đổi tên. Tất nhiên bạn được tự do cụ thể hơn:

mmv 'sb_606_HBO_DPM_*' 'sb_606_dpm_#1'

Nếu bạn có các tệp nằm ngoài phạm vi số được yêu cầu trong cùng thư mục, bạn có thể tốt hơn với vòng lặp trên các số được đưa ra trong câu trả lời này. Tuy nhiên, bạn cũng có thể sử dụng một chuỗi các lệnh gọi mmv với các mẫu phù hợp:

mmv 'sb_606_HBO_DPM_0089*'       'sb_606_dpm_0089#1'    # 0089000-0089999
mmv 'sb_606_HBO_DPM_009*'        'sb_606_dpm_009#1'     # 0090000-0099999
mmv 'sb_606_HBO_DPM_01[0-5]*'    'sb_606_dpm_01#1#2'    # 0100000-0159999
mmv 'sb_606_HBO_DPM_016[0-2]*'   'sb_606_dpm_016#1#2'   # 0160000-0162999
mmv 'sb_606_HBO_DPM_01630[01]?'  'sb_606_dpm_01630#1#2' # 0163000-0163019
mmv 'sb_606_HBO_DPM_016302[0-2]' 'sb_606_dpm_016302#1'  # 0163020-0163022

lặp qua số

Nếu bạn muốn tránh cài đặt bất cứ thứ gì, hoặc cần chọn theo phạm vi số tránh các kết quả khớp ngoài phạm vi này và bạn đã sẵn sàng chờ 74.023 lệnh, bạn có thể sử dụng vòng lặp bash đơn giản:

for i in {0089000..0163022}; do mv sb_606_HBO_DPM_$i sb_606_dpm_$i; done

Điều này đặc biệt tốt ở đây vì không có khoảng trống trong trình tự. Nếu không, bạn có thể muốn kiểm tra xem tập tin nguồn có thực sự tồn tại không.

for i in {0089000..0163022}; do
  test -e sb_606_HBO_DPM_$i && mv sb_606_HBO_DPM_$i sb_606_dpm_$i
done

Lưu ý rằng trái ngược với for ((i=89000; i<=163022; ++i))việc mở rộng cú đúp xử lý các số 0 hàng đầu kể từ khi một số Bash phát hành vài năm trước. Trên thực tế, một thay đổi tôi đã yêu cầu, vì vậy tôi rất vui khi thấy các trường hợp sử dụng cho nó.

Đọc thêm: Mở rộng cú đúp trong các trang thông tin Bash, đặc biệt là phần về {x..y[..incr]}.

lặp qua các tập tin

Một lựa chọn khác sẽ là lặp qua một quả cầu phù hợp, thay vì chỉ lặp qua phạm vi số nguyên trong câu hỏi. Một cái gì đó như thế này:

for i in *HBO_DPM*; do mv "$i" "${i/HBO_DPM/dpm}"; done

Một lần nữa, đây là một mvlời mời cho mỗi tệp. Và một lần nữa, vòng lặp có một danh sách dài các phần tử, nhưng toàn bộ danh sách không được chuyển thành đối số cho một quy trình con, mà được xử lý bên trong bằng bash, vì vậy giới hạn sẽ không gây ra vấn đề cho bạn.

Đọc thêm: Mở rộng tham số Shell trong các trang thông tin Bash, tài liệu ${parameter/pattern/string}giữa những người khác.

Nếu bạn muốn giới hạn phạm vi số cho phạm vi bạn cung cấp, bạn có thể thêm một kiểm tra cho điều đó:

for i in sb_606_HBO_DPM_+([0-9]); do
  if [[ "${i##*_*(0)}" -ge 89000 ]] && [[ "${i##*_*(0)}" -le 163022 ]]; then
    mv "$i" "${i/HBO_DPM/dpm}"
  fi
done

Ở đây ${i##pattern}loại bỏ các khớp tiền tố dài nhất patterntừ $i. Tiền tố dài nhất đó được định nghĩa là bất cứ thứ gì, sau đó là dấu gạch dưới, sau đó là 0 hoặc nhiều số không. Cái sau được viết như *(0)là một mẫu hình cầu mở rộng phụ thuộc vào extglobtùy chọn được đặt. Xóa các số 0 đứng đầu rất quan trọng để coi số là cơ sở 10 chứ không phải cơ sở 8. Đối số +([0-9])trong vòng lặp là một quả cầu mở rộng khác, khớp với một hoặc nhiều chữ số, chỉ trong trường hợp bạn có các tệp bắt đầu giống nhau nhưng không kết thúc bằng một con số.


Cảm ơn bạn! Điều này hoạt động như một giấc mơ: đối với tôi trong {0089000..0163022}; làm mv sb_606_HBO_DPM_ $ i sb_606_dpm_ $ i; xong - tôi đã phải thêm phần mở rộng tên tệp để làm cho nó hoạt động, nhưng nó đã làm đúng như tôi muốn và tôi thậm chí còn hiểu cú pháp. Cảm ơn bạn @MvG
giàu

@rich: Rất vui vì tôi có thể giúp - bạn và hy vọng khách truy cập trong tương lai cũng vậy. Đừng quên chấp nhận câu trả lời hữu ích nhất. Bạn luôn có thể thay đổi dấu kiểm đó trong tương lai nếu có thứ gì đó tốt hơn xuất hiện.
MvG

10

Một cách để vượt qua ARG_MAXgiới hạn là sử dụng nội dung của bash shell printf:

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'

Ví dụ.

rename -n 's/HBO_DPM/dpm/' sb_*
bash: /usr/bin/rename: Argument list too long

nhưng

printf '%s\0' sb_* | xargs -0 rename -n 's/HBO_DPM/dpm/'
rename(sb_606_HBO_DPM_0089000, sb_606_dpm_0089000)
.
.
.
rename(sb_606_HBO_DPM_0163022, sb_606_dpm_0163022)

7
find . -type f -exec bash -c 'echo $1 ${1/HBO_DPM/dpm}' _ {} \;
./sb_606_HBO_DPM_0089000 ./sb_606_dpm_0089000

findtrong thư mục hiện hành .cho tất cả các tập tin -type fvà thực hiện đổi tên các tập tin tìm thấy $1với thay thế HBO_DPMvới dmp từng người một-exec ... \;

thay thế echobằng mvđể thực hiện đổi tên.


6

Bạn có thể viết một đoạn script python nhỏ, đại loại như:

import os
for file in os.listdir("."):
    os.rename(file, file.replace("HBO_DPM", "dpm"))

Lưu nó dưới dạng tệp văn bản như rename.pytrong thư mục các tệp nằm trong, sau đó với thiết bị đầu cuối trong thư mục đó đi:

python rename.py

6

Bạn có thể thực hiện nó theo tệp (có thể mất một chút thời gian) với

sudo apt install util-linux  # if you don't have it already
for i in *; do rename.ul HBO_DPM dpm "$i"; done

Giống như Perl renameđược sử dụng trong các câu trả lời khác, rename.ulcũng có một tùy chọn -nhoặc --no-actđể thử nghiệm.


Tôi đã chỉnh sửa nhận xét của bạn về câu trả lời của Zanna, vui lòng chỉnh sửa câu trả lời của Zanna hoặc để lại nhận xét.
fosslinux

@ubashu đó không phải là một nhận xét về câu trả lời của tôi - nó đề cập đến -nlá cờ tôi đã sử dụng để thử nghiệm và đề nghị nó cũng có thể được sử dụng rename.ul.
Zanna

3

Tôi thấy rằng không ai đã mời người bạn thân nhất của tôi sedđến bữa tiệc :). forVòng lặp sau sẽ hoàn thành mục tiêu của bạn:

for i in sb_606_HBO_DPM*; do
  mv "$i" "$(echo $i | sed 's/HBO_DPM/dpm/')";
done

Có nhiều công cụ cho một công việc như vậy, hãy chọn một công cụ dễ hiểu nhất cho bạn. Điều này là đơn giản và dễ dàng thay đổi để phù hợp với mục đích này hoặc mục đích khác ...


Cấp, không liên quan lắm trong trường hợp cụ thể này, nhưng điều này sẽ thất bại nếu bất kỳ tên tệp nào chứa dòng mới. Tôi đề cập đến điều này vì hầu hết (tất cả?) Các câu trả lời khác đều mạnh mẽ và có thể xử lý các tên tệp tùy ý hoặc chỉ hoạt động trên sơ đồ đặt tên tệp của OP.
terdon

... dòng mới, dấu cách, ký tự đại diện, ... một số trong số đó có thể tránh được bằng cách trích dẫn $itrong thay thế lệnh, nhưng không có cách nào dễ dàng để xử lý một dòng mới trong tên tệp.
muru

3

Vì chúng tôi đang đưa ra các tùy chọn, đây là cách tiếp cận Perl. cdvào thư mục đích và chạy:

perl -e 'foreach(glob("sb_*")){rename $_, s/_HBO_DPM_/_dpm_/r}'

Giải trình

  • perl -e: chạy tập lệnh được đưa ra bởi -e.
  • foreach(glob){}: chạy bất cứ thứ gì có trong { }mỗi kết quả của toàn cầu.
  • glob("sb_*"): trả về danh sách tất cả các tệp và thư mục trong thư mục hiện tại có tên khớp với shell shell sb*.
  • rename $_, s/_HBO_DPM_/_dpm_/r: ma thuật perl. $_là một biến đặc biệt chứa từng phần tử mà chúng ta đang lặp lại (trong phần foreach). Vì vậy, ở đây, nó sẽ được từng tập tin được tìm thấy.s/_HBO_DPM_/_dpm_/thay thế sự xuất hiện đầu tiên của _HBO_DPM_với _dpm_. Nó chạy $_theo mặc định, vì vậy nó sẽ chạy trên mỗi tên tệp. Nghĩa /rlà "áp dụng thay thế này cho một bản sao của chuỗi đích (tên tệp) và trả về chuỗi đã sửa đổi. Thực renamehiện những gì bạn mong đợi: nó đổi tên các tệp. Vì vậy, toàn bộ điều sẽ đổi tên của tên tệp hiện tại ( $_) thành chính nó với _HBO_DPM_thay thế bằng _dpm_.

Bạn có thể viết điều tương tự như một tập lệnh mở rộng (và dễ đọc hơn):

#! /usr/bin/env perl
use strict;
use warnings;

foreach my $fileName (glob("sb_*")){
  ## Copy the name to a new variable
  my $newName = $fileName;
  ## change the copy. $newName is now the changed version
  $newName =~ s/_HBO_DPM_/_dpm_/;
  ## rename
  rename $fileName, $newName;
}

1

Tùy thuộc vào loại đổi tên bạn đang hình dung, sử dụng vidir với chỉnh sửa nhiều dòng có thể thỏa đáng.
Trong trường hợp cụ thể của bạn, bạn có thể chọn tất cả các dòng trong trình soạn thảo văn bản của mình và xóa phần _ " HBO" của tên tệp trong một vài lần nhấn phím.


yeah, vi đã tìm thấy và thay thế
Jasen

2
Bạn có thể vui lòng mở rộng câu trả lời của mình và đưa ra một ví dụ về cách đạt được mục tiêu của OP vidirkhông?
tráng miệng
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.