Tạo xargs xử lý tên tệp có chứa khoảng trắng


252
$ ls *mp3 | xargs mplayer  

Playing Lemon.  
File not found: 'Lemon'  
Playing Tree.mp3.  
File not found: 'Tree.mp3'  

Exiting... (End of file)  

Lệnh của tôi không thành công vì tệp "Lemon Tree.mp3" chứa khoảng trắng và vì vậy xargs nghĩ rằng đó là hai tệp. Tôi có thể làm cho find + xargs hoạt động với tên tệp như thế này không?


Thay vì ls |grep mp3 |sed -n "7p"bạn chỉ có thể sử dụng echo "Lemon Tree.mp3".
Micha Wiedenmann


Câu hỏi này cũng được trả lời bởi stackoverflow.com/a/33528111/94687
imz - Ivan Zakharyaschev

Câu trả lời:


256

Các xargslệnh có ký tự trắng không gian (các tab, không gian, dòng mới) như delimiters. Bạn chỉ có thể thu hẹp nó cho các ký tự dòng mới ('\ n') với -dtùy chọn như sau:

ls *.mp3 | xargs -d '\n' mplayer

Nó chỉ hoạt động với GNU xargs. Đối với các hệ thống BSD, sử dụng -0tùy chọn như thế này:

ls *.mp3 | xargs -0 mplayer

Phương pháp này đơn giản hơn và cũng hoạt động với GNU xargs.


6
Câu trả lời tốt nhất cho sử dụng chung! Điều này hoạt động ngay cả khi lệnh trước của bạn không "tìm thấy"
nexayq

28
Thật không may, tùy chọn này không khả dụng trên OS X.
Thomas Tempelmann

25
@Thomas Đối với OS X, cờ là -E, ví dụ:xargs -E '\n'

30
Trên OS X, -E '\ n' không có tác dụng đối với tôi, tôi cũng không mong đợi điều đó vì nó đã sửa đổi eofstr chứ không phải là dấu tách bản ghi. Tuy nhiên, tôi đã có thể sử dụng cờ -0 làm giải pháp, ngay cả khi lệnh trước đó không phải là 'find', bằng cách mô phỏng hiệu ứng của cờ find -print0 trong đầu vào của tôi, ví dụ: ls * mp3 | tr '\ n' '\ 0' | xargs -0 mplayer
biomiker

10
Đối với OS X, bạn có thể "BREW cài đặt findutils", mang đến cho bạn những "gxargs" lệnh đó không có công tắc -d.
Tom De Leu

213

Tiện ích xargs đọc các chuỗi phân tách không gian, tab, dòng mới và cuối tệp từ đầu vào tiêu chuẩn và thực thi tiện ích với các chuỗi làm đối số.

Bạn muốn tránh sử dụng không gian như một dấu phân cách. Điều này có thể được thực hiện bằng cách thay đổi dấu phân cách cho xargs. Theo hướng dẫn:

 -0      Change xargs to expect NUL (``\0'') characters as separators,
         instead of spaces and newlines.  This is expected to be used in
         concert with the -print0 function in find(1).

Nhu la:

 find . -name "*.mp3" -print0 | xargs -0 mplayer

Để trả lời câu hỏi về việc chơi mp3 thứ bảy; nó đơn giản hơn để chạy

 mplayer "$(ls *.mp3 | sed -n 7p)"

10
Đây là sử dụng GNU findvà GNU xargs; không phải tất cả các phiên bản của các chương trình đó đều hỗ trợ các tùy chọn đó (mặc dù có một trường hợp phải được thực hiện mà chúng nên).
Jonathan Leffler

1
@JonathanLeffler s / GNU / FreeBSD / g; Đáng buồn là POSIX sợ các ký tự NUL trong các tệp văn bản và chưa có đủ liệu pháp :-) Lời khuyên của tôi trong thực tế là sử dụng các tùy chọn không di động.
Jens

6
Và Mac OS X (một dẫn xuất BSD) có findvới -print0xargsvới -0. Tuy nhiên, AFAIK, HP-UX, AIX và Solaris không (nhưng tôi có thể sửa: HP-UX 11i không; Solaris 10 không; AIX 5.x không, nhưng chúng không phải là phiên bản hiện tại ). Chẳng hạn, sẽ khó thay đổi sedđể sử dụng kết thúc 'dòng' '\0'thay vì '\n'và POSIX 2008 getdelim()sẽ giúp bạn dễ dàng quản lý.
Jonathan Leffler

2
+1 + 1 mẹo sử dụng với đường dẫn tệp chứa tệp danh sách: cat $ file_paths_list_file | perl -ne 's | \ n | \ 000 | g; in' | xargs -0 zip $ zip_package
Yordan Georgiev

