tìm -exec với nhiều lệnh


429

Tôi đang cố gắng sử dụng find -exec với nhiều lệnh mà không thành công. Có ai biết nếu các lệnh như sau là có thể?

find *.txt -exec echo "$(tail -1 '{}'),$(ls '{}')" \;

Về cơ bản, tôi đang cố gắng in dòng cuối cùng của mỗi tệp txt trong thư mục hiện tại và in ở cuối dòng, dấu phẩy theo sau là tên tệp.



1
Theo như kiểm tra khả năng của lệnh, bạn đã không thử nó trên hệ thống của bạn?
Sriram

1
Từ findtrang hướng dẫn: There are unavoidable security problems surrounding use of the -exec option; you should use the -execdir option instead.unixhelp.ed.ac.uk/CGI/man-cgi?find
JVE999


Liên kết @ JVE999 bị hỏng, thay thế tại ss64.com/bash/find.html
Keith M

Câu trả lời:


657

findchấp nhận nhiều -execphần cho lệnh. Ví dụ:

find . -name "*.txt" -exec echo {} \; -exec grep banana {} \;

Lưu ý rằng trong trường hợp này, lệnh thứ hai sẽ chỉ chạy nếu lệnh đầu tiên trả về thành công, như được đề cập bởi @Caleb. Nếu bạn muốn cả hai lệnh chạy bất kể thành công hay thất bại của chúng, bạn có thể sử dụng cấu trúc này:

find . -name "*.txt" \( -exec echo {} \; -o -exec true \; \) -exec grep banana {} \;

