Thao tác tên tệp được lấy từ lệnh find


10

Tôi còn khá mới với Bash và đang cố gắng làm một cái gì đó trên bề mặt có vẻ khá đơn giản - hãy chạy tìm trên một hệ thống phân cấp thư mục để lấy tất cả các tệp * .wma, dẫn đến một lệnh mà tôi chuyển đổi chúng sang mp3 và lưu tệp đã chuyển đổi dưới dạng .mp3. Tôi nghĩ rằng lệnh sẽ giống như sau (Tôi đã bỏ lệnh chuyển đổi âm thanh và thay vào đó là sử dụng echo để minh họa):

$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3

Theo tôi hiểu, -print0 arg sẽ cho phép tôi xử lý tên tệp có khoảng trắng (mà nhiều trong số này làm như chúng là các tệp nhạc). Sau đó, tôi đang mong đợi (là kết quả của xargs) rằng mỗi đường dẫn tệp từ find được ghi lại trong f và sử dụng khớp / xóa chuỗi con từ cuối chuỗi, rằng tôi nên lặp lại đường dẫn tệp gốc bằng mp3 mở rộng thay vì wma. Tuy nhiên, thay vì kết quả này, tôi thấy như sau:

*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...

Vì vậy, câu hỏi của tôi (ngoài 'cụ thể tôi đang làm gì sai ở đây'), là đây - làm các giá trị là kết quả của một hoạt động đường ống cần được xử lý khác nhau trong các hoạt động thao tác chuỗi so với các kết quả của phép gán biến ?


1
Không có lý do để sử dụng xargsvới find. Nó đi kèm với một -exectùy chọn. Bạn có thể chỉ cần thêm lệnh bạn sẽ sử dụng vào câu hỏi của bạn và ai đó có thể chỉ cho bạn findlệnh chính xác ?
ixtmixilix

có (như tôi đã lưu ý bên dưới), miễn là tôi vẫn có thể thực hiện thao tác chuỗi trên mỗi kết quả của lệnh find (ví dụ: {}thành viên)
Howard Dierking

thực sự có trường hợp cạnh xargslà phù hợp hơn exec. Xem stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs để biết trường hợp cụ thể.
d34dh0r53

@ d34dh0r53 Trường hợp cạnh nào? Các chủ đề bạn liên kết đến không điểm bất kỳ.
Gilles 'SO- ngừng trở nên xấu xa'

Câu trả lời:


8

Như các câu trả lời khác đã được xác định, ${f%.*}được mở rộng bởi shell trước khi nó chạy xargslệnh. Bạn cần mở rộng này xảy ra một lần cho mỗi tên tệp, với biến shell fđược đặt thành tên tệp (truyền -I fkhông làm điều đó: xargskhông có khái niệm về biến shell, nó sẽ tìm chuỗi ftrong lệnh, vì vậy nếu bạn được sử dụng, ví dụ như xargs -I e echo …nó sẽ thực hiện các lệnh như ./somedir/somefile.wmacho .mp3).

Tiếp tục phương pháp này, hãy nói xargsđể gọi một trình bao có thể thực hiện việc mở rộng. Tốt hơn, hãy nói find- xargslà một công cụ phần lớn đã lỗi thời và khó sử dụng một cách chính xác, vì các phiên bản tìm kiếm hiện đại có cấu trúc thực hiện cùng một công việc (và hơn thế nữa) với ít khó khăn hơn về hệ thống ống nước. Thay vì find … -print0 | xargs -0 command …, chạy find … -exec command … {} +.

find . -name '*.wma' -type f -exec sh -c 'for f; do echo "${f%.*}.mp3"; done' _ {} +

Đối số _$0trong vỏ; tên tệp được truyền dưới dạng đối số vị trí for f; do …lặp lại. Một phiên bản đơn giản hơn của lệnh này thực thi một shell riêng cho mỗi tệp, tương đương nhưng chậm hơn một chút:

find . -name '*.wma' -type f -exec sh -c 'echo "${0%.*}.mp3"' {} \;

Bạn thực sự không cần phải sử dụng findở đây, giả sử bạn đang chạy một vỏ hợp lý gần đây (ksh93, bash ≥4.0 hoặc zsh). Trong bash, đặt shopt -s globstarvào của bạn .bashrcđể kích hoạt **/mô hình toàn cầu để lặp lại trong các thư mục con (theo ksh, đó là set -o globstar). Sau đó bạn có thể chạy

for f in **/*.wma; do
  echo "${f%.*}.mp3"
done

(Nếu bạn có các thư mục được gọi *.wma, hãy thêm [ -f "$f" ] || continuevào đầu vòng lặp.)


những lời giải thích tuyệt vời cũng như chỉ cho tôi một hướng mà tôi thậm chí không nhận ra (nâng cấp bash shell của mình để có được chức năng của sao) - cuối cùng, Globing đệ quy là giải pháp giữ cho lệnh đơn giản và hoàn thành mọi thứ tôi đang cố gắng làm . Cảm ơn!
Howard Dierking

6

Trong trường hợp đánh giá giải pháp của bạn là $ {f%. *}. Mp3 được thực hiện trong shell mà bạn đang gọi toàn bộ lệnh, chứ không phải trong shell được chia bởi xargs . Và trong shell của bạn không có biến f , nó được thay thế bằng một chuỗi rỗng.

Giải pháp sử dụng xargs với -I f :

% cat 1.sh 
#!/bin/sh
f=$1
echo ${f%.*}.mp3
% /bin/ls -1
1.sh
aaa.wma
bbbb.wma
ccccc.wma
% find ./ -name '*.wma' -type f -print0 | xargs -0 -I f ./1.sh f
./aaa.mp3
./ccccc.mp3
./bbbb.mp3

Nhưng tôi sẽ làm theo cách này:

% find ./ -name '*.wma' -type f | sed 's,\.wma$,.mp3,'
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.