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 sed
tì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 null
phâ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 function
có thể sẽ yêu cầu GNU
phiên bản sed
và find
để xử lý đúng đắn các find printf
và sed -z -e
và :;recursive regex test;t
cá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 fork
với sed
, nhưng tôi cũng đang thực hành một số sed
kỹ 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 đó
./app
có 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 ý
-noclobber
tù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
find
quá trình. Với việc find
chú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í mv
vớ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 exec
cuộ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-depth
gắ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. find
sử 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 mv
tệ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 sed
quy 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
mv
chuỗ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
-exec
cuộ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 mv
lệnh được cung cấp bởi lệnh hàm find
của 's -printf
khi 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 sed
hoà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ẽ read
nó 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