ใช้เครื่องหมายอัฒภาค (;) vs plus (+) กับ exec ในการค้นหา


159

เหตุใดจึงมีความแตกต่างของเอาต์พุตระหว่างการใช้งาน

find . -exec ls '{}' \+

และ

find . -exec ls '{}' \;

ฉันได้:

$ find . -exec ls  \{\} \+
./file1  ./file2

.:
file1  file2  testdir1

./testdir1:
testdir2

./testdir1/testdir2:


$ find . -exec ls  \{\} \;
file1  file2  testdir1
testdir2
./file2
./file1

คำตอบ:


249

นี่อาจเป็นตัวอย่างที่ดีที่สุด สมมติว่าfindเปิดไฟล์เหล่านี้:

file1
file2
file3

การใช้-execกับเครื่องหมายอัฒภาค ( find . -exec ls '{}' \;) จะทำงาน

ls file1
ls file2
ls file3

แต่ถ้าคุณใช้เครื่องหมายบวกแทน ( find . -exec ls '{}' \+) ชื่อไฟล์ให้ได้มากที่สุดจะถูกส่งผ่านเป็นอาร์กิวเมนต์ไปที่คำสั่งเดียว:

ls file1 file2 file3

จำนวนชื่อไฟล์ถูก จำกัด โดยความยาวบรรทัดคำสั่งสูงสุดของระบบเท่านั้น หากคำสั่งเกินความยาวนี้คำสั่งจะถูกเรียกหลายครั้ง


1
ขอบคุณ สิ่งนี้มีประโยชน์มากสำหรับการต้องการเรียงลำดับไฟล์ผลลัพธ์: find -maxdepth 1 -type f -mtime -1 -exec ls -ltr {} \ +
fbas

1
ใบ้คำถาม: ฉันสังเกตเห็นว่า+เกี่ยวข้องกับการ-execหลบหนีเสมอ แต่ที่+เกี่ยวข้องกับ-mtimeไม่ได้ คุณรู้เหตุผลหรือไม่ ผมคิดว่ามันเป็นนิสัยจากการหลบหนีที่เกี่ยวข้องกับ; -exec
kevinarpe

3
@kevinarpe ;แน่นอนฉันจะชอล์กมันขึ้นอยู่กับนิสัยจาก นึกไม่ออกว่าจะต้องหนีไปไหน+
มาร์ติน

36

คำตอบทั้งหมดถูกต้องแล้ว ฉันเสนอสิ่งนี้เป็นการสาธิตที่ชัดเจนยิ่งขึ้น (สำหรับฉัน) ของพฤติกรรมที่อธิบายโดยใช้echoมากกว่าls:

ด้วยเครื่องหมายอัฒภาคคำสั่งechoถูกเรียกหนึ่งครั้งต่อไฟล์ (หรือวัตถุระบบไฟล์อื่น ๆ ) ที่พบ:

$ find . -name 'test*' -exec echo {} \;
./test.c
./test.cpp
./test.new
./test.php
./test.py
./test.sh

ด้วยเครื่องหมายบวกคำสั่งechoจะถูกเรียกเพียงครั้งเดียว ทุกไฟล์ที่พบจะถูกส่งเป็นอาร์กิวเมนต์

$ find . -name 'test*' -exec echo {} \+
./test.c ./test.cpp ./test.new ./test.php ./test.py ./test.sh

หากfindพบผลลัพธ์จำนวนมากคุณอาจพบว่าคำสั่งที่เรียกว่าฉายาบนจำนวนอาร์กิวเมนต์


1
ไม่ควรค้นหาเพิ่มผลลัพธ์เฉพาะหมายเลขที่ปลอดภัยส่งผ่านไปยังเชลล์หรือไม่ อย่างน้อยก็คือสิ่งที่ xargsทำ ... โดยหลักการแล้วมันไม่เคยสำลักการมีปากเสียงมากเกินไป
Rmano

1
@Rmano: ฉันเคยเห็นfind(และxargs) บน Solaris ปล่อยข้อโต้แย้งมากกว่าที่จะบริโภค xargs(และfind) ในfindutils` GNU ของดูเหมือนจะทำงานมากขึ้นอย่างสมเหตุสมผล แต่ทุกคนไม่ใช้ GNU
Johnsyweb

2
@Johnsyweb POSIX ทั้งหมดfindจะพยายามหลีกเลี่ยงข้อ จำกัด เกี่ยวกับจำนวนอาร์กิวเมนต์ และนั่นรวมถึง Solaris '(อย่างน้อย 10) ที่มันอาจล้มเหลวคือถ้าคุณทำสิ่งที่ชอบfind ... -exec ksh -c 'cmd "$@" "$@"' sh {} +หรือfind ... -exec ksh -c 'files="$*" cmd "$@"' sh {} +แต่findไม่สามารถจริงๆจะถูกตำหนิว่า โปรดทราบว่า GNU findเป็นหนึ่งในfindการนำไปใช้งานล่าสุดเพื่อรองรับ+(เคยเป็นความเจ็บปวดของสคริปต์พอร์ตไปยังระบบ GNU)
Stephane Chazelas

19

จากman find:

คำสั่ง -exec

ดำเนินการคำสั่ง; เป็นจริงถ้าส่งคืนสถานะ 0 อาร์กิวเมนต์ต่อไปนี้เพื่อค้นหาทั้งหมดจะถูกนำไปเป็นอาร์กิวเมนต์ของคำสั่งจนกว่าอาร์กิวเมนต์ประกอบด้วย ';' พบ สตริง '{}' ถูกแทนที่ด้วยชื่อไฟล์ปัจจุบันที่ถูกประมวลผลทุกที่ที่เกิดขึ้นในอาร์กิวเมนต์ของคำสั่งไม่ใช่เฉพาะในอาร์กิวเมนต์ที่อยู่คนเดียวเช่นเดียวกับในบางรุ่นของการค้นหา ทั้งสองสิ่งก่อสร้างเหล่านี้อาจจำเป็นต้องหลบหนี (ด้วย '\') หรือยกมาเพื่อป้องกันพวกเขาจากการขยายตัวโดยเปลือก ดูส่วนตัวอย่างวินาทีสำหรับตัวอย่างของการใช้ตัวเลือก '-exec' คำสั่งที่ระบุจะรันหนึ่งครั้งสำหรับแต่ละไฟล์ที่ตรงกัน คำสั่งจะถูกดำเนินการในไดเรกทอรีเริ่มต้น มีปัญหาด้านความปลอดภัยที่หลีกเลี่ยงไม่ได้รอบการใช้ตัวเลือก -exec

คำสั่ง -exec {} +

แตกต่างจาก -exec ตัวเลือกนี้จะทำงานคำสั่งที่ระบุไว้บนไฟล์ที่เลือก แต่บรรทัดคำสั่งที่ถูกสร้างขึ้นโดยต่อท้ายชื่อไฟล์แต่ละเลือกที่สิ้นสุด ; จำนวนการร้องขอคำสั่งทั้งหมดจะน้อยกว่าจำนวนไฟล์ที่ตรงกัน บรรทัดคำสั่งสร้างขึ้นในลักษณะเดียวกับที่ xargs สร้างบรรทัดคำสั่ง อนุญาตเพียงหนึ่งอินสแตนซ์ของ '{}' ภายในคำสั่ง คำสั่งจะถูกดำเนินการในไดเรกทอรีเริ่มต้น

ดังนั้นวิธีที่ฉันเข้าใจจะ\;ดำเนินการคำสั่งแยกต่างหากสำหรับแต่ละไฟล์ที่พบโดยfindในขณะที่\+ผนวกไฟล์และดำเนินการคำสั่งเดียวในทุกไฟล์ The \เป็นตัวละครหนีดังนั้นมัน:

ls testdir1; ls testdir2 

VS

ls testdir1 testdir2

การทำข้างบนในเปลือกของฉันทำมิเรอร์ผลลัพธ์ในคำถามของคุณ

ตัวอย่างของเวลาที่คุณต้องการใช้ \+

สมมติว่าสองไฟล์1.tmpและ2.tmp:

1.tmp:

1
2
3

2.tmp:

0
2
3

ด้วย\;:

 find *.tmp -exec diff {} \;
> diff: missing operand after `1.tmp'
> diff: Try `diff --help' for more information.
> diff: missing operand after `2.tmp'
> diff: Try `diff --help' for more information.

โดยที่ถ้าคุณใช้\+(เพื่อต่อผลลัพธ์ของfind):

find *.tmp -exec diff {} \+
1c1,3
< 1
---
> 0
> 2
> 30

ดังนั้นในกรณีนี้มันคือความแตกต่างระหว่างdiff 1.tmp; diff 2.tmpและdiff 1.tmp 2.tmp

มีหลายกรณีที่\;เหมาะสมและ\+จำเป็น ใช้\+กับrmเป็นหนึ่งในตัวอย่างดังกล่าวซึ่งถ้าคุณกำลังลบเป็นจำนวนมากของการทำงานของไฟล์ (ความเร็ว) \;จะดีกว่า


ฉันสามารถอ่าน man page ได้เช่นกัน และฉันก็ทำ แต่ฉันไม่คิดว่าฉันเข้าใจความแตกต่างระหว่างการใช้ vs +
Ankur Agarwal

ฉันไม่คิดว่า -1 นั้นยุติธรรมฉันอธิบายการทำความเข้าใจชายคนนั้น ฉันไม่ได้แค่ลอกเลียนแบบชายคนนั้นแล้วออกไป แต่ฉันได้แก้ไขคำตอบของฉันเพื่อรวมตัวอย่างที่ดีกว่า
matchew

10

findมีไวยากรณ์พิเศษ คุณใช้{}ตามที่เป็นเพราะพวกเขามีความหมายที่จะหาเป็นชื่อพา ธ ของไฟล์ที่พบและเปลือกหอย (ส่วนใหญ่) ไม่ได้ตีความพวกเขาเป็นอย่างอื่น คุณต้องการแบ็กสแลช\;เนื่องจากเซมิโคลอนมีความหมายกับเชลล์ซึ่งกินก่อนfindจึงจะสามารถรับได้ ดังนั้นสิ่งที่findต้องการเห็นหลังจากเปลือกเสร็จแล้วในรายการอาร์กิวเมนต์ที่ส่งผ่านไปยังโปรแกรม C คือ

"-exec", "rm", "{}", ";"

แต่คุณต้องการ \;ในบรรทัดคำสั่งเพื่อรับเซมิโคลอนผ่านเชลล์ไปยังอาร์กิวเมนต์

คุณสามารถหนีไปได้\{\}เพราะการตีความคำพูดของเชลล์\{\}เป็นเพียงแค่{}เป็นเพียงในทำนองเดียวกันคุณสามารถใช้ '{}'

สิ่งที่คุณไม่สามารถทำได้คือใช้

 -exec 'rm {} ;'

เพราะเชลล์ตีความว่าเป็นหนึ่งอาร์กิวเมนต์

"-exec", "rm {};"

และrm {} ;ไม่ใช่ชื่อของคำสั่ง (อย่างน้อยนอกเสียจากว่ามีใครบางคนกำลังดิ้นรนอยู่)

ปรับปรุง

ความแตกต่างคือระหว่าง

$ ls file1
$ ls file2

และ

$ ls file1 file2

+เป็น catenating ชื่อลงบนบรรทัดคำสั่ง


1
ฉันเข้าใจสิ่งที่คุณพูด ฉันถามความแตกต่างระหว่างการใช้คืออะไร; vs +
Ankur Agarwal

1
ขออภัยคุณอ่านคำถามหรือความคิดเห็นของฉันอย่างถี่ถ้วนหรือไม่? ฉันอาจจะต้องใช้ถ้อยคำใหม่ เหตุใดจึงมี o / p แตกต่างกันเมื่อฉันใช้เครื่องหมายอัฒภาคกับ exec ในการค้นหาและเมื่อฉันใช้บวกกับ exec ในการค้นหา?
Ankur Agarwal

2
นี่เป็นคำอธิบายที่ยอดเยี่ยมว่าทำไมคำสั่งจึงเป็นเช่นนี้คำตอบที่ยอมรับไม่ครอบคลุม ขอบคุณ!
Sherwin Yu

1

ความแตกต่างระหว่าง;(เซมิโคลอน) หรือ+(เครื่องหมายบวก) คือวิธีที่อาร์กิวเมนต์ส่งผ่านไปยัง find's -exec/ -execdirพารามิเตอร์ ตัวอย่างเช่น:

  • การใช้;จะดำเนินการหลายคำสั่ง (แยกกันสำหรับแต่ละอาร์กิวเมนต์)

    ตัวอย่าง:

    $ find /etc/rc* -exec echo Arg: {} ';'
    Arg: /etc/rc.common
    Arg: /etc/rc.common~previous
    Arg: /etc/rc.local
    Arg: /etc/rc.netboot
    

    อาร์กิวเมนต์ต่อไปนี้ทั้งหมดไปที่ findถูกนำไปเป็นอาร์กิวเมนต์ของคำสั่ง

    สตริง{}จะถูกแทนที่ด้วยชื่อไฟล์ปัจจุบันที่กำลังประมวลผล

  • การใช้+จะดำเนินการคำสั่งที่เป็นไปได้น้อยที่สุด (เนื่องจากมีการรวมอาร์กิวเมนต์เข้าด้วยกัน) มันคล้ายกันมากกับวิธีการxargsทำงานของคำสั่งดังนั้นมันจะใช้อาร์กิวเมนต์เป็นจำนวนมากต่อคำสั่งเท่าที่จะทำได้เพื่อหลีกเลี่ยงการเกินขีด จำกัด สูงสุดของอาร์กิวเมนต์ต่อบรรทัด

    ตัวอย่าง:

    $ find /etc/rc* -exec echo Arg: {} '+'
    Arg: /etc/rc.common /etc/rc.common~previous /etc/rc.local /etc/rc.netboot
    

    บรรทัดคำสั่งถูกสร้างโดยการต่อท้ายชื่อไฟล์ที่เลือกแต่ละชื่อที่ท้าย

    ตัวอย่างเดียวของ {}ภายในคำสั่ง

ดูสิ่งนี้ด้วย:


-5

เราพยายามค้นหาไฟล์สำหรับการดูแลทำความสะอาด

หา -exec echo {} \; คำสั่งวิ่งข้ามคืนในท้ายที่สุดไม่มีผลลัพธ์

หา -exec echo {} \ + มีผลลัพธ์และใช้เวลาเพียงไม่กี่ชั่วโมง

หวังว่านี่จะช่วยได้


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