Đổi tên tệp một cách đệ quy bằng cách sử dụng find và sed


85

Thay vào đó, tôi muốn xem qua một loạt thư mục và đổi tên tất cả các tệp kết thúc bằng _test.rb thành _spec.rb. Đó là điều mà tôi chưa bao giờ tìm ra cách làm với bash nên lần này tôi nghĩ rằng mình sẽ cố gắng để có được nó. Mặc dù vậy, cho đến nay tôi đã nhận ra một cách ngắn gọn, nỗ lực tốt nhất của tôi là:

find spec -name "*_test.rb" -exec echo mv {} `echo {} | sed s/test/spec/` \;

NB: có thêm một tiếng vọng sau khi thực thi để lệnh được in ra thay vì chạy trong khi tôi đang kiểm tra nó.

Khi tôi chạy nó, đầu ra cho mỗi tên tệp phù hợp là:

mv original original

tức là sự thay thế bằng sed đã bị mất. Thủ thuật là gì?


BTW, tôi biết rằng có một lệnh đổi tên nhưng tôi thực sự muốn tìm ra cách thực hiện nó bằng cách sử dụng sed để tôi có thể thực hiện các lệnh mạnh mẽ hơn trong tương lai.
opsb


Câu trả lời:


32

Điều này xảy ra vì sednhận chuỗi {}làm đầu vào, như có thể được xác minh bằng:

find . -exec echo `echo "{}" | sed 's/./foo/g'` \;

mà sẽ in foofoocho từng tệp trong thư mục, một cách đệ quy. Lý do cho hành vi này là đường ống được thực thi một lần, bởi shell, khi nó mở rộng toàn bộ lệnh.

Không có cách nào trích dẫn sedđường ống theo cách findsẽ thực thi nó cho mọi tệp, vì findkhông thực hiện các lệnh qua trình bao và không có khái niệm về đường ống hoặc dấu ngoặc kép. Hướng dẫn sử dụng công cụ GNU giải thích cách thực hiện một tác vụ tương tự bằng cách đặt đường ống trong một tập lệnh shell riêng:

#!/bin/sh
echo "$1" | sed 's/_test.rb$/_spec.rb/'

(Có thể có một số cách sử dụng sai lầm sh -cvà rất nhiều dấu ngoặc kép để thực hiện tất cả điều này trong một lệnh, nhưng tôi sẽ không thử.)


27
Đối với những người thắc mắc về cách sử dụng sai của sh -c, đây là: tìm spec -name "* _test.rb" -exec sh -c 'echo mv "$ 1" "$ (echo" $ 1 "| sed s / test.rb \ $ / spec.rb /) "'_ {} \;
opsb

1
@opsb _ cái quái gì vậy? giải pháp tuyệt vời - nhưng tôi thích câu trả lời ramtam hơn :)
iRaS

Chúc mừng! Đã cứu tôi rất nhiều đau đầu. Vì lợi ích của sự hoàn chỉnh, đây là cách tôi chuyển nó thành một script: find. -name "file" -exec sh /path/to/script.sh {} \;
Sven M.

128

Để giải quyết nó theo cách gần với vấn đề ban đầu nhất có lẽ sẽ sử dụng tùy chọn xargs "args per command line":

find . -name "*_test.rb" | sed -e "p;s/test/spec/" | xargs -n2 mv

Nó tìm các tệp trong thư mục làm việc hiện tại một cách đệ quy, lặp lại tên tệp gốc ( p) và sau đó là tên đã sửa đổi ( s/test/spec/) và cung cấp tất cả thành mvcặp ( xargs -n2). Hãy lưu ý rằng trong trường hợp này, bản thân đường dẫn không được chứa một chuỗi test.


9
Thật không may, điều này có vấn đề về khoảng trắng. Vì vậy, sử dụng với các thư mục đó có các khoảng trống trong tên sẽ phá vỡ nó tại xargs (xác nhận với -p cho tiết / chế độ tương tác)
CDE

1
Đó chính xác là những gì tôi đang tìm kiếm. Quá tệ cho vấn đề khoảng trắng (mặc dù tôi đã không kiểm tra nó). Nhưng đối với nhu cầu hiện tại của tôi, nó hoàn hảo. Tôi khuyên bạn nên kiểm tra nó trước bằng "echo" thay vì "mv" như tham số trong "xargs".
Michele Dall'Agata

