วิธีรวมคำสั่ง 'tar' กับ 'find'


26

คำสั่ง find ให้เอาต์พุตนี้:

[root@localhost /]# find var/log/ -iname anaconda.*
var/log/anaconda.log
var/log/anaconda.xlog
var/log/anaconda.yum.log
var/log/anaconda.syslog
var/log/anaconda.program.log
var/log/anaconda.storage.log

หลังจากรวมกับ tar มันแสดงผลลัพธ์นี้:

[root@localhost /]# find var/log/ -iname anaconda.* -exec tar -cvf file.tar {} \;
var/log/anaconda.log
var/log/anaconda.xlog
var/log/anaconda.yum.log
var/log/anaconda.syslog
var/log/anaconda.program.log
var/log/anaconda.storage.log

แต่ในขณะที่รายชื่อไฟล์ tar ก็แสดงเพียงไฟล์เดียว

[root@localhost /]# tar -tvf file.tar
-rw------- root/root    208454 2012-02-27 12:01 var/log/anaconda.storage.log

ฉันทำอะไรผิดที่นี่

ด้วย xargs ฉันได้รับผลลัพธ์นี้:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf file1.tar

คำถามที่สอง

ในขณะที่พิมพ์ / หน้า var หมายความว่า find /var/log ทำไมมันถึงส่งข้อความนี้ tar: การนำชื่อ `/ 'นำออกจากชื่อสมาชิก

[root@localhost /]# find /var/log/ -iname anaconda.* -exec tar -cvf file.tar {} \;
tar: Removing leading `/' from member names
/var/log/anaconda.log
tar: Removing leading `/' from member names
/var/log/anaconda.xlog
tar: Removing leading `/' from member names
/var/log/anaconda.yum.log
tar: Removing leading `/' from member names
/var/log/anaconda.syslog
tar: Removing leading `/' from member names
/var/log/anaconda.program.log
tar: Removing leading `/' from member names
/var/log/anaconda.storage.log

ในรูปแบบที่เรียบง่ายความแตกต่างระหว่างสองต่อไปนี้คืออะไร?

find var/log และ find /var/log


นี่เป็นหัวข้อกึ่ง + ปิด แต่จะไปข้างหน้ากับ find คำสั่งคุณควรอ้างอิงข้อความค้นหา มันทำงานได้โดยไม่ต้องบางครั้ง แต่ไม่เสมอไป
nerdwaller

1
ถ้าคุณใช้ {} + แทน {} \; มันจะจัดกลุ่มผลลัพธ์ของการค้นหาเป็นหนึ่งอาร์กิวเมนต์
Jason S

คำตอบ:


35

สังเกตได้ว่า find จะโทรหา -exec การกระทำสำหรับ ทุกไฟล์เดียว พบว่า

ถ้าคุณวิ่ง tar -cvf file.tar {} สำหรับไฟล์ทุกไฟล์ find ผลลัพธ์หมายความว่าคุณจะเขียนทับ file.tar ทุกครั้งที่อธิบายว่าทำไมคุณถึงมีที่เก็บถาวรหนึ่งอันเหลืออยู่ anaconda.storage.log - เป็นไฟล์สุดท้าย find เอาท์พุท

ตอนนี้คุณต้องการจริง ผนวก ไฟล์ที่จะจัดเก็บแทนการสร้างขึ้นในแต่ละครั้ง (นี่คือสิ่งที่ -c ตัวเลือกทำ) ดังนั้นใช้สิ่งต่อไปนี้:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

-r ตัวเลือกผนวกเข้ากับที่เก็บแทนการสร้างใหม่ทุกครั้ง

บันทึก: แทนที่ -iname anaconda.* กับ -iname "anaconda.*". เครื่องหมายดอกจันเป็นสัญลักษณ์แทนและสามารถ ขยาย โดยเปลือกของคุณก่อน find แม้เห็นมัน เพื่อป้องกันการขยายตัวนี้ให้ตัดอาร์กิวเมนต์ในเครื่องหมายคำพูดคู่


ส่วน tar ถอดชั้นนำ /: ไฟล์เก็บถาวรควรมีเท่านั้น ญาติ ชื่อไฟล์ หากคุณเพิ่มไฟล์ด้วยการนำหน้า /พวกเขาจะถูกเก็บไว้เป็น แน่นอน ชื่อไฟล์หมายถึงแท้จริง /var/… เช่นบนคอมพิวเตอร์ของคุณ

IIRC นี่เป็นเพียงข้อควรระวังสำหรับ tar การใช้งานอื่นที่ไม่ใช่ GNU และเป็นวิธีที่ปลอดภัยกว่าเพราะคุณจะไม่เขียนทับข้อมูลจริงของคุณ /var/… เมื่อคุณแตกไฟล์เก็บถาวรถ้ามันมีชื่อไฟล์แบบสัมพัทธ์


6
แต่ทราบว่าถ้าคุณลอง tar ด้วยวิธีนี้ในการจัดเก็บเทปจริงเพิ่มไฟล์ในเวลาย้อนกลับเทปแล้วอ่านซ้ำทุกสิ่งทุกครั้งเพื่อไปให้ถึงจุดสิ้นสุดสิ่งทั้งหมดจะช้าลงอย่างน่าขัน ทางออกของคุณคือ เท่านั้น เหมาะสมถ้าคุณกำลังเขียนไฟล์ tar ลงบนดิสก์
Nicole Hamilton

2
จริง แต่ฉันคิดว่าเราสามารถเพิกเฉยต่อสถานการณ์นี้ได้อย่างปลอดภัย;)
slhck

@slhck * เป็น wildcard ที่ควรตรงกับความเป็นไปได้ทั้งหมดใช่ไหม แต่ที่นี่ find /var/log/ -iname anaconda* ให้อะไรและ find /var/log/ -iname anaconda.* ให้ผลลัพธ์ทำไม
max

เมื่อมีการใช้ไวด์การ์ดจะไม่สามารถเห็นได้ find อีกต่อไป ดังนั้นหากคุณมี anaconda*และในโฟลเดอร์ปัจจุบันของคุณมีชื่อบางอย่างเช่น anaconda5 (จับคู่ไวด์การ์ดนี้) ไวด์การ์ดจะถูกขยายและ find จะเห็น -iname anaconda5 แทน -iname anaconda*. ทำไมไฟล์แรกไม่ทำงานและไฟล์ที่สองขึ้นอยู่กับไฟล์ที่อยู่ในไดเรกทอรีปัจจุบันของคุณ @max
slhck

2
คุณสามารถใช้ได้ {} + แทน {} \; ดังนั้นมันจะจัดกลุ่มผลลัพธ์ของการค้นหาเป็นอาร์กิวเมนต์เดียว
Jason S

33

คุณสามารถใช้สิ่งที่ชอบ:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

-print0 และ -T ทำงานร่วมกันเพื่ออนุญาตให้ชื่อไฟล์ที่มีช่องว่างขึ้นบรรทัดใหม่ ฯลฯ สุดท้าย - บอก tar ให้อ่านชื่อไฟล์อินพุตจาก stdin

สังเกตได้ว่า -print0 ต้องมาท้ายคำสั่งของคุณต่อ คำตอบนี้ . มิฉะนั้นคุณอาจจะได้รับไฟล์มากกว่าที่คุณคาดหวัง


2
คุณได้ละเว้น -name ตัวเลือกทำให้การแก้ปัญหาของคุณ tar ไดเรกทอรีทั้งหมด ถ้านั่นคือสิ่งที่คุณต้องการคุณสามารถทำได้ง่ายขึ้นเช่น tar -cvf file.tar var/log โดยไม่ต้องใช้ find เลย
Nicole Hamilton

2
+1 ไปป์ที่รายการ tar เป็นความคิดที่ดี มันเป็นทางออกที่ดีที่สุดหากคุณคาดว่าชื่อพา ธ อาจมีช่องว่าง ฉันจะอธิบายด้วยว่ามันเป็นเทคนิคที่ดีที่สุดเนื่องจากมีทั้งความน่าเชื่อถือและมีประสิทธิภาพ แต่มันต้องการความรู้พิเศษเพิ่มเติมจากทั้งสองอย่าง find และ tar. ฉันชอบการทดแทนคำสั่งสวยมากเพียงเพราะเป็นเครื่องมือทั่วไปเพิ่มเติม: เรียนรู้วิธีการใช้เพียงครั้งเดียวแล้วใช้งานได้ทุกที่ (แต่ฉันยอมรับฉันอยู่บน Windows ด้วยกระสุนที่ใช้งานได้เสมอ) ขอโทษถ้าฉันดูหยาบคาย
Nicole Hamilton

2
คุณได้ +1 ของคุณแล้ว จงมีความสุข :) บรรทัดคำสั่งแบบยาวนั้นเป็นสิ่งที่ขาดไม่ได้ของการสร้างกระบวนการในทุกระบบ ฉันจำได้ว่าโต้เถียงกับ มาร์คลูคอฟสกี้ ที่ Microsoft ในช่วงต้น 90s ที่อักขระ 32K Unicode ของพวกเขา จำกัด บน NT นั้นน้อยเกินไปและทำให้เขาบ่นฉันไม่รู้ว่าจะต้องใช้จำนวนไบต์อีกนานเท่าใดในการจัดเก็บความยาวมากกว่ากางเกงขาสั้นในเคอร์เนล ถอนหายใจ วิธีแก้ปัญหากรณีทั่วไปมากขึ้นเมื่อรายการหาเรื่องยาวเกินไปจะทำมากขึ้นในเปลือก (ถ้าเป็นไปได้ในเหมืองมันเป็น) หรือใช้ xargs.
Nicole Hamilton

9
ถ้าคุณใช้ค้นหา -print0 ตัวเลือกคุณต้อง tar ของ --null ตัวเลือก
mivk

2
และ --no-unquote กลายเป็นสิ่งจำเป็นเช่นกัน: ชื่อไฟล์ที่มีเครื่องหมายแบ็กสแลช (ไม่นี่ไม่ใช่สมมุติฐาน - ฉันกำลังสร้างไฟล์เก็บถาวร tar จากรหัสของคนอื่นที่มีชื่อไฟล์ที่มีเครื่องหมายแบ็กสแลชในชื่อนั่นคือสิ่งที่ฉันค้นพบ)
hvd

11

ลองสิ่งนี้:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

คุณกำลังพยายามใช้ find ไปยัง -exec tar. แต่ทาง -exec ตัวเลือกใช้งานได้มันจะรันคำสั่งนั้นหนึ่งครั้งสำหรับแต่ละไฟล์ที่ตรงกันซึ่งพบ tar เพื่อเขียนทับไฟล์ tar ที่สร้างขึ้นในแต่ละครั้ง นั่นเป็นเหตุผลที่คุณจบลงด้วยการล่าสุด นอกจากนี้คุณต้องใส่เครื่องหมายคำพูดล้อมรอบรูปแบบที่คุณระบุ find เพื่อที่ว่าเชลล์จะไม่ขยายออกไปก่อนส่งต่อไป find.

การใช้การทดแทนคำสั่งด้วย backticks (หรือใช้ $(...) สัญกรณ์ถ้าคุณต้องการ) รายการทั้งหมดของชื่อที่ผลิตโดย find ถูกวางกลับไปยังบรรทัดคำสั่งเป็นอาร์กิวเมนต์ tarทำให้เขียนพวกเขาทั้งหมดในครั้งเดียว


2
สิ่งนี้อาจจบลงได้ไม่ดีหากค้นหาไฟล์ที่มีช่องว่างในชื่อบรรทัดใหม่หรืออักขระกลม สิ่งนี้ถูกผูกไว้กับความล้มเหลว - piping stdout จาก find ไม่ค่อยเป็นความคิดที่ดี mywiki.wooledge.org/ParsingLs
slhck

3
@slhck, การวางท่อ stdout จาก find มักจะเป็นความคิดที่ดีตามที่อธิบายไว้อย่างชัดเจนในหน้าเว็บที่คุณเชื่อมโยงกับความคิดเห็นของคุณ :) อันที่จริงแล้วมันเป็นวิธีที่แนะนำในการทำสิ่งต่าง ๆ คุณควรใช้ลูกเล่นบางอย่าง (เช่น read -r ของ -print0 ) ตามที่ฉันตอบ
terdon

4
@slhck นี่คือสาเหตุที่ชื่อไฟล์และไดเรกทอรีใน Unix และ Linux มีช่องว่างแบบดั้งเดิมหลีกเลี่ยงในชื่อ นอกจากนี้ยังเป็นสาเหตุที่บน Windows ที่ชื่อที่มีช่องว่างเป็นเรื่องธรรมดาฉันได้เพิ่มสัญกรณ์การทดแทนคำสั่งเพิ่มเติมให้กับตัวเอง แฮมิลตันซีเชลล์ ใช้ backticks สองครั้งที่รักษาทั้งบรรทัด (อาจรวมถึงช่องว่าง) เป็นคำเดียวที่จะวางกลับไปยังบรรทัดคำสั่ง โชคไม่ดีที่หอย Unix ไม่มีคุณสมบัตินั้น
Nicole Hamilton

1
พวกเขาอาจหลีกเลี่ยงแบบดั้งเดิม แต่ด้วยไฟล์ที่ถูกสร้างขึ้นในพื้นที่ผู้ใช้ผ่าน GUI คุณจะไม่สามารถละเลยไฟล์ที่มีช่องว่างได้อีกต่อไปและถือว่าเป็นพลเมืองชั้นสอง (เพราะมันเป็น Unix) เป็นเรื่องที่ดีที่คุณรวมไว้ในเชลล์ แต่สำหรับ Windows และ Unix shells ไม่ได้เป็นพิเศษ จำเป็นต้อง คุณลักษณะนั้นถ้าคุณใช้ไวยากรณ์ที่ถูกต้องและใช้ความระมัดระวังอย่างเหมาะสม นี่คือเหตุผลที่ฉันโพสต์ความคิดเห็นของฉันตั้งแต่แรก
slhck

2
ไม่ แต่ในสถานที่อื่นมันอาจเกิดขึ้นได้เป็นอย่างดี จึงเป็นความคิดที่ดีที่จะตั้งโปรแกรมป้องกัน - ดีกว่าปลอดภัยกว่าขออภัย นอกจากนี้ผู้เยี่ยมชมที่พบคำถามนี้อาจไม่จำเป็นต้องมีปัญหาเดียวกันอย่างแน่นอนและสงสัยว่าทำไมคำสั่งที่พวกเขาพบที่นี่ดูเหมือนจะทำงานได้ดีสำหรับกรณีนี้ แต่ไม่สำเร็จสำหรับพวกเขา ฉันจะทิ้งมันไว้กับคุณเพื่อแก้ไขคำสั่งฉันแค่คิดว่ามันสำคัญที่จะกล่าวถึงเพราะหลายคนพบเจอปัญหานี้ไม่ช้าก็เร็ว
slhck

6

คำถามที่ 1

คำสั่งของคุณล้มเหลวเพราะ tar กำลังนำแต่ละไฟล์ที่พบและเก็บถาวรลงใน file.tar. ในแต่ละครั้งที่ทำเช่นนั้นมันจะเขียนทับสิ่งที่สร้างไว้ก่อนหน้านี้ file.tar.

หากสิ่งที่คุณต้องการคือไฟล์เก็บถาวรหนึ่งไฟล์ที่มีไฟล์ทั้งหมดจากนั้นก็เรียกใช้ tar โดยตรงไม่จำเป็นต้องมี find (และใช่สิ่งนี้ใช้ได้กับไฟล์ที่มีช่องว่างในชื่อ):

tar -vcf file.tar /var/log/anaconda*   

คำถามที่ 2

คำสั่งทั้งสองนั้นแตกต่างกันโดยสิ้นเชิง:

  • find var / log จะค้นหาไดเรกทอรีที่เรียกว่า var/log ซึ่งเป็นไดเรกทอรีย่อยของไดเรกทอรีปัจจุบันของคุณ มันเทียบเท่ากับ find ./var/log แจ้งให้ทราบล่วงหน้า ./ )

  • find / var / log จะค้นหาไดเรกทอรีที่เรียกว่า /var/log ซึ่งเป็นไดเรกทอรีย่อยของรูท /.

การนำ / ข้อความมาจาก tarไม่ใช่ find. หมายความว่าเป็นการลบครั้งแรก / ของชื่อไฟล์ที่จะสร้าง แน่นอน เส้นทางเข้าสู่ ญาติ . ซึ่งหมายความว่าไฟล์จาก /var/log/anaconda.error จะถูกแยกออกไป ./var/log/anaconda.error เมื่อคุณยกเลิกการเก็บถาวร


1

มีสองวิธี -exec สามารถทำงานได้ วิธีหนึ่งรันคำสั่งหลายครั้ง - หนึ่งครั้งสำหรับแต่ละไฟล์ อีกวิธีหนึ่งรันคำสั่งหนึ่งครั้งรวมถึงไฟล์ทั้งหมดเป็นรายการพารามิเตอร์

  • -exec tar -cvf file.tar {} ';' ทำงาน tar คำสั่งสำหรับแต่ละไฟล์เขียนทับไฟล์เก็บถาวรในแต่ละครั้ง
  • -exec tar -cvf file.tar {} '+' ทำงาน tar คำสั่งหนึ่งครั้งสร้างไฟล์เก็บถาวรของไฟล์ทั้งหมดที่พบ

1

ฉันคิดว่าการใช้ -exec สำหรับแต่ละไฟล์สามารถทำให้การบีบอัด tar ช้ามากหากคุณมีไฟล์จำนวนมาก ฉันชอบใช้คำสั่ง:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.