2
Ý tưởng tốt để thay thế các dòng mới bằng NUL - Tôi đã phải làm điều này trên một hệ thống nhúng không có GNU find hay GNU xargs hay perl - nhưng lệnh tr có thể được sử dụng để thực hiện tương tự: cat $ file_paths_list_file | tr '\ n' '\ 0' | xargs -0 du -hms
joensson


16

xargs trên MacOS không có tùy chọn -d, vì vậy giải pháp này sử dụng -0 thay thế.

Nhận ls để xuất một tệp trên mỗi dòng, sau đó dịch dòng mới thành null và báo cho xargs sử dụng null làm dấu phân cách:

ls -1 *mp3 | tr "\n" "\0" | xargs -0 mplayer


8
find . -name 'Lemon*.mp3' -print0 | xargs 0 -i mplayer '{}' 

Điều này đã giúp trong trường hợp của tôi để xóa các tập tin khác nhau với không gian. Nó cũng nên hoạt động với mplayer. Thủ thuật cần thiết là dấu ngoặc kép. (Đã thử nghiệm trên Linux Xubfox 14.04.)


7

Câu trả lời của Dick.Guertin [1] cho rằng người ta có thể thoát khỏi khoảng trắng trong tên tệp là một giải pháp thay thế có giá trị cho các giải pháp khác được đề xuất ở đây (chẳng hạn như sử dụng ký tự null làm dấu phân cách thay vì khoảng trắng). Nhưng nó có thể đơn giản hơn - bạn không thực sự cần một nhân vật độc đáo. Bạn chỉ có thể có sed thêm các không gian thoát trực tiếp:

ls | grep ' ' | sed 's| |\\ |g' | xargs ...

Hơn nữa, grep chỉ cần thiết nếu bạn chỉ muốn các tệp có khoảng trắng trong tên. Tổng quát hơn (ví dụ: khi xử lý một loạt tệp có một số không gian, một số không có), chỉ cần bỏ qua grep:

ls | sed 's| |\\ |g' | xargs ...

Sau đó, tất nhiên, tên tệp có thể có khoảng trắng khác ngoài khoảng trắng (ví dụ: tab):

ls | sed -r 's|[[:blank:]]|\\\1|g' | xargs ...

Giả sử bạn có một sed hỗ trợ -r (regex mở rộng) như GNU sed hoặc các phiên bản gần đây của bsd sed (ví dụ: FreeBSD ban đầu đánh vần tùy chọn "-E" trước FreeBSD 8 và hỗ trợ cả -r & -E để tương thích thông qua FreeBSD 11 ít nhất). Nếu không, bạn có thể sử dụng biểu thức khung lớp ký tự regex cơ bản và nhập thủ công các ký tự khoảng trắng và tab trong[] dấu phân cách.

[1] Điều này có lẽ phù hợp hơn khi bình luận hoặc chỉnh sửa câu trả lời đó, nhưng hiện tại tôi không có đủ danh tiếng để bình luận và chỉ có thể đề xuất các chỉnh sửa. Vì các hình thức sau ở trên (không có grep) làm thay đổi hành vi của câu trả lời ban đầu của Dick.Guertin, nên một chỉnh sửa trực tiếp có lẽ không phù hợp.


1
những kẻ điên unix chạy các tập lệnh đặt tên tệp mà không xem xét đầu ra của chúng, đó là người
rút ra lorien

4

ls | grep mp3 | sed -n "7p" | xargs -i mplayer {}

Lưu ý rằng trong lệnh trên, xargssẽ gọi mplayermột lần nữa cho mỗi tệp. Điều này có thể là không mong muốn mplayer, nhưng có thể ổn cho các mục tiêu khác.


1
Một bổ sung hữu ích cho các câu trả lời hiện có, nhưng đáng chú ý là điều này sẽ gây ra mplayerđược gọi là một lần nữa cho mỗi tệp. Nó quan trọng nếu bạn cố gắng, ví dụ ... | xargs -I{} mplayer -shuffle {}: điều này sẽ chơi theo một thứ tự hoàn toàn xác định, mặc dù -shuffle.

1
Nó có lẽ thường không phải là ý định. xargsPhần lớn được sử dụng với các lệnh chấp nhận danh sách tên tệp (ví dụ dễ rm:) và cố gắng truyền càng nhiều tên tệp càng tốt cho mỗi lệnh gọi, chỉ tách thành nhiều lệnh nếu cần. Bạn có thể thấy sự khác biệt khi bạn sử dụng một lệnh trong đó hiển thị từng lời gọi, chẳng hạn như echo(mặc định): seq 0 100000 | xargsin tất cả các số từ 0 đến 23695 (dành riêng cho nền tảng, nhưng đó là những gì xảy ra trên hệ thống của tôi) trên dòng đầu tiên, đến 45539 trên dòng 2, v.v. Và bạn đã đúng, đối với hầu hết các lệnh, nó sẽ không thành vấn đề.

