ทำให้ xargs ดำเนินการคำสั่งหนึ่งครั้งสำหรับแต่ละบรรทัดอินพุต


341

ฉันจะทำให้ xargs เรียกใช้งานคำสั่งได้อย่างแม่นยำเพียงครั้งเดียวสำหรับแต่ละอินพุตที่ได้รับอย่างไร มันเป็นพฤติกรรมเริ่มต้นคือการ chunk บรรทัดและรันคำสั่งหนึ่งครั้งผ่านหลายบรรทัดไปยังแต่ละอินสแตนซ์

จากhttp://en.wikipedia.org/wiki/Xargs :

find / path -type f -print0 | xargs -0 rm

ในตัวอย่างนี้ค้นหาฟีดอินพุตของ xargs ด้วยรายการชื่อไฟล์แบบยาว xargs จากนั้นแยกรายการนี้เป็นรายการย่อยและเรียก rm หนึ่งครั้งสำหรับทุกรายการย่อย สิ่งนี้มีประสิทธิภาพมากกว่าเวอร์ชั่นที่เทียบเท่ากับการใช้งานจริง:

ค้นหา / เส้นทาง -type f -exec rm '{}' \;

ฉันรู้ว่าการค้นหามีธง "exec" ฉันแค่ยกตัวอย่างตัวอย่างจากแหล่งข้อมูลอื่น


4
ในตัวอย่างที่คุณให้find /path -type f -deleteจะยิ่งมีประสิทธิภาพมากขึ้น :)
tzot

พยายามอย่าใช้ xargs ...
Naib

6
โอปฉันรู้ว่าคำถามนี้เก่ามาก แต่ก็ยังเกิดขึ้นใน Google และ IMHO คำตอบที่ยอมรับนั้นผิด ดูคำตอบอีกต่อไปของฉันด้านล่าง
เบีย

โปรดลองเปลี่ยนคำตอบรับเป็นคำตอบของ @ Tobia ซึ่งดีกว่ามาก คำตอบที่ยอมรับไม่ได้จัดการช่องว่างในชื่อและไม่อนุญาตให้มีการขัดแย้งหลายคำสั่ง xargs ซึ่งเป็นหนึ่งในคุณสมบัติหลักของ xargs
สีเทา

คำตอบ:


394

ต่อไปนี้จะใช้งานได้ต่อเมื่อคุณไม่มีช่องว่างในอินพุต

xargs -L 1
xargs --max-lines=1 # synonym for the -L option

จากหน้าคน:

-L max-lines
          Use at most max-lines nonblank input lines per command line.
          Trailing blanks cause an input line to be logically continued  on
          the next input line.  Implies -x.

13
สำหรับฉันมันสามารถออกมาได้เหมือนxargs -n 1ที่คุณให้ไว้ "รายการอาร์กิวเมนต์ยาวเกินไป"
Wernight

19
หากMAX-LINESไม่ระบุค่าเริ่มต้นให้เป็น 1 จึงxargs -lเพียงพอ info xargsดู
ธ อร์

3
@Wernight: "-n1" ไม่ได้ให้ 1 การเรียกใช้ต่อบรรทัดอินพุต บางทีบรรทัดอินพุตของคุณยาวเกินไป echo "foo bar" | xargs -n1 echoสาธิต: ดังนั้นหากคุณใส่ในสิ่งที่ต้องการ 'ls' มันจะไม่จัดการช่องว่างได้ดี
gatoatigrado

8
นี่เป็นสิ่งที่ผิด -L 1ไม่ตอบคำถามดั้งเดิมและ-n 1ทำเช่นนั้นในการตีความอย่างใดอย่างหนึ่งเท่านั้น ดูคำตอบยาว ๆ ของฉันด้านล่าง
เบีย

2
@ โตเบีย: มันตอบคำถามเดิมซึ่งค่อนข้างเฉพาะเจาะจงเกี่ยวกับบรรทัดของอินพุต นั่นคือสิ่งที่-L 1ทำ สำหรับฉันแล้ว OP ดูเหมือนจะพยายามหลีกเลี่ยงพฤติกรรมการเริ่มต้นที่ชัดเจนและเนื่องจากเป็นที่ยอมรับฉันคิดว่าฉันพูดถูก คำตอบของคุณจะระบุถึงกรณีการใช้งานที่แตกต่างกันเล็กน้อยซึ่งคุณต้องการให้มีลักษณะการทำงานแบบ chunking เช่นกัน
Draemon

