ฉันเห็นคำตอบที่อ้างถึงลิงก์นี้อย่างสม่ำเสมอโดยระบุอย่างชัดเจนว่า"อย่าแยกวิเคราะห์ls
" เรื่องนี้ทำให้ฉันรำคาญด้วยเหตุผลสองประการ:
ดูเหมือนว่าข้อมูลในลิงก์นั้นได้รับการยอมรับโดยขายส่งด้วยคำถามเล็กน้อย แต่ฉันสามารถเลือกข้อผิดพลาดอย่างน้อยสองสามข้อในการอ่านแบบไม่เป็นทางการ
ดูเหมือนว่าปัญหาที่ระบุไว้ในลิงค์นั้นไม่ได้ก่อให้เกิดความปรารถนาที่จะหาทางแก้ไข
จากย่อหน้าแรก:
... เมื่อคุณขอ
[ls]
รายการไฟล์มีปัญหาใหญ่: Unix อนุญาตให้ตัวละครเกือบทุกตัวในชื่อไฟล์รวมถึงช่องว่าง, newlines, จุลภาค, สัญลักษณ์ท่อและสิ่งอื่น ๆ ที่คุณเคยลองใช้เป็น ตัวคั่นยกเว้น NUL ...ls
แยกชื่อไฟล์ด้วยการขึ้นบรรทัดใหม่ สิ่งนี้ใช้ได้จนกว่าคุณจะมีไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อ และเนื่องจากผมไม่ทราบว่าการดำเนินการใด ๆls
ที่ช่วยให้คุณสามารถที่จะยุติชื่อไฟล์ที่มีตัวอักษร NULls
แทนการขึ้นบรรทัดใหม่นี้จะทำให้เราไม่สามารถที่จะได้รับรายชื่อของชื่อไฟล์ได้อย่างปลอดภัยด้วย
คนเกียจคร้านใช่มั้ย วิธีที่เคยเราสามารถจัดการกับการขึ้นบรรทัดใหม่ยกเลิกชุดจดทะเบียนสำหรับข้อมูลที่อาจมีการขึ้นบรรทัดใหม่? ถ้าคนที่ตอบคำถามในเว็บไซต์นี้ไม่ได้ทำสิ่งนี้ทุกวันฉันอาจคิดว่าเรากำลังมีปัญหา
ความจริงก็คือls
การใช้งานส่วนใหญ่ให้ api ที่ง่ายมากสำหรับการแยกวิเคราะห์ผลลัพธ์ของพวกเขาและเราทุกคนได้ทำมันมาตลอดโดยไม่ได้ตระหนักถึงมัน ไม่เพียง แต่คุณสามารถจบชื่อไฟล์ด้วย null คุณสามารถเริ่มต้นด้วย null ได้เช่นกันหรือกับสตริงอื่น ๆ ที่คุณอาจต้องการ มีอะไรมากกว่าที่คุณสามารถกำหนดสตริงพลเหล่านี้ต่อไฟล์ชนิด โปรดพิจารณา:
LS_COLORS='lc=\0:rc=:ec=\0\0\0:fi=:di=:' ls -l --color=always | cat -A
total 4$
drwxr-xr-x 1 mikeserv mikeserv 0 Jul 10 01:05 ^@^@^@^@dir^@^@^@/$
-rw-r--r-- 1 mikeserv mikeserv 4 Jul 10 02:18 ^@file1^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 01:08 ^@file2^@^@^@$
-rw-r--r-- 1 mikeserv mikeserv 0 Jul 10 02:27 ^@new$
line$
file^@^@^@$
^@
ดูสิ่งนี้เพิ่มเติม
ตอนนี้เป็นส่วนถัดไปของบทความนี้ที่ทำให้ฉันเข้าใจว่า:
$ ls -l
total 8
-rw-r----- 1 lhunath lhunath 19 Mar 27 10:47 a
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a?newline
-rw-r----- 1 lhunath lhunath 0 Mar 27 10:47 a space
ปัญหาคือจากผลลัพธ์ของ
ls
ทั้งคุณและคอมพิวเตอร์ไม่สามารถบอกได้ว่าส่วนใดของชื่อไฟล์ มันแต่ละคำ? เลขที่มันคือแต่ละบรรทัด? ไม่ไม่มีคำตอบที่ถูกต้องสำหรับคำถามนี้นอกจากคุณไม่สามารถบอกได้โปรดสังเกตด้วยว่า
ls
บางครั้งข้อมูลของชื่อไฟล์ของคุณจะไม่ถูกต้อง (ในกรณีของเรามันเปลี่ยน\n
อักขระในระหว่างคำว่า"a"และ "newline"เป็นเครื่องหมายคำถาม? ......
หากคุณต้องการวนซ้ำไฟล์ทั้งหมดในไดเรกทอรีปัจจุบันให้ใช้การ
for
วนซ้ำและ glob:
for f in *; do
[[ -e $f ]] || continue
...
done
ผู้เขียนเรียกมันว่าชื่อไฟล์ที่อ่านไม่ออกเมื่อls
ส่งคืนรายการชื่อไฟล์ที่มีเปลือก globs แล้วแนะนำให้ใช้ shell glob เพื่อดึงรายชื่อไฟล์!
พิจารณาสิ่งต่อไปนี้:
printf 'touch ./"%b"\n' "file\nname" "f i l e n a m e" |
. /dev/stdin
ls -1q
f i l e n a m e
file?name
IFS="
" ; printf "'%s'\n" $(ls -1q)
'f i l e n a m e'
'file
name'
POSIX กำหนด-1
และ-q
ls
ถูกดำเนินการโดย:
-q
- บังคับแต่ละอินสแตนซ์ของอักขระชื่อไฟล์ที่ไม่สามารถพิมพ์ได้และ<tab>
s เพื่อเขียนเป็น'?'
อักขระเครื่องหมายคำถาม ( ) การใช้งานอาจมีตัวเลือกนี้เป็นค่าเริ่มต้นหากการส่งออกไปยังอุปกรณ์ปลายทาง
-1
- (ตัวเลขหนึ่งหลัก)บังคับให้เอาต์พุตเป็นหนึ่งรายการต่อบรรทัด
Globbing ไม่ได้ไม่มีปัญหาของตัวเอง - การ?
จับคู่ตัวละครใด ๆดังนั้น?
ผลลัพธ์ที่ตรงกันหลายรายการในรายการจะจับคู่ไฟล์เดียวกันหลายครั้ง ที่จัดการได้อย่างง่ายดาย
แม้ว่าวิธีการทำสิ่งนี้ไม่ได้เป็นจุด - มันไม่ใช้เวลามากที่จะทำหลังจากทั้งหมดและจะแสดงให้เห็นด้านล่าง - ฉันมีความสนใจในทำไมไม่ เมื่อพิจารณาแล้วคำตอบที่ดีที่สุดสำหรับคำถามนั้นได้รับการยอมรับแล้ว ฉันขอแนะนำให้คุณพยายามจดจ่อกับการบอกคนอื่น ๆ ว่าพวกเขาสามารถทำอะไรได้มากกว่าสิ่งที่พวกเขาทำไม่ได้ คุณมีโอกาสน้อยกว่ามากที่ฉันคิดว่าจะพิสูจน์ผิดอย่างน้อย
แต่ทำไมถึงต้องลอง แรงจูงใจหลักของฉันคือคนอื่นบอกฉันว่าฉันทำไม่ได้ ฉันรู้ดีว่าls
เอาต์พุตนั้นปกติและสามารถคาดเดาได้ตามที่คุณต้องการตราบใดที่คุณรู้ว่าควรมองหาอะไร ข้อมูลที่ผิดทำให้ฉันรำคาญมากกว่าทำในสิ่งส่วนใหญ่
ความจริงก็คือแม้ว่าจะมีข้อยกเว้นที่น่าทึ่งทั้งคำตอบของ Patrick และ Wumpus Q. Wumbley (แม้จะมีการจัดการที่ยอดเยี่ยมที่สุด)ฉันถือว่าข้อมูลส่วนใหญ่ในคำตอบที่นี่ถูกต้องเป็นส่วนใหญ่ - เปลือก glob ใช้ง่ายกว่า ls
และโดยทั่วไปมีประสิทธิภาพมากขึ้นเมื่อมันมาถึงการค้นหาไดเรกทอรีปัจจุบันกว่าจะแยก พวกเขาไม่ได้ แต่อย่างน้อยในเรื่องของฉันเหตุผลเพียงพอที่จะแสดงให้เห็นถึงทั้งการแพร่กระจายข้อมูลที่ผิดอ้างในบทความข้างต้นหรือพวกเขาให้เหตุผลที่ยอมรับได้ " ไม่เคยแยกls
. "
โปรดทราบว่าผลลัพธ์ที่สอดคล้องกันคำตอบของแพทริคส่วนใหญ่เป็นผลมาจากการที่เขาใช้แล้วzsh
- โดยค่าเริ่มต้น - ไม่ใช้คำสั่งแบบแยกคำด้วยผลลัพธ์แบบพกพา ดังนั้นเมื่อเขาถามว่าไฟล์ส่วนที่เหลือหายไปไหน คำตอบสำหรับคำถามนั้นคือเชลล์ของคุณกินมัน นี่คือเหตุผลที่คุณต้องตั้งค่าตัวแปรเมื่อใช้และจัดการกับรหัสเชลล์แบบพกพา ฉันถือว่าเขาล้มเหลวที่จะทราบสิ่งนี้ในคำตอบของเขาว่าทำให้เข้าใจผิดอย่างมากbash
zsh
$(
)
SH_WORD_SPLIT
zsh
คำตอบของ Wumpus ไม่ได้คำนวณสำหรับฉัน - ในบริบทรายการ?
ตัวละครคือเชลล์กลม ฉันไม่รู้วิธีอื่นที่จะพูดอย่างนั้น
เพื่อจัดการผลลัพธ์หลายกรณีคุณต้องจำกัดความโลภของ glob ต่อไปนี้จะสร้างฐานทดสอบชื่อไฟล์อันยิ่งใหญ่และแสดงให้คุณ:
{ printf %b $(printf \\%04o `seq 0 127`) |
sed "/[^[-b]*/s///g
s/\(.\)\(.\)/touch '?\v\2' '\1\t\2' '\1\n\2'\n/g" |
. /dev/stdin
echo '`ls` ?QUOTED `-m` COMMA,SEP'
ls -qm
echo ; echo 'NOW LITERAL - COMMA,SEP'
ls -m | cat
( set -- * ; printf "\nFILE COUNT: %s\n" $# )
}
เอาท์พุท
`ls` ?QUOTED `-m` COMMA,SEP
??\, ??^, ??`, ??b, [?\, [?\, ]?^, ]?^, _?`, _?`, a?b, a?b
NOW LITERAL - COMMA,SEP
?
\, ?
^, ?
`, ?
b, [ \, [
\, ] ^, ]
^, _ `, _
`, a b, a
b
FILE COUNT: 12
ตอนนี้ผมจะปลอดภัยตัวละครที่ไม่ได้เป็นทุก/slash
, -dash
, :colon
หรือตัวอักษรและตัวเลขใน glob เปลือกแล้วsort -u
รายการเพื่อให้ได้ผลลัพธ์ที่ไม่ซ้ำกัน สิ่งนี้มีความปลอดภัยเพราะเราls
ได้ลบอักขระที่ไม่สามารถพิมพ์ได้สำหรับเราแล้ว ดู:
for f in $(
ls -1q |
sed 's|[^-:/[:alnum:]]|[!-\\:[:alnum:]]|g' |
sort -u | {
echo 'PRE-GLOB:' >&2
tee /dev/fd/2
printf '\nPOST-GLOB:\n' >&2
}
) ; do
printf "FILE #$((i=i+1)): '%s'\n" "$f"
done
เอาท์พุท:
PRE-GLOB:
[!-\:[:alnum:]][!-\:[:alnum:]][!-\:[:alnum:]]
[!-\:[:alnum:]][!-\:[:alnum:]]b
a[!-\:[:alnum:]]b
POST-GLOB:
FILE #1: '?
\'
FILE #2: '?
^'
FILE #3: '?
`'
FILE #4: '[ \'
FILE #5: '[
\'
FILE #6: '] ^'
FILE #7: ']
^'
FILE #8: '_ `'
FILE #9: '_
`'
FILE #10: '?
b'
FILE #11: 'a b'
FILE #12: 'a
b'
ด้านล่างฉันเข้าหาปัญหาอีกครั้ง แต่ฉันใช้วิธีการอื่น โปรดจำไว้ว่า - นอกเหนือจาก\0
null - /
อักขระ ASCII เป็นสิ่งต้องห้ามเท่านั้นในชื่อพา ธ ฉันใส่ globs กันที่นี่และแทนที่จะรวม POSIX ระบุ-d
ตัวเลือกสำหรับการls
และยังระบุ POSIX สร้างสำหรับ-exec $cmd {} +
find
เนื่องจากfind
จะปล่อยออกมา/
ตามลำดับเพียงอย่างเดียวตามลำดับต่อไปนี้จะจัดหานักจัดรายการชื่อไฟล์ที่เรียกซ้ำได้ง่ายและเชื่อถือได้รวมถึงข้อมูลทันตกรรมทั้งหมดสำหรับทุกรายการ แค่คิดว่าคุณจะทำอะไรกับสิ่งนี้:
#v#note: to do this fully portably substitute an actual newline \#v#
#v#for 'n' for the first sed invocation#v#
cd ..
find ././ -exec ls -1ldin {} + |
sed -e '\| *\./\./|{s||\n.///|;i///' -e \} |
sed 'N;s|\(\n\)///|///\1|;$s|$|///|;P;D'
###OUTPUT
152398 drwxr-xr-x 1 1000 1000 72 Jun 24 14:49
.///testls///
152399 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
\///
152402 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
^///
152405 -rw-r--r-- 1 1000 1000 0 Jun 24 14:49
.///testls/?
`///
...
ls -i
มีประโยชน์มากโดยเฉพาะอย่างยิ่งเมื่อมีปัญหาเรื่องผลลัพธ์
ls -1iq |
sed '/ .*/s///;s/^/-inum /;$!s/$/ -o /' |
tr -d '\n' |
xargs find
นี่เป็นวิธีพกพาที่สุดที่ฉันนึกออก ด้วย GNU ls
คุณสามารถทำได้:
ls --quoting-style=WORD
และสุดท้ายนี้เป็นวิธีการแยกวิเคราะห์แบบls
ง่ายๆที่ฉันใช้บ่อยเมื่อต้องการหมายเลขไอโหนด:
ls -1iq | grep -o '^ *[0-9]*'
นั่นเป็นเพียงการส่งกลับหมายเลขไอโหนดซึ่งเป็นอีกทางเลือกหนึ่งสำหรับ POSIX
stat
ในคำตอบของฉันเพราะมันตรวจสอบว่าแต่ละไฟล์มีอยู่จริง บิตของคุณที่ด้านล่างพร้อมกับsed
สิ่งที่ไม่ทำงาน
ls
ในครั้งแรก? สิ่งที่คุณอธิบายยากมาก ฉันจะต้องแยกมันเพื่อทำความเข้าใจกับมันทั้งหมดและฉันเป็นผู้ใช้ที่มีความสามารถ คุณไม่สามารถคาดหวังว่า Joe เฉลี่ยของคุณจะสามารถจัดการกับสิ่งนี้ได้
ls
ผลลัพธ์ผิดในการเชื่อมโยงเดิม (และในที่อื่น ๆ ) คำถามนี้น่าจะสมเหตุสมผลถ้า OP ขอความช่วยเหลือให้เข้าใจ แต่ OP ก็แค่พยายามพิสูจน์การใช้งานที่ไม่ถูกต้องของเขาก็โอเค
parsing ls is bad
มันไม่ได้เป็นเพียงแค่ว่า การทำfor something in $(command)
และอาศัยการแยกคำเพื่อให้ได้ผลลัพธ์ที่ถูกต้องนั้นไม่ดีสำหรับคนส่วนใหญ่command's
ที่ไม่มีผลงานง่าย ๆ
time bash -c 'for i in {1..1000}; do ls -R &>/dev/null; done'
= 3.18s กับtime bash -c 'for i in {1..1000}; do echo **/* >/dev/null; done'
= 1.28s