4

Trên macOS 10.12.x (Sierra), nếu bạn có khoảng trắng trong tên tệp hoặc thư mục con, bạn có thể sử dụng như sau:

find . -name '*.swift' -exec echo '"{}"' \; |xargs wc -l

2

Nó phụ thuộc vào (a) mức độ gắn bó của bạn với số 7 trái ngược với, ví dụ như Lemons và (b) xem bất kỳ tên tệp nào của bạn có chứa dòng mới (và liệu bạn có sẵn sàng đổi tên chúng nếu chúng có) hay không.

Có nhiều cách để đối phó với nó, nhưng một số trong số đó là:

mplayer Lemon*.mp3

find . -name 'Lemon*.mp3' -exec mplayer {} ';'

i=0
for mp3 in *.mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

for mp3 in *.mp3
do
    case "$mp3" in
    (Lemon*) mplayer "$mp3";;
    esac
done

i=0
find . -name *.mp3 |
while read mp3
do
    i=$((i+1))
    [ $i = 7 ] && mplayer "$mp3"
done

Các readvòng lặp không hoạt động nếu tên tập tin chứa ký tự dòng mới; những cái khác hoạt động chính xác ngay cả với dòng mới trong tên (huống chi là khoảng trắng). Đối với tiền của tôi, nếu bạn có tên tệp chứa dòng mới, bạn nên đổi tên tệp mà không cần dòng mới. Sử dụng dấu ngoặc kép xung quanh tên tệp là chìa khóa để các vòng lặp hoạt động chính xác.

Nếu bạn có GNU findvà GNU xargs(hoặc FreeBSD (* BSD?) Hoặc Mac OS X), bạn cũng có thể sử dụng các tùy chọn -print0-0, như trong:

find . -name 'Lemon*.mp3' -print0 | xargs -0 mplayer

Điều này hoạt động bất kể nội dung của tên (hai ký tự duy nhất không thể xuất hiện trong tên tệp là dấu gạch chéo và NUL, và dấu gạch chéo không gây ra vấn đề gì trong đường dẫn tệp, vì vậy sử dụng NUL làm dấu phân cách tên bao trùm mọi thứ). Tuy nhiên, nếu bạn cần lọc ra 6 mục đầu tiên, bạn cần một chương trình xử lý 'dòng' kết thúc bằng NUL thay vì dòng mới ... và tôi không chắc có bất kỳ mục nào.

Đầu tiên là đơn giản nhất cho trường hợp cụ thể trên tay; tuy nhiên, nó có thể không khái quát để bao gồm các tình huống khác mà bạn chưa liệt kê.


2

Tôi biết rằng tôi sẽ không trả lời các xargscâu hỏi trực tiếp nhưng nó đáng nói find's -execlựa chọn.

Cho hệ thống tập tin sau:

[root@localhost bokeh]# tree --charset assci bands
bands
|-- Dream\ Theater
|-- King's\ X
|-- Megadeth
`-- Rush

0 directories, 4 files

Lệnh find có thể được thực hiện để xử lý không gian trong Dream Theater và King X. Vì vậy, để tìm các tay trống của mỗi ban nhạc bằng grep:

[root@localhost]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

Trong -exectùy chọn {}là viết tắt của tên tệp bao gồm đường dẫn. Lưu ý rằng bạn không phải thoát nó hoặc đặt nó trong dấu ngoặc kép.

Sự khác biệt giữa -execcác đầu cuối ( +\;) là +các nhóm có nhiều tên tệp có thể nằm trên một dòng lệnh. Trong khi đó \;sẽ thực thi lệnh cho mỗi tên tệp.

Vì vậy, find bands/ -type f -exec grep Drums {} +sẽ dẫn đến:

grep Drums "bands/Dream Theater" "bands/Rush" "bands/King's X" "bands/Megadeth"

find bands/ -type f -exec grep Drums {} \;sẽ dẫn đến:

grep Drums "bands/Dream Theater"
grep Drums "bands/Rush"
grep Drums "bands/King's X"
grep Drums "bands/Megadeth"

Trong trường hợp grepnày có tác dụng phụ là in tên tệp hay không.

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} \;
Drums:Mike Mangini
Drums: Neil Peart
Drums:Jerry Gaskill
Drums:Dirk Verbeuren

[root@localhost bokeh]# find bands/ -type f -exec grep Drums {} +
bands/Dream Theater:Drums:Mike Mangini
bands/Rush:Drums: Neil Peart
bands/King's X:Drums:Jerry Gaskill
bands/Megadeth:Drums:Dirk Verbeuren

Tất nhiên, grepcác tùy chọn -h-Hsẽ kiểm soát xem tên tệp có được in hay không bất kể grepđược gọi như thế nào .


xargs

xargs cũng có thể kiểm soát tập tin man trên dòng lệnh.

xargstheo nhóm mặc định tất cả các đối số trên một dòng. Để làm điều tương tự mà -exec \;sử dụng xargs -l. Lưu ý rằng -ttùy chọn yêu xargscầu in lệnh trước khi thực hiện.

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n' -l -t grep Drums
grep Drums ./bands/Dream Theater 
Drums:Mike Mangini
grep Drums ./bands/Rush 
Drums: Neil Peart
grep Drums ./bands/King's X 
Drums:Jerry Gaskill
grep Drums ./bands/Megadeth 
Drums:Dirk Verbeuren

Xem -ltùy chọn này cho xargs thực thi grep cho mỗi tên tệp.

So với mặc định (nghĩa là không có -ltùy chọn):

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush ./bands/King's X ./bands/Megadeth 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren

xargskiểm soát tốt hơn về số lượng tệp có thể nằm trên dòng lệnh. Cung cấp -ltùy chọn số lượng tệp tối đa cho mỗi lệnh.

[root@localhost bokeh]# find ./bands -type f  | xargs -d '\n'  -l2 -t grep Drums
grep Drums ./bands/Dream Theater ./bands/Rush 
./bands/Dream Theater:Drums:Mike Mangini
./bands/Rush:Drums: Neil Peart
grep Drums ./bands/King's X ./bands/Megadeth 
./bands/King's X:Drums:Jerry Gaskill
./bands/Megadeth:Drums:Dirk Verbeuren
[root@localhost bokeh]# 

Xem đó grepđã được thực hiện với hai tên tập tin vì -l2.


1

Với tiêu đề cụ thể của bài đăng này, đây là gợi ý của tôi:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g'

Ý tưởng là chuyển đổi khoảng trống thành bất kỳ ký tự duy nhất nào, như '<', và sau đó thay đổi nó thành '\', dấu gạch chéo ngược theo sau là khoảng trống. Sau đó, bạn có thể chuyển lệnh đó thành bất kỳ lệnh nào bạn thích, chẳng hạn như:

ls | grep ' ' | tr ' ' '<' | sed 's|<|\\ |g' | xargs -L1 GetFileInfo

Chìa khóa ở đây nằm trong các lệnh 'tr' và 'sed'; và bạn có thể sử dụng bất kỳ ký tự nào ngoài '<', chẳng hạn như '?' hoặc thậm chí là một ký tự tab.


Mục đích của đường vòng qua là trgì? Tại sao không chỉ ls *.mp3 | sed -n '7!b;s/\([[:space:]]\)/\\\1/g;p'?
tripleee

1
Tôi đã thấy rằng "tr '' '?'" Loại bỏ sự cần thiết của "sed". Đơn "?" ký tự không trống, nhưng khớp với BẤT K character ký tự đơn nào, trong trường hợp này: trống. Tỷ lệ của nó là một cái gì đó khá nhỏ và có thể chấp nhận được vì bạn đang cố xử lý TẤT CẢ các tệp kết thúc bằng .mp3: "ls | grep '' | tr '' '?' | xargs -L1 GetFileInfo "
Dick Guertin

Bạn cũng có thể xử lý "tab" cùng một lúc: tr '\ t' '??' xử lý cả hai.
Dick Guertin

1

Các giải pháp thay thế có thể hữu ích ...

Bạn cũng có thể thêm một ký tự null vào cuối dòng bằng Perl, sau đó sử dụng -0tùy chọn trong xargs. Không giống như xargs -d '\ n' (trong câu trả lời được phê duyệt) - điều này hoạt động ở mọi nơi, bao gồm cả OS X.

Ví dụ: để liệt kê đệ quy (thực thi, di chuyển, v.v.) các tệp MPEG3 có thể chứa dấu cách hoặc các ký tự vui nhộn khác - Tôi sẽ sử dụng:

find . | grep \.mp3 | perl -ne 'chop; print "$_\0"' | xargs -0  ls

(Lưu ý: Để lọc, tôi thích cú pháp "| grep" dễ nhớ hơn đối với "đối số" - tên đối số.)

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.