มีประเด็นสำคัญ 3 ข้อที่ควรคำนึงถึงเมื่อพบArgument list too long
ข้อผิดพลาด:
ความยาวของอาร์กิวเมนต์บรรทัดคำสั่งถูก จำกัด โดยARG_MAX
ตัวแปรซึ่งตามนิยาม POSIXคือ "... [m] ความยาว aximum ของการโต้แย้งไปยังฟังก์ชั่น execรวมถึงข้อมูลสภาพแวดล้อม" (เน้นเพิ่ม) "นั่นคือเมื่อเชลล์ดำเนินการไม่ -built-it คำสั่งจะต้องเรียกหนึ่งในexec()
การวางไข่กระบวนการของคำสั่งนั้นและนั่นคือสิ่งที่ARG_MAX
เข้ามาเล่นนอกจากนี้ชื่อหรือเส้นทางไปยังคำสั่งตัวเอง (ตัวอย่างเช่น/bin/echo
) มีบทบาท
คำสั่งในตัวของเชลล์ดำเนินการโดยเชลล์ซึ่งหมายความว่าเชลล์ไม่ได้ใช้exec()
ตระกูลฟังก์ชันดังนั้นจึงไม่ได้รับผลกระทบจากARG_MAX
ตัวแปร
คำสั่งบางอย่างเช่นxargs
และfind
ตระหนักถึงARG_MAX
ตัวแปรและดำเนินการซ้ำ ๆ ภายใต้ขีด จำกัด นั้น
จากจุดด้านบนและดังที่แสดงในคำตอบที่ยอดเยี่ยมของ Kusalanandaสำหรับคำถามที่เกี่ยวข้องสิ่งArgument list too long
นี้สามารถเกิดขึ้นได้เมื่อสภาพแวดล้อมมีขนาดใหญ่ ดังนั้นเมื่อคำนึงถึงสภาพแวดล้อมของผู้ใช้แต่ละคนอาจแตกต่างกันไปและขนาดอาร์กิวเมนต์เป็นไบต์มีความเกี่ยวข้องจึงยากที่จะสร้างไฟล์ / อาร์กิวเมนต์จำนวนเดียว
วิธีจัดการกับข้อผิดพลาดดังกล่าว?
สิ่งสำคัญคือต้องไม่ให้ความสำคัญกับจำนวนไฟล์ แต่เน้นว่าคำสั่งที่คุณจะใช้นั้นเกี่ยวข้องกับexec()
ตระกูลของฟังก์ชันหรือไม่ซึ่งก็คือพื้นที่สแต็ก
ใช้เชลล์บิวด์อิน
ตามที่กล่าวไว้ก่อนหน้านี้ตัวบิวด์อินมีภูมิคุ้มกันARG_MAX
จำกัด นั่นคือสิ่งต่าง ๆ เช่นfor
ลูwhile
ปลูปบิวด์อินecho
และบิวด์อินprintf
- ทั้งหมดจะทำงานได้ดีพอ
for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done
ในคำถามที่เกี่ยวข้องกับการลบไฟล์มีวิธีแก้ไขดังนี้:
printf '%s\0' *.jpg | xargs -0 rm --
โปรดทราบว่านี่ใช้เชลล์ในprintf
ตัว หากเราโทรหาภายนอกprintf
สิ่งนั้นจะเกี่ยวข้องexec()
ด้วยดังนั้นจะล้มเหลวด้วยการโต้แย้งจำนวนมาก:
$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long
ทุบตีอาร์เรย์
ตามคำตอบของ jlliagre bash
ไม่ได้กำหนดขอบเขตในอาร์เรย์ดังนั้นการสร้างอาร์เรย์ของชื่อไฟล์และการใช้ชิ้นต่อการวนซ้ำสามารถทำได้เช่นกันดังแสดงในคำตอบของ danjpreron :
files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do
cp -t /path/to/new_dir/ "${files[@]:I:1000}"
done
อย่างไรก็ตามสิ่งนี้มีข้อ จำกัด ในการเป็น bash-specific และไม่ใช่ POSIX
เพิ่มพื้นที่สแต็ก
บางครั้งคุณสามารถเห็นคนแนะนำให้เพิ่มพื้นที่สแต็คที่มีulimit -s <NUM>
; บนค่า Linux ARG_MAX คือ 1 / 4th ของพื้นที่สแต็กสำหรับแต่ละโปรแกรมซึ่งหมายถึงการเพิ่มพื้นที่สแต็กเพิ่มสัดส่วนพื้นที่สำหรับอาร์กิวเมนต์
# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $(( $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304
ตามคำตอบของ Franck Dernoncourtซึ่งอ้างถึง Linux Journal เรายังสามารถคอมไพล์เคอร์เนลลินุกซ์ที่มีค่ามากกว่าสำหรับหน้าหน่วยความจำสูงสุดสำหรับการโต้แย้งอย่างไรก็ตามมันทำงานได้ดีเกินความจำเป็นและเปิดโอกาสในการหาประโยชน์ตามที่ระบุไว้ในบทความ
หลีกเลี่ยงเปลือก
อีกวิธีหนึ่งคือการใช้งานpython
หรือpython3
ที่มาพร้อมกับ Ubuntu python + here-docตัวอย่างด้านล่างเป็นสิ่งที่ฉันใช้เพื่อคัดลอกไดเรกทอรีขนาดใหญ่ของไฟล์บางแห่งในช่วง 40,000 รายการ:
$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
> if os.path.isfile(f):
> shutil.copy(f,'./newdir/')
> EOF
สำหรับ traversals recursive คุณสามารถใช้os.walk
ดูสิ่งนี้ด้วย: