ข้อผิดพลาด 'รายการอาร์กิวเมนต์ยาวเกินไป' ขณะคัดลอกไฟล์จำนวนมาก


12

ฉันใช้คำสั่งต่อไปนี้:

\cp -uf /home/ftpuser1/public_html/ftparea/*.jpg /home/ftpuser2/public_html/ftparea/

และฉันได้รับข้อผิดพลาด:

-bash: /bin/cp: Argument list too long

ฉันได้ลองด้วย:

ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} /home/ftpuser2/public_html/ftparea/

ยังคงได้รับ -bash: / bin / ls: รายการอาร์กิวเมนต์ยาวเกินไป

ความคิดใด ๆ


ฉันพยายามคัดลอก jpgs ทั้งหมดจากไดเรกทอรีหนึ่งไปยังอีกไดเรกทอรีหนึ่ง แต่มีเพียงไฟล์ใหม่และไฟล์ที่อัปเดตแล้วเท่านั้น
icelizard

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

ปัญหาไม่ได้อยู่กับ ls มันขึ้นอยู่กับจำนวนอาร์กิวเมนต์ที่เชลล์ส่งผ่านไปยัง ls คุณจะได้รับข้อผิดพลาดเดียวกันกับ vi หรือกับคำสั่งที่ไม่ใช่ builtin
Chris

แต่lsเป็นโดยเฉพาะอย่างยิ่งไม่ได้ออกแบบมาเพื่อทำสิ่งนี้: mywiki.wooledge.org/ParsingLs
หยุดชั่วคราวจนกว่าจะมีประกาศ

จริง แต่ในกรณีนี้ข้อผิดพลาดไม่ได้เกิดจากข้อผิดพลาดในการแยกวิเคราะห์ด้วย ls มันเกิดจากการส่งอาร์กิวเมนต์เป็นพันล้านครั้งไปยังกระบวนการใหม่ที่เกิดขึ้นกับ ls นอกเหนือจากการใช้งาน ls ที่ไม่เหมาะสมแล้วมันยังเกิดขึ้นกับข้อ จำกัด ด้านทรัพยากร / การออกแบบของ unix ในกรณีนี้ผู้ป่วยมีอาการปวดท้องและขาหัก
Chris

คำตอบ:


19

* .jpg ขยายไปยังรายการที่ยาวกว่าที่เชลล์สามารถจัดการได้ ลองใช้วิธีนี้แทน

find  /home/ftpuser/public_html/ftparea/ -name "*.jpg" -exec cp -uf "{}" /your/destination \;

ฉันใช้ find / home / ftpuser1 / public_html / ftparea / -name "* jpg" -exec cp -uf "{}" / home / ftpuser2 / public_html / ftparea / และพบข้อผิดพลาดต่อไปนี้: `อาร์กิวเมนต์ที่หายไปถึง` -exec '
icelizard

คุณไม่มีอาร์กิวเมนต์สุดท้ายของ CP ผู้ตอบตอบคุณถูกต้อง ตรวจสอบการใช้งานของคุณอีกครั้ง โปรดทราบว่าในคำตอบนี้จุดใน "* .jpg" หายไปสิ่งนี้อาจนำไปสู่พฤติกรรมที่ไม่เหมาะสม (เช่น cp a dir ที่ชื่อว่า "myjpg" เป็นต้น) หมายเหตุแล้วที่อาจจะ paranoic แต่ปลอดภัยกว่าที่จะระบุอย่างใกล้ชิดสิ่งที่คุณกำลังจะไปคัดลอกโดยใช้ไฟล์ประเภท (ป้องกัน dirs, symlinks และอื่น ๆ ได้รับผลกระทบ)
drAlberT

หลังจากการตรวจสอบอย่างใกล้ชิดฉันพลาด“ \;” เพื่อเสร็จสิ้นคำสั่งที่ -exec ควรดำเนินการ โง่ฉัน!
icelizard

@ อัลเบอร์: ขอบคุณสำหรับหัวอีกจุดที่หายไป นั่นคือการพิมพ์ผิด อัปเดตคำตอบแล้ว
Shawn Chin

ไม่ใช่ cp ที่ไม่สามารถจัดการได้ เปลือกไม่สามารถ
d -_- b

6

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

เนื่องจากวิธีจัดการแบบกลมโดยเชลล์สิ่งนี้จะส่งผลต่อคำสั่งระบบส่วนใหญ่เมื่อคุณใช้อาร์กิวเมนต์เดียวกัน ("* .jpg") เนื่องจากเชลล์ประมวลผลโดยเชลล์ก่อนจากนั้นจึงส่งไปยังคำสั่งคำสั่ง:

cp -uf *.jpg /targetdir/

โดยพื้นฐานแล้วเหมือนกับเชลล์เหมือนกับที่คุณเขียน:

cp -uf 1.jpg 2.jpg ... n-1.jpg n.jpg /targetdir/

หากคุณกำลังจัดการกับ jpegs จำนวนมากสิ่งนี้อาจกลายเป็นสิ่งที่ไม่สามารถจัดการได้อย่างรวดเร็ว ขึ้นอยู่กับหลักการตั้งชื่อของคุณและจำนวนไฟล์ที่คุณต้องดำเนินการจริง ๆ คุณสามารถเรียกใช้คำสั่งcpบนเซ็ตย่อยอื่นของไดเรกตอรีในแต่ละครั้ง:

cp -uf /sourcedir/[a-m]*.jpg /targetdir/
cp -uf /sourcedir/[n-z]*.jpg /targetdir/

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

Globbable ฉันชอบคำนั้น

คำสั่งบางคำสั่งเช่นfindและxargsสามารถจัดการรายการไฟล์ขนาดใหญ่ได้โดยไม่ต้องสร้างรายการอาร์กิวเมนต์ขนาดที่เจ็บปวด

find /sourcedir/ -name '*.jpg' -exec cp -uf {} /targetdir/ \;

อาร์กิวเมนต์ -exec จะรันส่วนที่เหลือของบรรทัดคำสั่งหนึ่งครั้งสำหรับแต่ละไฟล์ที่พบโดยfindโดยแทนที่ {} ด้วยชื่อไฟล์ที่พบ เนื่องจากคำสั่งcpรันเพียงไฟล์เดียวในแต่ละครั้งขีด จำกัด รายการอาร์กิวเมนต์จึงไม่เป็นปัญหา

อาจช้าเนื่องจากต้องดำเนินการแต่ละไฟล์แยกกัน การใช้xargsสามารถมอบโซลูชันที่มีประสิทธิภาพมากขึ้น:

find /sourcedir/ -name '*.jpg' -print0 | xargs -0 cp -uf -t /destdir/

xargsสามารถนำรายชื่อไฟล์ทั้งหมดที่มีให้โดยfindและแบ่งออกเป็นรายการอาร์กิวเมนต์ที่มีขนาดที่สามารถจัดการได้และเรียกใช้cpในแต่ละรายการย่อย

แน่นอนนอกจากนี้ยังมีความเป็นไปได้เพียง recompiling MAX_ARG_PAGESเคอร์เนลของคุณการตั้งค่าขนาดใหญ่ แต่การคอมไพล์เคอร์เนลใหม่นั้นทำงานได้ดีกว่าที่ฉันเต็มใจจะอธิบายในคำตอบนี้


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

เพิ่มในโซลูชัน xargs แต่ฉันยังคงกังวลว่า downvotes นั้นเป็นเพราะมีบางอย่างผิดปกติในรายละเอียดของฉันและไม่มีใครอยากบอกฉันว่ามันคืออะไร :(
goldPseudo

xargsดูเหมือนว่าจะมีประสิทธิภาพมากขึ้นเนื่องจากจำนวนการเรียกคำสั่งมีขนาดเล็กกว่ามาก ในกรณีของฉันฉันเห็นประสิทธิภาพที่ดีขึ้น 6-12 เท่าเมื่อใช้argsแล้วเมื่อใช้-execโซลูชันที่มีจำนวนไฟล์เพิ่มขึ้นเป็นการเพิ่มประสิทธิภาพ
Jan Vlcinsky

3

สิ่งนี้เกิดขึ้นเนื่องจากนิพจน์ตัวแทนของคุณ ( *.jpg) เกินขีดจำกัดความยาวอาร์กิวเมนต์บรรทัดคำสั่งเมื่อขยาย (อาจเป็นเพราะคุณมีไฟล์. jpg จำนวนมากอยู่ด้านล่าง/home/ftpuser/public_html/ftparea)

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


+1 สำหรับทรัพยากรภายนอกที่ดีในเรื่อง
viam0Zah

3

ในฐานะที่เป็น GoldPseudo แสดงความคิดเห็นมีข้อ จำกัด จำนวนอาร์กิวเมนต์ที่คุณสามารถส่งไปยังกระบวนการที่คุณกำลังวางไข่ ดูคำตอบของเขาสำหรับคำอธิบายที่ดีของพารามิเตอร์นั้น

คุณสามารถหลีกเลี่ยงปัญหาโดยการไม่ผ่านกระบวนการโต้แย้งมากเกินไปหรือลดจำนวนอาร์กิวเมนต์ที่คุณผ่าน

สำหรับห่วงในเปลือกค้นหาและ ls, grep และห่วงขณะที่ทุกคนทำสิ่งเดียวกันในสถานการณ์นี้ -

for file in /path/to/directory/*.jpg ; 
do
  rm "$file"
done

และ

find /path/to/directory/ -name '*.jpg' -exec rm  {} \;

และ

ls /path/to/directory/ | 
  grep "\.jpg$" | 
  while
    read file
  do
    rm "$file"
  done

ทั้งหมดมีหนึ่งโปรแกรมที่อ่านไดเร็กทอรี (เชลล์เอง, ค้นหาและ ls) และโปรแกรมอื่นที่ใช้อาร์กิวเมนต์หนึ่งตัวต่อการเรียกใช้หนึ่งครั้งและซ้ำผ่านรายการคำสั่งทั้งหมด

ทีนี้สิ่งนี้จะช้าเพราะ rm จำเป็นต้องถูกแยกและดำเนินการสำหรับแต่ละไฟล์ที่ตรงกับรูปแบบ * .jpg

นี่คือที่มา xargs เข้ามาเล่น xargs รับอินพุตมาตรฐานและสำหรับทุก N (สำหรับ freebsd เป็นค่าเริ่มต้น 5,000 บรรทัด) จะวางไข่หนึ่งโปรแกรมที่มีอาร์กิวเมนต์ N xargs เป็นการเพิ่มประสิทธิภาพของลูปด้านบนเนื่องจากคุณต้องการแยก 1 / N โปรแกรมเพื่อวนซ้ำทั้งชุดของไฟล์ที่อ่านอาร์กิวเมนต์จากบรรทัดคำสั่ง


2

มีจำนวนอาร์กิวเมนต์สูงสุดที่สามารถระบุให้กับโปรแกรมได้ bash จะขยาย * .jpg เป็นอาร์กิวเมนต์จำนวนมากไปยัง cp คุณสามารถแก้ไขได้โดยใช้ find, xargs หรือ rsync เป็นต้น

ดูที่นี่เกี่ยวกับ xargs และค้นหา

/programming/143171/how-can-i-use-xargs-to-copy-files-that-have-spaces-and-quotes-in-their-names


1

ลูกโลก '*' กำลังขยายไปยังชื่อไฟล์มากเกินไป ใช้ find / home / ftpuser / public_html -name '* .jpg' แทน


ค้นหาและ echo * ส่งผลให้ได้ผลลัพธ์เดียวกัน - ปุ่มที่นี่ใช้ xargs ไม่เพียง แต่ผ่านอาร์กิวเมนต์บรรทัดคำสั่ง 1 พันล้านรายการทั้งหมดไปยังคำสั่งที่เชลล์พยายามแยก
Chris

echo * จะล้มเหลวหากมีไฟล์มากเกินไป แต่ find จะสำเร็จ นอกจากนี้การใช้ find -exec พร้อม + เทียบเท่ากับการใช้ xargs (ไม่ใช่ทุกคนหาการสนับสนุน +, แม้ว่า)
William Pursell

1

การใช้+ตัวเลือกเพื่อfind -execเพิ่มความเร็วในการทำงานอย่างมาก

find  /home/ftpuser/public_html/ftparea/ -name "*jpg" -exec cp -uf -t /your/destination "{}" +

+ตัวเลือกที่ต้องใช้{}ให้เป็นอาร์กิวเมนต์สุดท้ายเพื่อใช้-t /your/destination(หรือ--target-directory=/your/destination) ตัวเลือกที่จะcpทำให้มันทำงาน

จากman find:

คำสั่ง -exec {} +

          This  variant  of the -exec action runs the specified command on  
          the selected files, but the command line is built  by  appending  
          each  selected file name at the end; the total number of invoca  
          tions of the command will  be  much  less  than  the  number  of  
          matched  files.   The command line is built in much the same way  
          that xargs builds its command lines.  Only one instance of  ‘{}’  
          is  allowed  within the command.  The command is executed in the  
          starting directory.

แก้ไข : จัดเรียงอาร์กิวเมนต์ใหม่เป็น cp


ฉันกำลังค้นหา: ไม่มีอาร์กิวเมนต์สำหรับ `-exec '/ home / ftpuser1 / public_html / ftparea / -name' * jpg '-exec cp -uf" {} "/ home / ftpuser2 / public_html / ftparea / +
icelizard

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

1

ดูเหมือนว่าคุณมี*.jpgไฟล์จำนวนมากเกินไปในไดเรกทอรีนั้นเพื่อให้พวกเขาทั้งหมดในบรรทัดคำสั่งในครั้งเดียว คุณสามารถลอง:

find /home/ftpuser/public_html/ftparea1 -name '*.jpg' | xargs -I {} cp -uf {} /home/ftpuser/public_html/ftparea2/

คุณอาจต้องตรวจสอบman xargsการติดตั้งเพื่อดูว่า-Iสวิตช์นั้นถูกต้องสำหรับระบบของคุณหรือไม่

ที่จริงแล้วคุณตั้งใจจะคัดลอกไฟล์เหล่านั้นไปยังตำแหน่งเดียวกันกับที่เคยมีอยู่หรือไม่?


ขอโทษเหล่านี้มีสองไดเรกทอรีที่แตกต่างกันควรจะ ftpuser1 และ ftpuser2
icelizard

เพิ่งลองสิ่งนี้: ls /home/ftpuser1/public_html/ftparea/*.jpg | xargs -I {} cp -uf {} / home / ftpuser2 / public_html / ftparea / ยังมี -bash: / bin / ls: รายการอาร์กิวเมนต์ยาวเกินไป
icelizard

โอ้คุณพูดถูกแล้วแน่นอนlsจะมีปัญหาแบบเดียวกัน! ฉันเปลี่ยนเป็นfindสิ่งที่จะไม่
Greg Hewgill

0

ไปที่โฟลเดอร์

cd /home/ftpuser1/public_html/

และดำเนินการต่อไปนี้:

cp -R ftparea/ /home/ftpuser2/public_html/

ด้วยวิธีนี้หากโฟลเดอร์ 'ftparea' มีโฟลเดอร์ย่อยนี่อาจเป็นผลกระทบเชิงลบหากคุณต้องการเฉพาะไฟล์ '* .jpg' แต่ถ้าไม่มีโฟลเดอร์ย่อยวิธีนี้จะเร็วกว่า ใช้ find และ xargs

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