2013-03-16 43 views
5

Trong thư mục ~/temp/a/foo/~/temp/b/foo foo/ Tôi có một số tập tin có tên bar1, bar2, bar bar1, bar bar2 vvdấu ngoặc kép vs mở rộng dấu hoa thị tên tập tin trong Bash

Tôi cố gắng để viết một dòng Bash rằng bản sao tất cả những tập tin này trong một thư mục có chứa "foo" như là phần cuối của tên vào thư mục phía trên thư mục "foo" tương ứng.

Chừng nào không có dấu cách trong tên tập tin, đây là một nhiệm vụ dễ dàng, nhưng hai lệnh sau thất bại khi giao dịch với các foo foo thư mục:

for dir in `find . -type d -name '*foo'` ; do cp $dir/* "$(echo $dir|sed 's_foo__g')" ; done 

(Lệnh cp thất bại để xem cuối cùng foo của "foo foo" như một phần của tên thư mục tương tự.)

for dir in `find . -type d -name '*foo'` ; do cp "$dir/*" "$(echo $dir|sed 's_foo__g')" ; done 

("$dir/*" không được mở rộng.)

Nỗ lực như thay thế $dir/* bằng "$(echo $dir/*)" thậm chí còn kém thành công hơn.

Có cách nào dễ dàng để mở rộng $dir/* để cp hiểu không?

+0

Vui lòng xem http://mywiki.wooledge.org/DontReadLinesWithFor –

Trả lời

4

Không chỉ là vòng lặp for sai - sed cũng không phải là công cụ phù hợp cho công việc này.

while IFS= read -r -d '' dir; do 
    cp "$dir" "${dir/foo/}" 
done < <(find . -type d -name '*foo' -print0) 

Sử dụng -print0 trên find (và IFS= read -r -d '') đảm bảo rằng tên tập tin với dòng mới sẽ không mess bạn lên.

Sử dụng cấu trúc < <(...) đảm bảo rằng nếu bên trong vòng lặp của bạn đặt biến, thay đổi trạng thái thư mục hoặc thực hiện những điều tương tự, những thay đổi đó sẽ tồn tại (phía bên phải của đường ống nằm trong vỏ bọc trong bash) vào một vòng lặp while có nghĩa là bất kỳ thay đổi nào đối với trạng thái của trình bao được tạo bên trong vòng lặp while đó sẽ bị loại bỏ khi thoát khỏi nó).

Sử dụng ${dir/foo/} hiệu quả hơn nhiều so với cách gọi sed, vì nó thay thế chuỗi bên trong thành bash.

+0

Bạn có chắc chắn 'IFS =' là cần thiết không? Tuyệt vời đăng btw! – janos

+0

Bất chấp đề nghị của janos để chấp nhận câu trả lời này như một giải pháp, nó chỉ ra rằng mã không thực sự di chuyển bất kỳ tập tin nào. Bash 4.2.24 phàn nàn rằng cp không thể stat ''. Tôi không chắc chắn 100% dòng lệnh 'cp' $ dir "" $ {dir/foo /} "' nào. Liệu nó thực sự cố gắng sao chép các tập tin vào một thư mục hiện có phía trên thư mục 'foo'? – uvett

+0

Không nên 'đọc ... tập tin' được' đọc ... dir'? –

0

Đổi tên ~/temp/b/foo foo/ thành thứ gì đó không có dấu cách, ví dụ: ~/temp/b/foo_foo/ và làm những gì bạn đang cố gắng làm lại. Sau đó bạn có thể đổi tên nó trở lại bản gốc, với không gian, nếu bạn thực sự phải. BTW. bản thân tôi, tôi không bao giờ sử dụng tên tập tin có chứa không gian, chỉ đơn giản là để tránh các biến chứng như một trong những bạn đang phải đối mặt bây giờ.

+0

Tôi đã cố gắng đơn giản hóa một vấn đề liên quan đến hàng chục thư mục và hàng trăm tệp có tên xấu. Đổi tên chúng có vẻ giống như một tùy chọn phụ tối ưu, đặc biệt là vì tôi sẽ phải khôi phục lại tên thư mục sau này. – uvett

2

Vấn đề ở đây không phải là với cp, nhưng với for, vì theo mặc định, nó chia tách đầu ra của vỏ của bạn theo từ, chứ không phải theo tên thư mục.

Một workaround lười biếng là sử dụng while thay vào đó và xử lý danh sách các dòng thư mục bằng dòng như sau:

find . -type d -name '*foo' | while read dir; do cp "$dir"/* "$(echo $dir | sed 's_foo__g')" ; done 

này cần khắc phục vấn đề của bạn với không gian, nhưng điều này là không phải là một giải pháp hoàn hảo.

Xem câu trả lời của Charles Duffy về giải pháp chính xác hơn.

+0

Hầu như đúng, nhưng bạn thực sự nên NUL-delimit nội dung của các đường ống - nếu không, một tên tập tin độc hại crafted có chứa một dòng mới có thể gây ra bất kỳ tập tin tùy ý được sao chép. –

+0

... bằng cách này, nó cũng không chính xác rằng 'dir' sẽ không bao giờ chứa khoảng trống như một tạo tác của cách' for' hoạt động; tách từ là _không phải là một phần của ngữ nghĩa của, và nếu 'IFS' được thiết lập để không chứa khoảng trống, thì chuỗi đầu ra phân tách _can_ chứa khoảng trắng. Điều đó nói rằng, dựa vào việc tách chuỗi là sai vì các lý do khác (ví dụ, bởi vì việc mở rộng glob xảy ra cùng một lúc). –

+0

Cảm ơn, điều này dường như làm việc cho tất cả các vấn đề sao chép tập tin trong thế giới thực của tôi. Rằng nó thực sự thất bại trên ví dụ đơn giản của tôi, là một chi tiết vui nhộn. (sed xóa cả 'foo's trong tên thư mục' foo foo'). – uvett

Các vấn đề liên quan