5
Nếu bạn cần để đối phó với khoảng trắng trong đường dẫn và bạn đang sử dụng GNU sed> = 4.2.2 thì bạn có thể sử dụng các -ztùy chọn cùng với phát hiện -print0và xargs -0:find -name '*._test.rb' -print0 | sed -ze "p;s/test/spec/" | xargs -0 -n2 mv
Evan Purkhiser

Giải pháp tốt nhất. Nhanh hơn nhiều so với tìm -exec. Cảm ơn bạn
Miguel A. Baldi Hörlle

Điều này sẽ không hoạt động, nếu có nhiều testthư mục trong một đường dẫn. sedsẽ chỉ đổi tên cái đầu tiên và mvlệnh sẽ bị No such file or directorylỗi.
Casey

22

bạn có thể muốn xem xét theo cách khác như

for file in $(find . -name "*_test.rb")
do 
  echo mv $file `echo $file | sed s/_test.rb$/_spec.rb/`
done

Đó có vẻ là một cách tốt để làm điều đó. Tôi thực sự đang tìm cách bẻ khóa một lớp lót, để nâng cao kiến ​​thức của mình hơn bất cứ thứ gì khác.
opsb

2
cho tệp trong $ (find. -name "* _test.rb"); làm echo mv $ tập tin echo $file | sed s/_test.rb$/_spec.rb/; xong là một lớp lót, phải không?
Bretticus

5
Điều này sẽ không hoạt động nếu bạn có tên tệp có dấu cách. forsẽ tách chúng thành các từ riêng biệt. Bạn có thể làm cho nó hoạt động bằng cách hướng dẫn vòng lặp for chỉ tách trên các dòng mới. Xem cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html để biết các ví dụ.
onitake

Tôi đồng ý với @onitake, mặc dù tôi muốn sử dụng -exectùy chọn từ tìm.
ShellFish

18

Tôi thấy cái này ngắn hơn

find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;

Hi, tôi nghĩ rằng ' _test.rb" nên được ' _test.rb'(dấu ngoặc kép để trích dẫn đơn). Tôi có thể hỏi lý do tại sao bạn đang sử dụng dấu gạch dưới để đẩy đối số bạn muốn vị trí $ 1 khi có vẻ như với tôi rằng find . -name '*_test.rb' -exec bash -c 'echo mv $0 ${0/test.rb/spec.rb}' {} \;công trình ? Như sẽfind . -name '*_test.rb' -exec bash -c 'echo mv $1 ${1/test.rb/spec.rb}' iAmArgumentZero {} \;
agtb 16/10/11

Cảm ơn các đề xuất của bạn, đã khắc phục
csg 16/10/11

Cảm ơn cho thanh toán bù trừ mà lên - Tôi chỉ nhận xét vì tôi đã dành một thời gian cân nhắc ý nghĩa của _ nghĩ đó là có thể một số sử dụng trick của $ _ ( '_' là khá khó khăn để tìm kiếm trong tài liệu!)
agtb

9

Bạn có thể làm điều đó mà không cần sed, nếu bạn muốn:

for i in `find -name '*_test.rb'` ; do mv $i ${i%%_test.rb}_spec.rb ; done

${var%%suffix}dải suffixtừ giá trị của var.

hoặc, để làm điều đó bằng cách sử dụng sed:

for i in `find -name '*_test.rb'` ; do mv $i `echo $i | sed 's/test/spec/'` ; done

điều này không hoạt động ( sedmột) như được giải thích bởi câu trả lời được chấp nhận.
Ali

@Ali, Nó hoạt động - Tôi đã tự kiểm tra nó khi tôi viết câu trả lời. Lời giải thích của @ larsman không áp dụng cho for i in... ; do ... ; done, thực thi các lệnh thông qua trình bao và không hiểu được backtick.
Wayne Conrad

9

Bạn đề cập rằng bạn đang sử dụng bashlàm trình bao của mình, trong trường hợp đó bạn thực sự không cần findsedđể đạt được đổi tên hàng loạt mà bạn đang theo đuổi ...