Làm thế nào để grep hai lần? điều này không thành công: find ./* -exec grep -v 'COLD,' {} \; -exec egrep -i "my_ chuỗi" {} \;
rajeev

51
@rajeev Người thực thi thứ hai sẽ chỉ chạy nếu mã trả về cho lần trả về đầu tiên thành công, nếu không nó sẽ bị bỏ qua. Điều này có lẽ nên được lưu ý trong câu trả lời này.
Caleb

1
Đây sẽ là câu trả lời
pylover

1
Lưu ý việc sử dụng -ntrong một số câu trả lời khác để chặn dòng mới được tạo bởi echo, sẽ rất hữu ích nếu lệnh thứ hai của bạn chỉ tạo ra một dòng đầu ra và bạn muốn chúng dễ đọc hơn.
William Turrell

Giải pháp tuyệt vời. Hoạt động như một lá bùa.
Philippe Delteil


52

Một trong những điều sau đây:

find *.txt -exec awk 'END {print $0 "," FILENAME}' {} \;

find *.txt -exec sh -c 'echo "$(tail -n 1 "$1"),$1"' _ {} \;

find *.txt -exec sh -c 'echo "$(sed -n "\$p" "$1"),$1"' _ {} \;

12
Dấu gạch dưới trước {} để làm gì?
qed

2
@qed: Đó là một giá trị vứt đi giữ vị trí của $0. Hãy thử điều này với "foobar" thay vì "_": find /usr/bin -name find -exec sh -c 'echo "[$0] [$1]"' foobar {} \;- đầu ra: "[foobar] [/ usr / bin / find]".
Tạm dừng cho đến khi có thông báo mới.

1
@XuWang: Vâng, tôi sẽ nói đó là trường hợp. Như bạn biết, $0thường là tên chương trình ( ARGV[0]).
Tạm dừng cho đến khi có thông báo mới.

3
Điều quan trọng là, đối với phương pháp này, tập lệnh được chuyển đến sh -cnằm trong dấu ngoặc đơn, không phải gấp đôi. Nếu không thì $1trong phạm vi sai.
Nick

1
Dấu ngoặc kép @Nick không có gì để làm với nó - bạn có thể viết '$1'với dấu ngoặc kép miễn là bạn thoát khỏi biến ( "\$1"). Bạn cũng có thể thoát các nhân vật khác ( "\"").
Camilo Martin

22

Một cách khác là như thế này:

multiple_cmd() { 
    tail -n1 $1; 
    ls $1 
}; 
export -f multiple_cmd; 
find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;

trong một dòng

multiple_cmd() { tail -1 $1; ls $1 }; export -f multiple_cmd; find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;
  • " multiple_cmd()" - là một chức năng
  • " export -f multiple_cmd" - sẽ xuất nó để bất kỳ subshell nào khác có thể nhìn thấy nó
  • " find *.txt -exec bash -c 'multiple_cmd "$0"' {} \;" - tìm thấy sẽ thực thi chức năng trên ví dụ của bạn

Theo cách này, nhiều_cmd có thể dài và phức tạp như bạn cần.

Hi vọng điêu nay co ich.


hoàn hảo, chỉ là những gì tôi cần!
Anentropic

không hoạt động với tôi trên OSX
Thomas

@Thomas nó làm nhưng hãy thử một lớp lót này, đã thử nghiệm trong osx. Tôi đã tạo một thư mục có tên 'aaa' với một số tệp / thư mục trong đó và CDd cho nó. Sau đó, ~/aaa$ acmd() { echo x \"$1\" x; }; export -f acmd; find . -exec bash -c 'acmd {}' \;
barlop

@barlop, tôi sẽ thử. cảm ơn!
Thomas

14

Có một cách dễ dàng hơn:

find ... | while read -r file; do
    echo "look at my $file, my $file is amazing";
done

Cách khác:

while read -r file; do
    echo "look at my $file, my $file is amazing";
done <<< "$(find ...)"

1
tên tệp có thể có dòng mới trong đó, đây là lý do tại sao find có đối số -print0 và xargs có đối số -0
abasterfield

@abasterfield Tôi luôn hy vọng không bao giờ tìm thấy những người trong lol hoang dã
Camilo Martin

1
điều tôi muốn làm là "tìm ... -exec zcat {} | wc -l \;" mà không làm việc Tuy nhiên, tìm ... | trong khi đọc tập tin -r; làm tiếng vang "$ file: zcat $file | wc -l"; làm xong không, cảm ơn bạn!
Greg Dougherty

Trong bình luận ở trên, tôi có "dấu tích ngược" xung quanh "zcat $ file | wc -l". Thật không may, SO biến những điều đó thành định dạng, vì vậy tôi đã thêm nó như một câu trả lời thực tế với mã chính xác hiển thị
Greg Dougherty

1
@GregDougherty Bạn có thể thoát khỏi backticks `để làm điều đó bằng cách sử dụng dấu gạch chéo ngược như vậy: \​`(vẫn vậy, đó là một lý do tốt khác để sử dụng $()thay thế).
Camilo Martin

8

Tôi không biết liệu bạn có thể làm điều này với find hay không, nhưng một giải pháp thay thế sẽ là tạo một kịch bản shell và chạy nó với find.

dòng cuối.sh:

echo $(tail -1 $1),$1

Làm cho tập lệnh thực thi

chmod +x lastline.sh

Sử dụng find:

find . -name "*.txt" -exec ./lastline.sh {} \;

7
backticks không được chấp nhận, vui lòng khuyến khích việc sử dụng $ (...) dễ đọc hơn, dễ đọc hơn và dễ dàng lồng nhau. Cảm ơn bạn.
người dùng không xác định

4

Câu trả lời đầu tiên của Denis là câu trả lời để giải quyết rắc rối. Nhưng trên thực tế, nó không còn là một phát hiện với một số lệnh chỉ trong một lệnh thực thi như tiêu đề gợi ý. Để trả lời một thực thi với một số lệnh, chúng ta sẽ phải tìm một cái gì đó khác để giải quyết. Đây là một ví dụ:

Giữ 10000 dòng tệp .log cuối cùng đã được sửa đổi trong 7 ngày qua bằng cách sử dụng lệnh 1 exec bằng các tham chiếu {}

1) xem lệnh sẽ làm gì trên tập tin nào:

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "echo tail -10000 {} \> fictmp; echo cat fictmp \> {} " \;

2) Thực hiện: (không lưu ý thêm "\>" mà chỉ ">" điều này là muốn)

find / -name "*.log" -a -type f -a -mtime -7 -exec sh -c "tail -10000 {} > fictmp; cat fictmp > {} ; rm fictmp" \;


Nhưng điều này sẽ phá vỡ nếu một trong những tên tập tin có một khoảng trống, tôi tin.
Camilo Martin

4

Nhờ Camilo Martin, tôi đã có thể trả lời một câu hỏi liên quan:

Điều tôi muốn làm là

find ... -exec zcat {} | wc -l \;

mà không làm việc Tuy nhiên,

find ... | while read -r file; do echo "$file: `zcat $file | wc -l`"; done

không hoạt động, cảm ơn bạn


2

Mở rộng câu trả lời của @ Tinker,

Trong trường hợp của tôi, tôi cần phải tạo một command | command | commandbên trong -execđể in cả tên tệp và văn bản tìm thấy trong các tệp có chứa một văn bản nhất định.

Tôi đã có thể làm điều đó với:

find . -name config -type f \( -exec  grep "bitbucket" {} \; -a -exec echo {} \;  \) 

kết quả là:

    url = git@bitbucket.org:a/a.git
./a/.git/config
    url = git@bitbucket.org:b/b.git
./b/.git/config
    url = git@bitbucket.org:c/c.git
./c/.git/config

1
Bạn cũng có thể in tên tệp và nội dung grep'd trên một dòng bằng cách chuyển /dev/nulldưới dạng đối số thứ hai cho greplệnh với một -exectham số:find . -name config -type f -exec grep "bitbucket" {} /dev/null \;
Bill Feth

1

nên sử dụng xargs :)

find *.txt -type f -exec tail -1 {} \; | xargs -ICONSTANT echo $(pwd),CONSTANT

một số khác (làm việc trên osx)

find *.txt -type f -exec echo ,$(PWD) {} + -exec tail -1 {} + | tr ' ' '/'

3
Điều này bỏ qua trường hợp sử dụng chính cho find- các tình huống trong đó số lượng tệp phù hợp quá lớn cho một dòng lệnh. -execlà một cách để vượt qua giới hạn này. Đường ống dẫn đến một tiện ích bỏ lỡ lợi ích đó.
Chris Johnson

xargs -ntồn tại để chọn số lượng trận đấu cho mỗi lần gọi. xargs -n 1 foocmdsẽ thực hiện foocmd {}cho mọi trận đấu.
AndrewF

0

Một find+xargscâu trả lời.

Ví dụ dưới đây tìm thấy tất cả .htmlcác tệp và tạo một bản sao có .BAKphần mở rộng được nối thêm (ví dụ 1.html> 1.html.BAK).

Lệnh đơn có nhiều chỗ dành sẵn

find . -iname "*.html" -print0 | xargs -0 -I {} cp -- "{}" "{}.BAK"

Nhiều lệnh có nhiều chỗ dành sẵn

find . -iname "*.html" -print0 | xargs -0 -I {} echo "cp -- {} {}.BAK ; echo {} >> /tmp/log.txt" | sh

# if you need to do anything bash-specific then pipe to bash instead of sh

Lệnh này cũng sẽ hoạt động với các tệp bắt đầu bằng dấu gạch nối hoặc chứa khoảng trắng như -my file.htmlnhờ trích dẫn tham số và --sau cpđó báo hiệu đến cpcuối tham số và bắt đầu tên tệp thực tế.

-print0 dẫn các kết quả với các đầu cuối null-byte.


cho xargs các -I {}tham số định nghĩa {}như giữ chỗ; bạn có thể sử dụng bất kỳ giữ chỗ nào bạn thích; -0chỉ ra rằng các mục đầu vào được phân tách bằng null.

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.