207

ดูเหมือนว่าฉันมีคำตอบที่มีอยู่ทั้งหมดในหน้านี้ผิดรวมถึงคำตอบที่ถูกต้อง นั่นเกิดจากความจริงที่ว่าคำถามนั้นเป็นคำที่คลุมเครือ

สรุป:   หากคุณต้องการรันคำสั่ง"หนึ่งครั้งสำหรับแต่ละบรรทัดอินพุตที่กำหนด"ผ่านทั้งบรรทัด (โดยไม่ต้องขึ้นบรรทัดใหม่) ให้กับคำสั่งเป็นอาร์กิวเมนต์เดียวดังนั้นนี่เป็นวิธีที่ดีที่สุดในการเข้ากันได้กับ UNIX:

... | tr '\n' '\0' | xargs -0 -n1 ...

GNU xargsอาจมีหรือไม่มีส่วนขยายที่มีประโยชน์ซึ่งอนุญาตให้คุณทำไปได้trแต่ไม่มีใน OS X และระบบ UNIX อื่น ๆ

ตอนนี้สำหรับคำอธิบายที่ยาว ...


มีสองประเด็นที่ต้องคำนึงถึงเมื่อใช้ xargs:

  1. มันแยกอินพุตเป็น "อาร์กิวเมนต์" ได้อย่างไร; และ
  2. จำนวนอาร์กิวเมนต์ที่ต้องส่งคำสั่งย่อยในแต่ละครั้ง

ในการทดสอบพฤติกรรมของ xargs เราต้องการยูทิลิตี้ที่แสดงจำนวนครั้งที่มันถูกเรียกใช้งานและด้วยจำนวนอาร์กิวเมนต์ ฉันไม่รู้ว่ามียูทิลิตี้มาตรฐานในการทำเช่นนั้นหรือไม่ แต่เราสามารถเขียนโค้ดได้อย่างง่ายดายในการทุบตี:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

สมมติว่าคุณบันทึกไว้showในไดเรกทอรีปัจจุบันของคุณและทำให้มันทำงานได้นี่คือวิธีการทำงาน:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

ตอนนี้ถ้าคำถามเดิมเป็นจริงเกี่ยวกับประเด็นที่ 2 ด้านบน (อย่างที่ฉันคิดว่าเป็นหลังจากอ่านไปสองสามครั้ง) และจะต้องอ่านดังนี้ (เปลี่ยนเป็นตัวหนา):

ฉันจะทำให้ xargs เรียกใช้งานคำสั่งเพียงครั้งเดียวสำหรับแต่ละอาร์กิวเมนต์ของอินพุตที่กำหนดได้อย่างไร พฤติกรรมเริ่มต้นของมันคือการป้อนข้อมูลเข้าไปในข้อโต้แย้งและดำเนินการคำสั่งให้น้อยที่สุดเท่าที่เป็นไปได้ผ่านหลายอาร์กิวเมนต์ไปยังแต่ละอินสแตนซ์

-n 1แล้วคำตอบคือ

ลองเปรียบเทียบพฤติกรรมเริ่มต้นของ xargs ซึ่งแยกอินพุตรอบช่องว่างและเรียกคำสั่งสองสามครั้งเท่าที่จะทำได้

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

และพฤติกรรมกับ-n 1:

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

หากในทางกลับกันคำถามเดิมเป็นเรื่องเกี่ยวกับประเด็นที่ 1 การแยกอินพุตและต้องอ่านแบบนี้ (หลายคนที่มาที่นี่ดูเหมือนจะคิดว่าเป็นกรณีหรือสับสนทั้งสองประเด็น):

ฉันจะทำให้ xargs รันคำสั่งกับว่าอาร์กิวเมนต์หนึ่งสำหรับแต่ละบรรทัดของการป้อนข้อมูลให้? พฤติกรรมเริ่มต้นของมันคือการก้อนเส้นรอบช่องว่าง

ดังนั้นคำตอบนั้นลึกซึ้งยิ่งขึ้น