Giả sử bạn đang sử dụng bashlàm trình bao của mình:

$ echo $SHELL
/bin/bash
$ _

... và giả sử bạn đã bật globstartùy chọn được gọi là shell:

$ shopt -p globstar
shopt -s globstar
$ _

... và cuối cùng giả sử bạn đã cài đặt renametiện ích (có trong util-linux-nggói)

$ which rename
/usr/bin/rename
$ _

... thì bạn có thể đổi tên hàng loạt trong một lớp lót bash như sau:

$ rename _test _spec **/*_test.rb

( globstartùy chọn shell sẽ đảm bảo rằng bash tìm thấy tất cả *_test.rbcác tệp phù hợp , bất kể chúng được lồng sâu như thế nào trong hệ thống phân cấp thư mục ... sử dụng help shoptđể tìm hiểu cách đặt tùy chọn)


7

Cách dễ nhất :

find . -name "*_test.rb" | xargs rename s/_test/_spec/

Cách nhanh nhất (giả sử bạn có 4 bộ xử lý):

find . -name "*_test.rb" | xargs -P 4 rename s/_test/_spec/

Nếu bạn có một số lượng lớn tệp cần xử lý, có thể danh sách tên tệp được chuyển đến xargs sẽ khiến dòng lệnh kết quả vượt quá độ dài tối đa cho phép.

Bạn có thể kiểm tra giới hạn hệ thống của mình bằng cách sử dụng getconf ARG_MAX

Trên hầu hết các hệ thống linux, bạn có thể sử dụng free -bhoặc cat /proc/meminfođể tìm dung lượng RAM bạn phải làm việc với; Nếu không, hãy sử dụng tophoặc ứng dụng giám sát hoạt động hệ thống của bạn.

Một cách an toàn hơn (giả sử bạn có 1000000 byte ram để làm việc):

find . -name "*_test.rb" | xargs -s 1000000 rename s/_test/_spec/

2

Đây là những gì đã làm việc cho tôi khi tên tệp có khoảng trắng trong chúng. Ví dụ bên dưới đổi tên một cách đệ quy tất cả các tệp .dar thành tệp .zip:

find . -name "*.dar" -exec bash -c 'mv "$0" "`echo \"$0\" | sed s/.dar/.zip/`"' {} \;

2

Đối với điều này bạn không cần sed. Bạn hoàn toàn có thể đơn độc với một whilevòng lặp được cung cấp với kết quả của findviệc thay thế quy trình .

Vì vậy, nếu bạn có một findbiểu thức chọn các tệp cần thiết, hãy sử dụng cú pháp:

while IFS= read -r file; do
     echo "mv $file ${file%_test.rb}_spec.rb"  # remove "echo" when OK!
done < <(find -name "*_test.rb")

Điều này sẽ findtập tin và đổi tên tất cả chúng _test.rbtách chuỗi khỏi phần cuối và phần nối tiếp _spec.rb.

Đối với bước này, chúng tôi sử dụng Shell Parameter Expansion , nơi ${var%string}loại bỏ "chuỗi" mẫu phù hợp ngắn nhất $var.

$ file="HELLOa_test.rbBYE_test.rb"
$ echo "${file%_test.rb}"          # remove _test.rb from the end
HELLOa_test.rbBYE
$ echo "${file%_test.rb}_spec.rb"  # remove _test.rb and append _spec.rb
HELLOa_test.rbBYE_spec.rb

Xem một ví dụ:

$ tree
.
├── ab_testArb
├── a_test.rb
├── a_test.rb_test.rb
├── b_test.rb
├── c_test.hello
├── c_test.rb
└── mydir
    └── d_test.rb

$ while IFS= read -r file; do echo "mv $file ${file/_test.rb/_spec.rb}"; done < <(find -name "*_test.rb")
mv ./b_test.rb ./b_spec.rb
mv ./mydir/d_test.rb ./mydir/d_spec.rb
mv ./a_test.rb ./a_spec.rb
mv ./c_test.rb ./c_spec.rb

Cảm ơn rất nhiều! Nó đã giúp tôi dễ dàng loại bỏ đuôi .gz khỏi tất cả các tên tệp một cách đệ quy. while IFS= read -r file; do mv $file ${file%.gz}; done < <(find -type f -name "*.gz")
Vinay Vissh

1
@CasualCoder rất vui khi đọc điều đó :) Lưu ý bạn có thể nói trực tiếp find .... -exec mv .... Ngoài ra, hãy cẩn thận với $filevì nó sẽ không thành công nếu nó chứa khoảng trắng. Sử dụng tốt hơn báo giá "$file".
fedorqui 'VẬY đừng làm hại nữa'

1

nếu bạn có Ruby (1.9+)

ruby -e 'Dir["**/*._test.rb"].each{|x|test(?f,x) and File.rename(x,x.gsub(/_test/,"_spec") ) }'

