Sử dụng sed để đổi tên hàng loạt tệp


87

Mục tiêu

Thay đổi các tên tệp này:

  • F00001-0708-RG-biasliuyda
  • F00001-0708-CS-akgdlaul
  • F00001-0708-VF-hioulgigl

cho các tên tệp này:

  • F0001-0708-RG-biasliuyda
  • F0001-0708-CS-akgdlaul
  • F0001-0708-VF-hioulgigl

Mã vỏ

Để kiểm tra:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/'

Để thực hiện:

ls F00001-0708-*|sed 's/\(.\).\(.*\)/mv & \1\2/' | sh

Câu hỏi của tôi

Tôi không hiểu mã sed. Tôi hiểu lệnh thay thế là gì

$ sed 's/something/mv'

có nghĩa. Và tôi hiểu phần nào biểu thức chính quy. Nhưng tôi không hiểu chuyện gì đang xảy ra ở đây:

\(.\).\(.*\)

hoặc ở đây:

& \1\2/

Trước đây, đối với tôi, có vẻ như nó có nghĩa là: "một ký tự duy nhất, theo sau là một ký tự duy nhất, theo sau là bất kỳ chuỗi độ dài nào của một ký tự duy nhất" - nhưng chắc chắn còn nhiều điều hơn thế nữa. Theo như phần sau:

& \1\2/

Tôi không có ý kiến.


Câu trả lời:


149

Đầu tiên, tôi nên nói rằng cách dễ nhất để làm điều này là sử dụng các lệnh đặt tên trước hoặc đổi tên.

Trên Ubuntu, OSX (gói Homebrew rename, gói MacPorts p5-file-rename) hoặc các hệ thống khác có đổi tên perl (prename):

rename s/0000/000/ F0000*

hoặc trên các hệ thống đổi tên từ using-linux-ng, chẳng hạn như RHEL:

rename 0000 000 F0000*

Điều đó dễ hiểu hơn nhiều so với lệnh sed tương đương.

Nhưng để hiểu lệnh sed, trang sed rất hữu ích. Nếu bạn chạy man sed và tìm kiếm & (sử dụng lệnh / để tìm kiếm), bạn sẽ thấy đó là một ký tự đặc biệt trong s / foo / bar / thay thế.

  s/regexp/replacement/
         Attempt  to match regexp against the pattern space.  If success‐
         ful,  replace  that  portion  matched  with  replacement.    The
         replacement may contain the special character & to refer to that
         portion of the pattern space  which  matched,  and  the  special
         escapes  \1  through  \9  to refer to the corresponding matching
         sub-expressions in the regexp.

Do đó, \(.\)khớp với ký tự đầu tiên, có thể được tham chiếu bởi \1. Sau đó, .khớp với ký tự tiếp theo, luôn là 0. Sau đó \(.*\)khớp với phần còn lại của tên tệp, có thể được tham chiếu bởi \2.

Chuỗi thay thế đặt tất cả lại với nhau bằng cách sử dụng &(tên tệp gốc) và \1\2là mọi phần của tên tệp ngoại trừ ký tự thứ 2, là số 0.

Đây là một cách khá khó hiểu để làm điều này, IMHO. Nếu vì lý do nào đó mà lệnh đổi tên không khả dụng và bạn muốn sử dụng sed để đổi tên (hoặc có lẽ bạn đang làm điều gì đó quá phức tạp cho việc đổi tên?), Thì regex của bạn rõ ràng hơn sẽ làm cho nó dễ đọc hơn nhiều. Có lẽ một cái gì đó như:

ls F00001-0708-*|sed 's/F0000\(.*\)/mv & F000\1/' | sh

Có thể thấy những gì thực sự thay đổi trong s / tìm kiếm / thay thế / làm cho nó dễ đọc hơn nhiều. Ngoài ra, nó sẽ không tiếp tục hút các ký tự ra khỏi tên tệp của bạn nếu bạn vô tình chạy nó hai lần hoặc một cái gì đó.


1
trên máy chủ RHEL của tôi, cú pháp đổi tên sẽ là "đổi tên 0000 000 F0000 *"
David LeBauer

1
Rất có thể đó renamelà một liên kết được "đổi tên" . tức là renameđã được "đổi tên" từ prename.. ví dụ: trong Ubuntu: readlink -f $(which rename)đầu ra /usr/bin/prename... Davidrename đề cập là một chương trình hoàn toàn khác.
Peter.O

1
Tốt, Peter. Tôi đã cập nhật câu trả lời để giải quyết cả hai tiện ích đổi tên.
Edward Anderson

3
Để gỡ lỗi này, hãy loại bỏ đường ống vào sh ở cuối. Các lệnh sẽ vang ra màn hình.
Ben Mathews