ใคร ๆ ก็คิดว่า-L 1อาจเป็นประโยชน์ได้ แต่กลับกลายเป็นว่าไม่ได้เปลี่ยนการแยกวิเคราะห์ มันจะรันคำสั่งเพียงครั้งเดียวสำหรับแต่ละบรรทัดอินพุตโดยมีอาร์กิวเมนต์มากเท่าที่มีบนบรรทัดอินพุตนั้น:

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

ไม่เพียงแค่นั้น แต่หากบรรทัดลงท้ายด้วยช่องว่างมันจะผนวกเข้ากับถัดไป:

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

เห็นได้ชัดว่า-Lไม่เกี่ยวกับการเปลี่ยนวิธีที่ xargs แยกอินพุตเป็นอาร์กิวเมนต์

อาร์กิวเมนต์เดียวที่ทำในรูปแบบข้ามแพลตฟอร์ม (ยกเว้นส่วนขยาย GNU) คือ-0ซึ่งแยกอินพุตรอบ ๆ NUL ไบต์

จากนั้นเป็นเพียงการแปลบรรทัดใหม่เป็น NUL ด้วยความช่วยเหลือของtr:

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

ตอนนี้การแยกวิเคราะห์อาร์กิวเมนต์ดูทั้งหมดรวมทั้งช่องว่างต่อท้าย

สุดท้ายถ้าคุณรวมเทคนิคนี้เข้าด้วย-n 1กันคุณจะได้รับคำสั่งหนึ่งคำสั่งต่อหนึ่งบรรทัดอินพุตไม่ว่าคุณจะใส่อะไรก็ตามซึ่งอาจเป็นอีกวิธีหนึ่งในการดูคำถามเดิม (อาจเป็นคำถามที่เข้าใจง่ายที่สุด)

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

ดูเหมือนว่านี่เป็นคำตอบที่ดีกว่า อย่างไรก็ตามฉันยังไม่เข้าใจว่าอะไรคือความแตกต่างระหว่าง -L และ -n ... คุณช่วยอธิบายอีกเล็กน้อยได้ไหม
olala

5
@olala -Lเรียกใช้งานคำสั่งหนึ่งครั้งต่อบรรทัดอินพุต (แต่ช่องว่างที่ท้ายบรรทัดจะรวมเข้ากับบรรทัดถัดไปและบรรทัดยังคงแบ่งเป็นอาร์กิวเมนต์ตามช่องว่าง) ในขณะที่-nเรียกใช้งานคำสั่งหนึ่งครั้งต่ออาร์กิวเมนต์ที่ป้อนเข้า หากคุณนับจำนวน->ในตัวอย่างผลลัพธ์ผลลัพธ์เหล่านั้นคือจำนวนครั้งที่สคริปต์./showถูกเรียกใช้งาน
เบีย

ฉันเห็น! ไม่ทราบว่ามีช่องว่างในตอนท้ายของบรรทัดเชื่อมต่อกับบรรทัดถัดไป ขอบคุณ!
olala

4
GNU xargsอาจมีหรือไม่มีส่วนขยายที่เป็นประโยชน์ที่อนุญาตให้คุณทำไปtrได้ส่วนขยายนั้นมีประโยชน์อย่างมาก จากxargs --help- -d, --delimiter = รายการ CHARACTER ในอินพุตสตรีมจะถูกคั่นด้วย CHARACTER ไม่ใช่ whitespace; ปิดใช้งานการประมวลผลคำพูดและแบ็กสแลชและการประมวลผล EOF แบบลอจิคัล
Piotr Dobrogost

-Lคำตอบนี้ดูเหมือนสับสนเกี่ยวกับ -Lไม่ได้บอกจำนวนครั้งในการเรียกใช้งานสคริปต์ต่อบรรทัด แต่จะบอกว่ามีการป้อนข้อมูลจำนวนเท่าใดในแต่ละครั้ง
Moberg

22

หากคุณต้องการรันคำสั่งสำหรับทุกบรรทัด (เช่นผลลัพธ์) ที่มาจากfindนั้นคุณต้องการxargsอะไร

ลอง:

find เส้นทาง -type f -exec คำสั่งของคุณ {} \;

เมื่อตัวอักษร{}ได้รับการแทนที่ด้วยชื่อไฟล์และ\;จำเป็นต้องfindรู้ว่าคำสั่งที่กำหนดเองสิ้นสุดลงที่นั่น

แก้ไข:

(หลังจากการแก้ไขคำถามของคุณชี้แจงว่าคุณรู้-exec)