1

Trong câu trả lời của ramtam mà tôi thích, phần tìm thấy hoạt động tốt nhưng phần còn lại thì không nếu đường dẫn có khoảng trắng. Tôi không quá quen thuộc với sed, nhưng tôi đã có thể sửa đổi câu trả lời đó thành:

find . -name "*_test.rb" | perl -pe 's/^((.*_)test.rb)$/"\1" "\2spec.rb"/' | xargs -n2 mv

Tôi thực sự cần một thay đổi như thế này vì trong trường hợp sử dụng của tôi, lệnh cuối cùng trông giống

find . -name "olddir" | perl -pe 's/^((.*)olddir)$/"\1" "\2new directory"/' | xargs -n2 mv

1

Tôi không có tâm huyết để làm lại tất cả, nhưng tôi đã viết điều này để trả lời cho Commandline Find Sed Exec . Ở đó, người hỏi muốn biết cách di chuyển toàn bộ cây, có thể loại trừ một hoặc hai thư mục, và đổi tên tất cả các tệp và thư mục chứa chuỗi "OLD" thay vì chứa "MỚI" .

Bên cạnh việc mô tả cách thức với độ chi tiết khó khăn bên dưới, phương pháp này cũng có thể độc đáo ở chỗ nó kết hợp gỡ lỗi tích hợp sẵn. Về cơ bản, nó không thực hiện bất cứ điều gì như đã viết ngoại trừ biên dịch và lưu vào một biến tất cả các lệnh mà nó tin rằng nó phải làm để thực hiện công việc được yêu cầu.

Nó cũng tránh các vòng lặp càng nhiều càng tốt. Bên cạnh việc sedtìm kiếm đệ quy cho nhiều hơn một kết quả phù hợp của mẫu , không có đệ quy nào khác theo như tôi biết.

Và cuối cùng, điều này được nullphân định hoàn toàn - nó không đi trên bất kỳ ký tự nào trong bất kỳ tên tệp nào ngoại trừ null. Tôi không nghĩ rằng bạn nên có điều đó.

Nhân tiện, điều này THỰC SỰ nhanh chóng. Nhìn:

% _mvnfind() { mv -n "${1}" "${2}" && cd "${2}"
> read -r SED <<SED
> :;s|${3}\(.*/[^/]*${5}\)|${4}\1|;t;:;s|\(${5}.*\)${3}|\1${4}|;t;s|^[0-9]*[\t]\(mv.*\)${5}|\1|p
> SED
> find . -name "*${3}*" -printf "%d\tmv %P ${5} %P\000" |
> sort -zg | sed -nz ${SED} | read -r ${6}
> echo <<EOF
> Prepared commands saved in variable: ${6}
> To view do: printf ${6} | tr "\000" "\n"
> To run do: sh <<EORUN
> $(printf ${6} | tr "\000" "\n")
> EORUN
> EOF
> }
% rm -rf "${UNNECESSARY:=/any/dirs/you/dont/want/moved}"
% time ( _mvnfind ${SRC=./test_tree} ${TGT=./mv_tree} \
> ${OLD=google} ${NEW=replacement_word} ${sed_sep=SsEeDd} \
> ${sh_io:=sh_io} ; printf %b\\000 "${sh_io}" | tr "\000" "\n" \
> | wc - ; echo ${sh_io} | tr "\000" "\n" |  tail -n 2 )

   <actual process time used:>
    0.06s user 0.03s system 106% cpu 0.090 total

   <output from wc:>

    Lines  Words  Bytes
    115     362   20691 -

    <output from tail:>

    mv .config/replacement_word-chrome-beta/Default/.../googlestars \
    .config/replacement_word-chrome-beta/Default/.../replacement_wordstars        