1
Bạn có chắc rằng đó là một lời khuyên tốt khi đưa ra dữ liệu ngẫu nhiên sh? điều này có thể nguy hiểm vì mã tùy ý có thể được thực thi (bạn đang coi dữ liệu là mã).
gniourf_gniourf 29/12/16

44

bạn đã có lời giải thích về sed của mình, bây giờ bạn có thể chỉ sử dụng shell, không cần lệnh bên ngoài

for file in F0000*
do
    echo mv "$file" "${file/#F0000/F000}"
    # ${file/#F0000/F000} means replace the pattern that starts at beginning of string
done

1
Tốt nhưng bạn không thể thực hiện tham chiếu với dấu ngoặc đơn.
Leonidas Tsampros

26

Tôi đã viết một bài đăng nhỏ với các ví dụ về đổi tên hàng loạt bằng cách sử dụng sedvài năm trước:

http://www.guyrutenberg.com/2009/01/12/batch-renaming-using-sed/

Ví dụ:

for i in *; do
  mv "$i" "`echo $i | sed "s/regex/replace_text/"`";
done

Nếu regex chứa các nhóm (ví dụ \(subregex\) thì bạn có thể sử dụng chúng trong văn bản thay thế dưới dạng \1\, \2v.v.


Lưu ý rằng các câu trả lời chỉ có liên kết không được khuyến khích (các liên kết có xu hướng cũ dần theo thời gian). Vui lòng xem xét chỉnh sửa câu trả lời của bạn và thêm tóm tắt ở đây.
kleopatra

không phải là hiệu quả, nhưng hoàn thành công việc cho vài trăm tệp. Đã ủng hộ.
Varun Chandak

21

Cách dễ nhất sẽ là:

for i in F00001*; do mv "$i" "${i/F00001/F0001}"; done

hoặc, chắc chắn,

for i in F00001*; do mv "$i" "F0001${i#F00001}"; done

Điều này thay thế F00001tiền tố trong tên tệp bằng F0001. tín dụng cho mahesh tại đây: http://www.debian-administration.org/articles/150


3
Bạn nên trích dẫn chính xác các nội suy biến; mv "$i" "${i/F00001/F0001}". Nhưng +1
tripleee

7

các sedlệnh

s/\(.\).\(.*\)/mv & \1\2/

có nghĩa là thay thế:

\(.\).\(.*\)

với:

mv & \1\2

giống như một sedlệnh thông thường . Tuy nhiên, các dấu ngoặc đơn &\ndấu thay đổi nó một chút.

Chuỗi tìm kiếm khớp (và ghi nhớ ở dạng mẫu 1) với ký tự đơn ở đầu, theo sau là một ký tự duy nhất, theo sau là phần còn lại của chuỗi (được nhớ là mẫu 2).

Trong chuỗi thay thế, bạn có thể tham khảo các mẫu phù hợp này để sử dụng chúng như một phần của chuỗi thay thế. Bạn cũng có thể tham khảo toàn bộ phần đã khớp là &.

Vì vậy, những gì sedlệnh đó đang làm là tạo một mvlệnh dựa trên tệp gốc (đối với nguồn) và ký tự 1 và 3 trở đi, loại bỏ ký tự 2 (đối với đích) một cách hiệu quả. Nó sẽ cung cấp cho bạn một loạt các dòng theo định dạng sau:

mv F00001-0708-RG-biasliuyda F0001-0708-RG-biasliuyda
mv abcdef acdef

và như thế.


1
Đây là một lời giải thích hay, nhưng có thể hữu ích khi chỉ ra cách bạn sử dụng lệnh sed với các lệnh khác để thực sự đổi tên tệp. Ví dụ:ls | sed "s/\(.\).\(.*\)/mv & \1\2/" | bash
jcarballo

@jcarballo: rất nguy hiểm khi phân tích cú pháp ls, chuyển qua đường ống sedsau đó chuyển qua vỏ! nó phải thực thi mã tùy ý với tên tệp giả mạo. Vấn đề là dữ liệu phải được coi là dữ liệu và ở đây nó thường được tuần tự hóa thành mã mà không có bất kỳ biện pháp phòng ngừa nào. Tôi ước paxdiablo có thể xóa câu trả lời này vì nó thực sự không cho thấy thực tiễn tốt. (Tôi đã vấp phải câu hỏi này bởi vì một người mới bắt đầu đặt câu hỏi ngẫu nhiên | shsau một lệnh không hoạt động và sau khi nhìn thấy câu hỏi này và các câu trả lời nghĩ rằng nó sẽ hoạt động tốt hơn — tôi thật kinh hoàng!) :).
gniourf_gniourf 29/12/16

3