จากman xargs:

-L max-lines
ใช้ที่max-linesมากที่สุดบรรทัดอินพุตที่ไม่ว่างต่อบรรทัดคำสั่ง ช่องว่างต่อท้ายทำให้บรรทัดอินพุตถูกดำเนินการทางตรรกะต่อไปในบรรทัดอินพุตถัดไป นัย -x

โปรดทราบว่าชื่อไฟล์ที่ลงท้ายด้วยช่องว่างจะทำให้คุณมีปัญหาหากคุณใช้xargs:

$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\  b c\  c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory

ดังนั้นถ้าคุณไม่สนใจ-execตัวเลือกคุณควรใช้-print0และ-0:

$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a

18

ฉันจะทำให้ xargs เรียกใช้งานคำสั่งได้อย่างแม่นยำเพียงครั้งเดียวสำหรับแต่ละอินพุตที่ได้รับอย่างไร

-L 1เป็นวิธีการแก้ปัญหาที่ง่าย แต่ไม่สามารถใช้งานได้หากไฟล์ใด ๆ มีช่องว่างอยู่ นี่เป็นหน้าที่หลักของ-print0อาร์กิวเมนต์ของ find - เพื่อแยกอาร์กิวเมนต์ด้วยอักขระ '\ 0' แทนที่จะเป็นช่องว่าง นี่คือตัวอย่าง:

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory

ทางออกที่ดีกว่าคือใช้trในการแปลงบรรทัดใหม่เป็น\0อักขระnull ( ) จากนั้นใช้xargs -0อาร์กิวเมนต์ นี่คือตัวอย่าง:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

หากคุณต้องการ จำกัด จำนวนการโทรคุณสามารถใช้-n 1อาร์กิวเมนต์เพื่อโทรหนึ่งครั้งไปที่โปรแกรมสำหรับแต่ละอินพุต:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

สิ่งนี้ยังช่วยให้คุณกรองผลลัพธ์การค้นหาก่อนที่จะแปลงตัวแบ่งเป็นโมฆะ

find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

1
มีข้อผิดพลาดทางไวยากรณ์ในรหัสบล็อกที่สอง tr '\ n' '\ 0 \ => tr' \ n '' \ 0 'ฉันพยายามแก้ไขปัญหานี้ แต่ "การแก้ไขต้องมีอย่างน้อย 6 ตัวอักษร" (ดูเหมือนจะเป็น โง่เป็นคอมไพล์ปฏิเสธที่จะกระทำเพราะการเปลี่ยนแปลงของฉันน้อยกว่า 6 ตัวอักษร)
htaccess

1
สิ่งนี้หมายความว่า: "ปัญหาอีกประการของการใช้-Lคือมันไม่อนุญาตให้มีหลายอาร์กิวเมนต์สำหรับการxargsเรียกใช้คำสั่งแต่ละครั้ง"
Moberg

ฉันได้ปรับปรุงคำตอบเพื่อลบข้อมูลที่ไม่เกี่ยวข้อง @Moberg
สีเทา


9

ทั้งสองวิธีใช้งานได้และจะทำงานกับคำสั่งอื่น ๆ ที่ไม่ได้ใช้ find!

xargs -I '{}' rm '{}'
xargs -i rm '{}'

กรณีการใช้งานตัวอย่าง:

find . -name "*.pyc" | xargs -i rm '{}'

จะลบไฟล์ pyc ทั้งหมดภายใต้ไดเรกทอรีนี้แม้ว่าไฟล์ pyc จะมีช่องว่าง


ปัญหานี้เรียกการเรียกใช้อรรถประโยชน์หนึ่งรายการสำหรับองค์ประกอบที่ไม่เหมาะสม
สีเทา

7
find path -type f | xargs -L1 command 

คือทั้งหมดที่คุณต้องการ


4

คำสั่งต่อไปนี้จะค้นหาไฟล์ทั้งหมด (-type f) ใน/pathและคัดลอกไฟล์เหล่านั้นcpไปยังโฟลเดอร์ปัจจุบัน จดบันทึกการใช้งานหาก-I %ต้องการระบุตัวยึดตำแหน่งในcpบรรทัดคำสั่งเพื่อให้สามารถวางอาร์กิวเมนต์หลังชื่อไฟล์

find /path -type f -print0 | xargs -0 -I % cp % .