LƯU Ý: Trên đây functioncó thể sẽ yêu cầu GNUphiên bản sedfindđể xử lý đúng đắn các find printfsed -z -e:;recursive regex test;tcác cuộc gọi. Nếu những điều này không có sẵn cho bạn, chức năng có thể bị trùng lặp với một vài điều chỉnh nhỏ.

Điều này sẽ làm mọi thứ bạn muốn từ đầu đến cuối với rất ít phiền phức. Tôi đã làm forkvới sed, nhưng tôi cũng đang thực hành một số sedkỹ thuật phân nhánh đệ quy nên đó là lý do tại sao tôi ở đây. Tôi đoán nó giống như được cắt tóc giảm giá ở một trường dạy cắt tóc. Đây là quy trình làm việc:

  • rm -rf ${UNNECESSARY}
    • Tôi cố ý bỏ qua bất kỳ lệnh gọi chức năng nào có thể xóa hoặc phá hủy dữ liệu dưới bất kỳ hình thức nào. Bạn đề cập đến điều đó ./appcó thể không mong muốn. Xóa nó hoặc chuyển nó đi nơi khác trước, hoặc, cách khác, bạn có thể xây dựng một \( -path PATTERN -exec rm -rf \{\} \)thói quen findđể thực hiện nó theo chương trình, nhưng đó là tất cả của bạn.
  • _mvnfind "${@}"
    • Khai báo các đối số của nó và gọi hàm worker. ${sh_io}đặc biệt quan trọng ở chỗ nó lưu được kết quả trả về từ hàm. ${sed_sep}đến trong giây phút gần kề; đây là một chuỗi tùy ý được sử dụng để tham chiếu sedđệ quy trong hàm. Nếu ${sed_sep}được đặt thành một giá trị có thể được tìm thấy trong bất kỳ tên tệp hoặc đường dẫn nào của bạn được tác động vào ... thì, đừng để nó như vậy.
  • mv -n $1 $2
    • Toàn bộ cây được chuyển từ đầu. Nó sẽ tiết kiệm rất nhiều đau đầu; tin tôi đi. Phần còn lại của những gì bạn muốn làm - đổi tên - chỉ đơn giản là vấn đề siêu dữ liệu hệ thống tệp. Ví dụ: nếu bạn đang di chuyển ổ đĩa này từ ổ đĩa này sang ổ đĩa khác hoặc qua bất kỳ ranh giới hệ thống tệp nào, thì tốt hơn hết bạn nên làm điều đó cùng một lúc với một lệnh. Nó cũng an toàn hơn. Lưu ý -noclobbertùy chọn được đặt cho mv; như đã viết, hàm này sẽ không đặt ${SRC_DIR}ở vị trí ${TGT_DIR}đã tồn tại.
  • read -R SED <<HEREDOC
    • Tôi đặt tất cả các lệnh của sed ở đây để tiết kiệm khi thoát khỏi những rắc rối và đọc chúng thành một biến để cấp cho sed bên dưới. Giải thích bên dưới.
  • find . -name ${OLD} -printf
    • Chúng tôi bắt đầu findquá trình. Với việc findchúng tôi chỉ tìm kiếm bất kỳ thứ gì cần đổi tên vì chúng tôi đã thực hiện tất cả các thao tác từ vị trí đến vị trí mvvới lệnh đầu tiên của hàm. Thay vì thực hiện bất kỳ hành động trực tiếp nào find, chẳng hạn như một execcuộc gọi, chúng tôi thay vào đó sử dụng nó để xây dựng động dòng lệnh với -printf.
  • %dir-depth :tab: 'mv '%path-to-${SRC}' '${sed_sep}'%path-again :null delimiter:'
    • Sau khi findđịnh vị các tệp chúng tôi cần, nó sẽ trực tiếp xây dựng và in ra ( hầu hết ) lệnh mà chúng tôi sẽ cần để xử lý việc đổi tên của bạn. Phần %dir-depthgắn ở đầu mỗi dòng sẽ giúp đảm bảo rằng chúng tôi không cố gắng đổi tên một tệp hoặc thư mục trong cây bằng một đối tượng mẹ chưa được đổi tên. findsử dụng tất cả các loại kỹ thuật tối ưu hóa để đi qua cây hệ thống tệp của bạn và không chắc chắn rằng nó sẽ trả về dữ liệu chúng tôi cần theo thứ tự an toàn cho hoạt động. Đây là lý do tại sao chúng ta tiếp theo ...
  • sort -general-numerical -zero-delimited
    • Chúng tôi sắp xếp tất cả findđầu ra của dựa trên %directory-depthđể các đường dẫn gần nhất trong mối quan hệ với $ {SRC} được làm việc đầu tiên. Điều này tránh các lỗi có thể xảy ra liên quan đến việc nhập mvtệp vào các vị trí không tồn tại và nó giảm thiểu nhu cầu lặp lại đệ quy. ( trên thực tế, bạn có thể khó tìm thấy một vòng lặp nào cả )
  • sed -ex :rcrs;srch|(save${sep}*til)${OLD}|\saved${SUBSTNEW}|;til ${OLD=0}
    • Tôi nghĩ đây là vòng lặp duy nhất trong toàn bộ tập lệnh và nó chỉ lặp lại trên thứ hai %Pathđược in cho mỗi chuỗi trong trường hợp nó chứa nhiều hơn một giá trị $ {OLD} có thể cần thay thế. Tất cả các giải pháp khác mà tôi tưởng tượng đều liên quan đến sedquy trình thứ hai và mặc dù một vòng lặp ngắn có thể không được mong muốn, nhưng chắc chắn nó sẽ đánh bại quá trình sinh sản và tạo ra toàn bộ quy trình.
    • Vì vậy, về cơ bản những gì sedở đây là tìm kiếm $ {sed_sep}, sau đó, sau khi tìm thấy nó, lưu nó và tất cả các ký tự mà nó gặp cho đến khi tìm thấy $ {OLD}, sau đó nó sẽ thay thế bằng $ {NEW}. Sau đó, nó quay trở lại $ {sed_sep} và tìm lại $ {OLD}, trong trường hợp nó xuất hiện nhiều lần trong chuỗi. Nếu nó không được tìm thấy, nó sẽ in ra chuỗi đã sửa đổi stdout(mà nó sẽ bắt lại tiếp theo) và kết thúc vòng lặp.
    • Điều này tránh phải phân tích cú pháp toàn bộ chuỗi và đảm bảo rằng nửa đầu của mvchuỗi lệnh, tất nhiên cần bao gồm $ {OLD}, sẽ bao gồm nó và nửa sau được thay đổi nhiều lần nếu cần thiết để xóa $ {OLD} tên từ mvđường dẫn đích của.
  • sed -ex...-ex search|%dir_depth(save*)${sed_sep}|(only_saved)|out
    • Hai -execcuộc gọi ở đây xảy ra không có giây phút nào fork. Trong lần đầu tiên, như chúng ta đã thấy, chúng tôi sửa đổi mvlệnh được cung cấp bởi lệnh hàm findcủa 's -printfkhi cần thiết để thay đổi chính xác tất cả các tham chiếu của $ {OLD} thành $ {NEW}, nhưng để làm như vậy chúng tôi phải sử dụng một số các điểm tham chiếu tùy ý không được đưa vào kết quả cuối cùng. Vì vậy, sau khi sedhoàn thành tất cả những gì nó cần làm, chúng tôi hướng dẫn nó xóa sạch các điểm tham chiếu của nó khỏi bộ đệm giữ trước khi chuyển nó đi.