Nội dung dấu gạch chéo ngược có nghĩa là, "trong khi khớp với mẫu, hãy giữ nội dung khớp ở đây." Sau đó, ở phía văn bản thay thế, bạn có thể lấy lại các đoạn đã nhớ đó bằng "\ 1" (khối đầu tiên được đặt trong ngoặc đơn), "\ 2" (khối thứ hai), v.v.


1

Nếu tất cả những gì bạn thực sự đang làm là xóa ký tự thứ hai, bất kể ký tự đó là gì, bạn có thể làm điều này:

s/.//2

nhưng lệnh của bạn đang xây dựng một mvlệnh và chuyển nó đến trình bao để thực thi.

Điều này không thể đọc được nhiều hơn phiên bản của bạn:

find -type f | sed -n 'h;s/.//4;x;s/^/mv /;G;s/\n/ /g;p' | sh

Ký tự thứ tư bị xóa vì findđược thêm trước mỗi tên tệp bằng "./".


Tôi ước bạn có thể xóa câu trả lời này. Mặc dù nó có thể tốt trong trường hợp rất cụ thể của OP, nhưng có rất nhiều người nhìn thấy các câu trả lời như thế này và không hiểu nó, và ngẫu nhiên ngắt | shsau một lệnh không hoạt động, với hy vọng rằng nó sẽ hoạt động tốt hơn. Thật kinh hoàng! (và bên cạnh đó, đó không phải là thực hành tốt). Tôi hy vọng bạn sẽ hiểu!
gniourf_gniourf 29/12/16

0

Các dấu ngoặc đơn nắm bắt các chuỗi cụ thể để sử dụng bởi các số gạch chéo ngược.


0
 ls F00001-0708-*|sed 's|^F0000\(.*\)|mv & F000\1|' | bash

Kinh khủng! tùy thuộc vào thực thi mã tùy ý (có thể không phải trong ngữ cảnh cụ thể của câu hỏi, nhưng có rất nhiều người nhìn thấy câu trả lời như thế này và cố gắng nhập ngẫu nhiên một cái gì đó giống như nó, và điều đó thật nguy hiểm!). Tôi ước bạn có thể xóa câu trả lời này (ngoài ra, bạn có một câu trả lời hay khác ở đây, mà tôi đã ủng hộ).
gniourf_gniourf 29/12/16

0

Đây là những gì tôi sẽ làm:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done

Sau đó, nếu điều đó có vẻ ổn, hãy thêm | shvào cuối. Vì thế:

for file in *.[Jj][Pp][Gg] ;do 
    echo mv -vi \"$file\" `jhead $file|
                           grep Date|
                           cut -b 16-|
                           sed -e 's/:/-/g' -e 's/ /_/g' -e 's/$/.jpg/g'` ;
done | sh

0

Sử dụng đổi tên perl ( phải có trong hộp công cụ):

rename -n 's/0000/000/' F0000*

Loại bỏ -ncông tắc khi đầu ra có vẻ tốt để đổi tên cho thực.

cảnh báo Có những công cụ khác có cùng tên có thể có hoặc không thể thực hiện việc này, vì vậy hãy cẩn thận.

Lệnh đổi tên là một phần của util-linuxgói, sẽ không.

Nếu bạn chạy lệnh sau ( GNU)

$ rename

và bạn thấy đấy perlexpr, đây có vẻ là công cụ phù hợp.

Nếu không, hãy đặt nó làm mặc định (thường đã là trường hợp) trên Debianvà dẫn xuất như Ubuntu:

$ sudo apt install rename
$ sudo update-alternatives --set rename /usr/bin/file-rename

Đối với Archlinux:

pacman -S perl-rename

Đối với các bản phân phối RedHat-family:

yum install prename

Gói 'tên trước' nằm trong kho EPEL .


Đối với Gentoo:

emerge dev-perl/rename

Đối với * BSD:

pkg install gprename

hoặc là p5-File-Rename


Đối với người dùng Mac:

brew install rename

Nếu bạn không có lệnh này với một bản phân phối khác, hãy tìm kiếm trình quản lý gói của bạn để cài đặt hoặc thực hiện theo cách thủ công :

cpan -i File::Rename

Phiên bản độc lập cũ có thể được tìm thấy tại đây


đổi tên người đàn ông


Công cụ này ban đầu được viết bởi Larry Wall, cha của Perl.


-1
for i in *; do mv $i $(echo $i|sed 's/AAA/BBB/'); done

4
Chào mừng đến với SO. Vui lòng xem xét thêm giải thích về mã của bạn. Nó sẽ giúp những người dùng khác hiểu được nó.
Digvijay S

Câu trả lời này là tốt nhưng nó là một câu trả lời gần như trùng lặp với một câu trả lời được ủng hộ cao ở trên.
Eric Leschinski
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.