ทดสอบด้วย xargs (GNU findutils) 4.4.0


2

คุณสามารถ จำกัด จำนวนบรรทัดหรืออาร์กิวเมนต์ (หากมีช่องว่างระหว่างแต่ละอาร์กิวเมนต์) โดยใช้แฟล็ก --max-lines หรือ --max-args ตามลำดับ

  -L max-lines
         Use at most max-lines nonblank input lines per command line.  Trailing blanks cause an input line to be logically continued on the next  input
         line.  Implies -x.

  --max-lines[=max-lines], -l[max-lines]
         Synonym  for  the -L option.  Unlike -L, the max-lines argument is optional.  If max-args is not specified, it defaults to one.  The -l option
         is deprecated since the POSIX standard specifies -L instead.

  --max-args=max-args, -n max-args
         Use at most max-args arguments per command line.  Fewer than max-args arguments will be used if the size (see  the  -s  option)  is  exceeded,
         unless the -x option is given, in which case xargs will exit.

0

ดูเหมือนว่าฉันไม่มีชื่อเสียงเพียงพอที่จะเพิ่มความคิดเห็นในคำตอบของ Tobia ด้านบนดังนั้นฉันจึงเพิ่ม "คำตอบ" นี้เพื่อช่วยพวกเราที่ต้องการทดลองด้วยxargsวิธีเดียวกันบนแพลตฟอร์ม Windows

นี่คือไฟล์แบตช์ของ windows ที่ทำสิ่งเดียวกันกับสคริปต์ "แสดง" ของ Tobia ที่เขียนโค้ดอย่างรวดเร็ว

@echo off
REM
REM  cool trick of using "set" to echo without new line
REM  (from:  http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
    exit /b
)

<nul set /p=Args:  "%~1"
shift

:start
if not "%~1" == "" (
    <nul set /p=, "%~1"
    shift
    goto start
)
echo.

0

@Draemon คำตอบน่าจะเหมาะสมกับ "-0" ถึงแม้จะมีช่องว่างในไฟล์

ฉันลองใช้คำสั่ง xargs และพบว่า "-0" ทำงานได้อย่างสมบูรณ์กับ "-L" แม้ช่องว่างจะถือว่า ต่อไปนี้เป็นตัวอย่าง:

#touch "file with space"
#touch "file1"
#touch "file2"

ต่อไปนี้จะแบ่ง nulls และดำเนินการคำสั่งในแต่ละอาร์กิวเมนต์ในรายการ:

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

ดังนั้น-L1จะดำเนินการโต้แย้งกับแต่ละตัวละครยกเลิก null ถ้าใช้กับ "-0" หากต้องการดูความแตกต่างลอง:

 #find . -name 'file*' -print0 | xargs -0 | xargs -L1
 ./file with space ./file1 ./file2

แม้จะดำเนินการครั้งเดียว:

 #find . -name 'file*' -print0  | xargs -0  | xargs -0 -L1
./file with space ./file1 ./file2

คำสั่งจะดำเนินการเพียงครั้งเดียวเนื่องจาก "-L" ตอนนี้จะไม่แยกไบต์ว่าง คุณต้องระบุทั้ง "-0" และ "-L" เพื่อให้ทำงานได้


-3

ในตัวอย่างของคุณจุดไพพ์ของเอาต์พุตของ find to xargs คือลักษณะการทำงานมาตรฐานของตัวเลือก -exec ของ find คือดำเนินการคำสั่งหนึ่งครั้งสำหรับแต่ละไฟล์ที่พบ หากคุณใช้ find และคุณต้องการพฤติกรรมมาตรฐานคำตอบนั้นง่าย - อย่าใช้ xargs เพื่อเริ่มต้นด้วย


ที่จริงแล้วสิ่งที่ฉันสามารถบอกเป็นนัยได้จากการแก้ไขของ OP คือข้อมูลอินพุตไม่มีส่วนเกี่ยวข้องfindและนั่นคือสาเหตุที่พวกเขาไม่ชอบ-execตัวเลือก
tzot

-3

เรียกใช้งาน ant clean-all บนทุก build.xml ในปัจจุบันหรือโฟลเดอร์ย่อย

find . -name 'build.xml' -exec ant -f {} clean-all \;

ไม่ใช่ทุกคนที่antติดตั้ง
สีเทา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.