VÀ BÂY GIỜ CHÚNG TÔI ĐANG QUAY LẠI

read sẽ nhận được một lệnh giống như sau:

% mv /path2/$SRC/$OLD_DIR/$OLD_FILE /same/path_w/$NEW_DIR/$NEW_FILE \000

Nó sẽ readnó vào ${msg}như ${sh_io}có thể được xem xét theo ý bên ngoài của hàm.

Mát mẻ.

-Mike


1

Tôi đã có thể xử lý các tên tệp có khoảng trắng bằng cách làm theo các ví dụ do onitake đề xuất.

Điều này không bị hỏng nếu đường dẫn chứa khoảng trắng hoặc chuỗi test:

find . -name "*_test.rb" -print0 | while read -d $'\0' file
do
    echo mv "$file" "$(echo $file | sed s/test/spec/)"
done

1

Đây là một ví dụ nên hoạt động trong mọi trường hợp. Hoạt động đệ quy, chỉ cần shell và hỗ trợ tên tệp có dấu cách.

find spec -name "*_test.rb" -print0 | while read -d $'\0' file; do mv "$file" "`echo $file | sed s/test/spec/`"; done

0
$ find spec -name "*_test.rb"
spec/dir2/a_test.rb
spec/dir1/a_test.rb

$ find spec -name "*_test.rb" | xargs -n 1 /usr/bin/perl -e '($new=$ARGV[0]) =~ s/test/spec/; system(qq(mv),qq(-v), $ARGV[0], $new);'
`spec/dir2/a_test.rb' -> `spec/dir2/a_spec.rb'
`spec/dir1/a_test.rb' -> `spec/dir1/a_spec.rb'

$ find spec -name "*_spec.rb"
spec/dir2/b_spec.rb
spec/dir2/a_spec.rb
spec/dir1/a_spec.rb
spec/dir1/c_spec.rb

Ah..tôi không biết cách sử dụng sed khác ngoài việc đặt logic trong một tập lệnh shell và gọi nó trong thực thi. didnt xem yêu cầu để sử dụng sed ban đầu
Damodharan R

0

Câu hỏi của bạn có vẻ là về sed, nhưng để hoàn thành mục tiêu đổi tên đệ quy của bạn, tôi đề xuất như sau, được trích xuất một cách đáng xấu hổ từ một câu trả lời khác mà tôi đã đưa ra ở đây: đổi tên đệ quy trong bash

#!/bin/bash
IFS=$'\n'
function RecurseDirs
{
for f in "$@"
do
  newf=echo "${f}" | sed -e 's/^(.*_)test.rb$/\1spec.rb/g'
    echo "${f}" "${newf}"
    mv "${f}" "${newf}"
    f="${newf}"
  if [[ -d "${f}" ]]; then
    cd "${f}"
    RecurseDirs $(ls -1 ".")
  fi
done
cd ..
}
RecurseDirs .

Làm thế nào để sedcông việc hoạt động mà không thoát ()nếu bạn không đặt -rtùy chọn?
mikeserv

0

Cách an toàn hơn để thực hiện đổi tên với find utils và loại biểu thức chính quy sed:

  mkdir ~/practice

  cd ~/practice

  touch classic.txt.txt

  touch folk.txt.txt

Xóa phần mở rộng ".txt.txt" như sau:

  cd ~/practice

  find . -name "*txt" -execdir sh -c 'mv "$0" `echo "$0" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'`' {} \;

Nếu bạn sử dụng dấu + thay cho; để hoạt động ở chế độ hàng loạt, lệnh trên sẽ chỉ đổi tên tệp phù hợp đầu tiên, nhưng không đổi tên toàn bộ danh sách tệp khớp bằng 'tìm'.

  find . -name "*txt" -execdir sh -c 'mv "$0" `echo "$0" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'`' {} +

0

Đây là một oneliner tuyệt vời thực hiện thủ thuật. Sed không thể xử lý quyền này, đặc biệt nếu nhiều biến được truyền bởi xargs với -n 2. Một phân vùng bash sẽ xử lý điều này dễ dàng như:

find ./spec -type f -name "*_test.rb" -print0 | xargs -0 -I {} sh -c 'export file={}; mv $file ${file/_test.rb/_spec.rb}'

Thêm -type -f sẽ giới hạn các hoạt động di chuyển chỉ đối với tệp, -print 0 sẽ xử lý các khoảng trống trong đường dẫn.



0

Đây là giải pháp làm việc của tôi:

for FILE in {{FILE_PATTERN}}; do echo ${FILE} | mv ${FILE} $(sed 's/{{SOURCE_PATTERN}}/{{TARGET_PATTERN}}/g'); done
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.