ทำไม * ไม่ * แยก `ls '(และจะทำอะไรแทน)


204

ฉันเห็นคำตอบที่อ้างถึงลิงก์นี้อย่างสม่ำเสมอโดยระบุอย่างชัดเจนว่า"อย่าแยกวิเคราะห์ls" เรื่องนี้ทำให้ฉันรำคาญด้วยเหตุผลสองประการ:

  1. ดูเหมือนว่าข้อมูลในลิงก์นั้นได้รับการยอมรับโดยขายส่งด้วยคำถามเล็กน้อย แต่ฉันสามารถเลือกข้อผิดพลาดอย่างน้อยสองสามข้อในการอ่านแบบไม่เป็นทางการ

  2. ดูเหมือนว่าปัญหาที่ระบุไว้ในลิงค์นั้นไม่ได้ก่อให้เกิดความปรารถนาที่จะหาทางแก้ไข

จากย่อหน้าแรก:

... เมื่อคุณขอ[ls]รายการไฟล์มีปัญหาใหญ่: Unix อนุญาตให้ตัวละครเกือบทุกตัวในชื่อไฟล์รวมถึงช่องว่าง, newlines, จุลภาค, สัญลักษณ์ท่อและสิ่งอื่น ๆ ที่คุณเคยลองใช้เป็น ตัวคั่นยกเว้น NUL ... lsแยกชื่อไฟล์ด้วยการขึ้นบรรทัดใหม่ สิ่งนี้ใช้ได้จนกว่าคุณจะมีไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อ และเนื่องจากผมไม่ทราบว่าการดำเนินการใด ๆlsที่ช่วยให้คุณสามารถที่จะยุติชื่อไฟล์ที่มีตัวอักษร NUL lsแทนการขึ้นบรรทัดใหม่นี้จะทำให้เราไม่สามารถที่จะได้รับรายชื่อของชื่อไฟล์ได้อย่างปลอดภัยด้วย

คนเกียจคร้านใช่มั้ย วิธีที่เคยเราสามารถจัดการกับการขึ้นบรรทัดใหม่ยกเลิกชุดจดทะเบียนสำหรับข้อมูลที่อาจมีการขึ้นบรรทัดใหม่? ถ้าคนที่ตอบคำถามในเว็บไซต์นี้ไม่ได้ทำสิ่งนี้ทุกวันฉันอาจคิดว่าเรากำลังมีปัญหา

ความจริงก็คือ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 - โดยค่าเริ่มต้น - ไม่ใช้คำสั่งแบบแยกคำด้วยผลลัพธ์แบบพกพา ดังนั้นเมื่อเขาถามว่าไฟล์ส่วนที่เหลือหายไปไหน คำตอบสำหรับคำถามนั้นคือเชลล์ของคุณกินมัน นี่คือเหตุผลที่คุณต้องตั้งค่าตัวแปรเมื่อใช้และจัดการกับรหัสเชลล์แบบพกพา ฉันถือว่าเขาล้มเหลวที่จะทราบสิ่งนี้ในคำตอบของเขาว่าทำให้เข้าใจผิดอย่างมากbashzsh$()SH_WORD_SPLITzsh

คำตอบของ 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'

ด้านล่างฉันเข้าหาปัญหาอีกครั้ง แต่ฉันใช้วิธีการอื่น โปรดจำไว้ว่า - นอกเหนือจาก\0null - /อักขระ 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


12
@mikeserv ตกลงฉันทำ Shell glob เร็วกว่า 2.48 เท่า 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
Patrick

28
เกี่ยวกับการอัปเดตล่าสุดของคุณโปรดหยุดการใช้งานเอาต์พุตแบบวิชวลเนื่องจากระบุว่าโค้ดของคุณใช้งานได้ ส่งเอาต์พุตของคุณไปยังโปรแกรมจริงและให้โปรแกรมลองและดำเนินการกับไฟล์ นี่คือเหตุผลที่ฉันใช้statในคำตอบของฉันเพราะมันตรวจสอบว่าแต่ละไฟล์มีอยู่จริง บิตของคุณที่ด้านล่างพร้อมกับsedสิ่งที่ไม่ทำงาน
Patrick

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

46
-1 สำหรับการใช้คำถามเพื่อเลือกอาร์กิวเมนต์ เหตุผลทั้งหมดที่การแยกวิเคราะห์lsผลลัพธ์ผิดในการเชื่อมโยงเดิม (และในที่อื่น ๆ ) คำถามนี้น่าจะสมเหตุสมผลถ้า OP ขอความช่วยเหลือให้เข้าใจ แต่ OP ก็แค่พยายามพิสูจน์การใช้งานที่ไม่ถูกต้องของเขาก็โอเค
..

14
@mikeserv parsing ls is badมันไม่ได้เป็นเพียงแค่ว่า การทำfor something in $(command)และอาศัยการแยกคำเพื่อให้ได้ผลลัพธ์ที่ถูกต้องนั้นไม่ดีสำหรับคนส่วนใหญ่command'sที่ไม่มีผลงานง่าย ๆ
BroSlow

คำตอบ:


184

ฉันไม่เชื่อเลยในเรื่องนี้ แต่ขอสมมติเพื่อเหตุผลที่คุณสามารถทำได้ถ้าคุณพร้อมที่จะใช้ความพยายามมากพอให้แจงส่วนที่lsน่าเชื่อถือแม้หน้า "ศัตรู" - คนที่ รู้รหัสที่คุณเขียนและตั้งใจเลือกชื่อไฟล์ที่ออกแบบมาเพื่อทำลายมัน

แม้ว่าคุณจะทำอย่างนั้นก็ยังจะเป็นความคิดที่ไม่ดี

เชลล์เป้าหมายไม่ใช่ภาษาที่ดี ไม่ควรใช้กับสิ่งที่ซับซ้อนเว้นแต่ว่าการพกพาที่รุนแรงเป็นสิ่งสำคัญมากกว่าปัจจัยอื่น ๆ (เช่นautoconf)

ฉันอ้างว่าหากคุณประสบกับปัญหาในการแยกวิเคราะห์ผลลัพธ์ที่lsดูเหมือนเส้นทางที่น้อยที่สุดสำหรับเชลล์สคริปนั่นเป็นข้อบ่งชี้ที่ชัดเจนว่าสิ่งที่คุณกำลังทำนั้นซับซ้อนเกินไปสำหรับเชลล์และคุณควรเขียนใหม่ทั้งหมดใน Perl หรือ Python นี่คือโปรแกรมสุดท้ายของคุณใน Python:

import os, sys
for subdir, dirs, files in os.walk("."):
    for f in dirs + files:
      ino = os.lstat(os.path.join(subdir, f)).st_ino
      sys.stdout.write("%d %s %s\n" % (ino, subdir, f))

สิ่งนี้ไม่มีปัญหาใด ๆ กับตัวละครที่ผิดปกติในชื่อไฟล์ - เอาต์พุตนั้นคลุมเครือในแบบเดียวกับผลลัพธ์ของlsคลุมเครือ แต่นั่นไม่สำคัญว่าจะอยู่ในโปรแกรม "ของจริง" (ตรงข้ามกับตัวอย่างเช่นนี้) ซึ่งจะ ใช้ผลos.path.join(subdir, f)โดยตรง

ความสำคัญเท่าเทียมกันและในทางตรงข้ามกับสิ่งที่คุณเขียนมันจะยังคงสมเหตุสมผลอยู่หกเดือนนับจากนี้และมันจะง่ายต่อการปรับเปลี่ยนเมื่อคุณต้องการให้มันทำสิ่งที่แตกต่างออกไปเล็กน้อย ตามภาพประกอบสมมติว่าคุณค้นพบความต้องการที่จะแยก dotfiles และการสำรองข้อมูลเอดิเตอร์และประมวลผลทุกอย่างตามลำดับตัวอักษรโดยชื่อฐาน:

import os, sys
filelist = []
for subdir, dirs, files in os.walk("."):
    for f in dirs + files:
        if f[0] == '.' or f[-1] == '~': continue
        lstat = os.lstat(os.path.join(subdir, f))
        filelist.append((f, subdir, lstat.st_ino))

filelist.sort(key = lambda x: x[0])
for f, subdir, ino in filelist: 
   sys.stdout.write("%d %s %s\n" % (ino, subdir, f))

5
ดีจัง. นั่นfor in | for inพูดถึงการเรียกซ้ำ? ฉันไม่แน่ใจ. แม้ว่าจะเป็นมากกว่านั้นไม่ได้ใช่ไหม? นี่เป็นคำตอบเดียวที่ทำให้ฉันมีเหตุผล
mikeserv

10
ไม่มีการเรียกซ้ำเพียงซ้อน - forลูป os.walkคือการทำบางยกของหนักอย่างจริงจังอยู่เบื้องหลัง แต่คุณไม่ต้องกังวลเกี่ยวกับเรื่องใด ๆ มากกว่าที่คุณต้องกังวลเกี่ยวกับวิธีการlsหรือfindทำงานภายใน
zwol

6
ในทางเทคนิคแล้วos.walkส่งกลับวัตถุกำเนิด Generators เป็นรายการสันหลังยาวของ Python ทุกครั้งที่วนรอบนอกวนซ้ำตัวสร้างจะถูกเรียกใช้และ "ให้ผลตอบแทน" เนื้อหาของไดเรกทอรีย่อยอื่น ฟังก์ชันการทำงานที่เทียบเท่าใน Perl คือFile::Findถ้าช่วย
zwol

6
คุณควรตระหนักว่าฉัน 100% เห็นด้วยกับเอกสารที่คุณกำลังวิพากษ์วิจารณ์และกับคำตอบของ Patrick และ Terdon คำตอบของฉันมีวัตถุประสงค์เพื่อให้เพิ่มเติมเหตุผลอิสระเพื่อหลีกเลี่ยงการแยกวิเคราะห์lsผลลัพธ์
zwol

19
นี่เป็นสิ่งที่ทำให้เข้าใจผิดมาก เชลล์ไม่ใช่ภาษาการเขียนโปรแกรมที่ดี แต่เนื่องจากไม่ใช่ภาษาการเขียนโปรแกรม มันเป็นภาษาสคริปต์ และเป็นภาษาสคริปต์ที่ดี
Miles Rout

178

ลิงก์นั้นมีการอ้างอิงมากเนื่องจากข้อมูลมีความถูกต้องสมบูรณ์และอยู่ที่นั่นเป็นเวลานาน


lsแทนที่อักขระที่ไม่สามารถพิมพ์ได้ด้วยอักขระ glob ใช่ แต่อักขระเหล่านั้นไม่ได้อยู่ในชื่อไฟล์จริง เหตุใดเรื่องนี้ 2 เหตุผล:

  1. หากคุณส่งชื่อไฟล์นั้นไปยังโปรแกรมชื่อไฟล์นั้นจะไม่มีอยู่จริง มันจะต้องขยาย glob เพื่อให้ได้ชื่อไฟล์จริง
  2. ไฟล์ glob อาจตรงกับมากกว่าหนึ่งไฟล์

ตัวอย่างเช่น:

$ touch a$'\t'b
$ touch a$'\n'b
$ ls -1
a?b
a?b

สังเกตว่าเรามี 2 ไฟล์ซึ่งมีลักษณะเหมือนกันทุกประการ คุณจะแยกแยะพวกเขาอย่างไรถ้าพวกเขาทั้งคู่แสดงเป็นa?bอย่างไร


ผู้เขียนเรียกมันว่าชื่อไฟล์ที่อ่านไม่ออกเมื่อ ls ส่งคืนรายการชื่อไฟล์ที่มีเปลือก globs แล้วแนะนำให้ใช้ shell glob เพื่อดึงรายชื่อไฟล์!

มีความแตกต่างที่นี่ เมื่อคุณได้รับ glob กลับมาดังที่แสดงว่า glob อาจตรงกับมากกว่าหนึ่งไฟล์ อย่างไรก็ตามเมื่อคุณวนซ้ำผลลัพธ์ที่ตรงกับ glob คุณจะได้รับไฟล์ที่แน่นอนไม่ใช่ glob

ตัวอย่างเช่น:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

แจ้งให้ทราบว่าxxdการแสดงผลลัพธ์ที่$fileมีตัวละครดิบ\tและไม่\n?

หากคุณใช้lsคุณจะได้รับสิ่งนี้แทน:

for file in $(ls -1q); do printf '%s' "$file" | xxd; done
0000000: 613f 62                                  a?b
0000000: 613f 62                                  a?b

"ฉันจะย้ำต่อไปทำไมไม่ใช้lsล่ะ"

ตัวอย่างที่คุณให้มาไม่ได้ผลจริง ดูเหมือนว่าจะใช้งานได้ แต่ก็ไม่เป็นเช่นนั้น

ฉันหมายถึงสิ่งนี้:

 for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done

ฉันได้สร้างไดเรกทอรีที่มีชื่อไฟล์มากมาย:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62                                  a b
0000000: 6120 2062                                a  b
0000000: 61e2 8082 62                             a...b
0000000: 61e2 8083 62                             a...b
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

เมื่อฉันเรียกใช้รหัสของคุณฉันจะได้รับสิ่งนี้:

$ for f in $(ls -1q | tr " " "?") ; do [ -f "$f" ] && echo "./$f" ; done
./ab
./ab

ไฟล์ที่เหลือจะไปไหน

ลองทำสิ่งนี้แทน:

$ for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a??b’: No such file or directory
./ab
./ab
stat: cannot stat ‘./a?b’: No such file or directory
stat: cannot stat ‘./a?b’: No such file or directory

ตอนนี้ให้ใช้ glob จริง:

$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a  b
./ab
./ab
./a b
./a
b

ด้วยทุบตี

ตัวอย่างข้างต้นคือกับเชลล์ปกติของฉัน zsh เมื่อฉันทำขั้นตอนซ้ำด้วย bash ฉันจะได้รับชุดผลลัพธ์ที่แตกต่างอย่างสิ้นเชิงกับตัวอย่างของคุณ:

ชุดไฟล์เดียวกัน:

$ for file in *; do printf '%s' "$file" | xxd; done
0000000: 6120 62                                  a b
0000000: 6120 2062                                a  b
0000000: 61e2 8082 62                             a...b
0000000: 61e2 8083 62                             a...b
0000000: 6109 62                                  a.b
0000000: 610a 62                                  a.b

ผลลัพธ์ที่แตกต่างอย่างสิ้นเชิงกับรหัสของคุณ:

for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f"; done
./a b
./ab
./ab
./a b
./a
b
./a  b
./ab
./ab
./a b
./ab
./ab
./a b
./a
b
./a b
./ab
./ab
./a b
./a
b

ด้วยเปลือกกลมมันทำงานได้อย่างสมบูรณ์แบบ:

$ for f in *; do stat --format='%n' "./$f"; done
./a b
./a  b
./ab
./ab
./a b
./a
b

เหตุผลทุบตีทำงานด้วยวิธีนี้กลับไปที่หนึ่งในจุดที่ฉันทำในตอนต้นของคำตอบ: "ไฟล์ glob อาจตรงกับมากกว่าหนึ่งไฟล์"

lsกำลังคืนค่า glob เดียวกัน ( a?b) สำหรับไฟล์หลาย ๆ ไฟล์ดังนั้นทุกครั้งที่เราขยายไฟล์นี้เราจะได้ไฟล์ทุกไฟล์ที่ตรงกับมัน


วิธีสร้างรายการไฟล์ที่ฉันใช้ซ้ำ:

touch 'a b' 'a  b' a$'\xe2\x80\x82'b a$'\xe2\x80\x83'b a$'\t'b a$'\n'b

รหัสฐานสิบหกคืออักขระ UTF-8 NBSP


5
@mikeserv แก้ปัญหาของเขาจริง ๆ แล้วไม่คืน glob ฉันเพิ่งปรับปรุงคำตอบของฉันเพื่อชี้แจงจุดนั้น
Patrick

18
"ไม่ใช่ส่วนที่เหลือ"? มันเป็นพฤติกรรมที่ไม่สอดคล้องกันและผลลัพธ์ที่ไม่คาดคิดนั่นคือเหตุผลใด?
Patrick

11
@mikeserv คุณไม่เห็นความคิดเห็นของฉันในคำถามของคุณ? เชลล์ globbing คือ 2.5 lsครั้งเร็วกว่า ฉันขอให้คุณทดสอบรหัสของคุณด้วยเพราะมันไม่ทำงาน zsh เกี่ยวข้องอะไรกับเรื่องนี้บ้าง?
Patrick

27
@mikeserv ไม่ทุกอย่างยังคงใช้ได้แม้ในการทุบตี แม้ว่าฉันจะทำกับคำถามนี้เพราะคุณไม่ได้ฟังสิ่งที่ฉันพูด
Patrick

7
คุณรู้ว่าฉันคิดว่าฉันจะยกเลิกคำตอบนี้และชี้แจงให้ชัดเจนว่าฉันเห็นด้วยกับทุกอย่างที่พูด ;-)
zwol

54

ลองและทำให้ง่ายขึ้นเล็กน้อย:

$ touch a$'\n'b a$'\t'b 'a b'
$ ls
a b  a?b  a?b
$ IFS="
"
$ set -- $(ls -1q | uniq)
$ echo "Total files in shell array: $#"
Total files in shell array: 4

ดู? มันผิดไปหมดแล้ว มี 3 ไฟล์เป็น แต่ทุบตีเป็นรายงาน 4. นี้เป็นเพราะsetกำลังจะได้รับ globs ที่สร้างขึ้นโดยที่มีการขยายตัวเปลือกก่อนที่จะถูกส่งผ่านไปยังls setนี่คือสาเหตุที่คุณได้รับ:

$ for x ; do
>     printf 'File #%d: %s\n' $((i=$i+1)) "$x"
> done
File #1: a b
File #2: a b
File #3: a    b
File #4: a
b

หรือถ้าคุณต้องการ:

$ printf ./%s\\0 "$@" |
> od -A n -c -w1 |
> sed -n '/ \{1,3\}/s///;H
> /\\0/{g;s///;s/\n//gp;s/.*//;h}'
./a b
./a b
./a\tb
./a\nb

bash 4.2.45ดังกล่าวข้างต้นได้รับการทำงานใน


2
ฉันลงทะเบียนสิ่งนี้ เป็นการดีที่เห็นรหัสของคุณกัดคุณ แต่เพียงเพราะฉันเข้าใจผิดไม่ได้หมายความว่าทำไม่ถูก ฉันแสดงให้คุณเห็นวิธีที่ง่ายมากที่จะทำในเช้าวันนี้ด้วยls -1qRi | grep -o '^ *[0-9]*'- นั่นคือการแยกวิเคราะห์lsผู้ชายและมันเป็นวิธีที่เร็วและดีที่สุดที่ฉันรู้ว่าจะได้รับรายการหมายเลขไอโหนด
mikeserv

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

@cHao - ฉันไม่โต้แย้งข้อดีของมัน - ฉันประท้วงการโฆษณาชวนเชื่อ
mikeserv

16
@mikeserv: ข้อโต้แย้งที่มีหลักฐานดีและสมควรได้รับ แม้คุณจะแสดงให้พวกเขาเป็นจริง
cHao

1
@cHao - ฉันไม่เห็นด้วย ไม่มีเส้นแบ่งระหว่างมนต์และภูมิปัญญา
mikeserv

50

ผลผลิตของls -qไม่ได้เป็นก้อนเลย มันใช้?เพื่อหมายถึง "มีตัวละครที่นี่ที่ไม่สามารถแสดงได้โดยตรง" Globs ใช้?เพื่อหมายถึง "อนุญาตให้ใช้อักขระใดก็ได้ที่นี่"

Globs มีอักขระพิเศษอื่น ๆ ( *และ[]อย่างน้อยและภายใน[]คู่มีมากกว่า) ไม่มีสิ่งใดที่ถูกหลบหนีls -qได้

$ touch x '[x]'
$ ls -1q
[x]
x

หากคุณปฏิบัติต่อls -1qผลลัพธ์นั้นจะมีชุดของโกลว์และขยายออกไม่เพียง แต่คุณจะได้รับxสองครั้งเท่านั้นคุณจะพลาด[x]อย่างสมบูรณ์ ในฐานะที่เป็นรูปกลมมันไม่ตรงกับตัวเองเป็นสตริง

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


42

คำตอบนั้นง่าย: กรณีพิเศษของlsคุณต้องจัดการกับผลประโยชน์ที่เกินดุล กรณีพิเศษเหล่านี้สามารถหลีกเลี่ยงได้หากคุณไม่แยกวิเคราะห์lsเอาต์พุต

มนต์ที่นี่ไม่เคยไว้วางใจระบบไฟล์ผู้ใช้ (เทียบเท่ากับไม่เคยไว้ใจผู้ใช้อินพุต ) หากมีวิธีการที่จะทำงานได้ตลอดเวลาด้วยความมั่นใจ 100% ควรเป็นวิธีที่คุณต้องการแม้ว่าlsจะใช้วิธีเดียวกัน แต่มีความมั่นใจน้อยกว่า ฉันจะไม่เข้าไปดูรายละเอียดทางเทคนิคเนื่องจากมันครอบคลุมโดยterdonและPatrickอย่างกว้างขวาง ฉันรู้ว่าเนื่องจากความเสี่ยงของการใช้lsในการทำธุรกรรม (และอาจมีราคาแพง) ที่สำคัญซึ่งงาน / ชื่อเสียงของฉันอยู่ในสายฉันจะชอบวิธีการแก้ปัญหาใด ๆ ที่ไม่ได้มีระดับของความไม่แน่นอนถ้าสามารถหลีกเลี่ยงได้

ฉันรู้ว่าบางคนชอบความเสี่ยงบางกว่าแน่นอนแต่ผมได้ยื่นรายงานข้อผิดพลาด


33

เหตุผลที่ผู้คนบอกว่าไม่เคยทำอะไรไม่จำเป็นเพราะมันไม่สามารถทำสิ่งที่ถูกต้องได้ เราอาจทำเช่นนั้นได้ แต่อาจซับซ้อนกว่าและมีประสิทธิภาพน้อยกว่าทั้งในด้านอวกาศและเวลา ตัวอย่างเช่นจะเป็นการดีถ้าจะพูดว่า "อย่าสร้างแบ็กเอนด์อีคอมเมิร์ซขนาดใหญ่ในชุด x86"

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

ซับซ้อนกว่านี้ไหม? ใช่ แต่เราสามารถซ่อนสิ่งนั้นไว้ข้างหลังฟังก์ชันผู้ช่วยได้

ดังนั้นตอนนี้เพื่อประสิทธิภาพ:

ประสิทธิภาพของพื้นที่: โซลูชันของคุณอาศัยuniqการกรองรายการที่ซ้ำกันดังนั้นเราจึงไม่สามารถสร้างผลลัพธ์ที่ขี้เกียจ ดังนั้นทั้งO(1)กับหรือทั้งสองมีO(n)O(n)

เวลาที่มีประสิทธิภาพ: กรณีที่ดีที่สุดuniqใช้วิธี HashMap ดังนั้นเราจึงยังคงมีO(n)ขั้นตอนวิธีการในจำนวนขององค์ประกอบจัดหาO(n log n)อาจแม้ว่ามันจะเป็น

ตอนนี้ปัญหาที่แท้จริง: ในขณะที่อัลกอริทึมของคุณยังไม่ได้ดูแย่เกินไปฉันก็ระวังที่จะใช้องค์ประกอบที่จัดหาและไม่ใช่องค์ประกอบสำหรับ n เพราะนั่นจะสร้างความแตกต่างใหญ่ สมมติว่าคุณมีไฟล์\n\nที่จะส่งผลให้กลมกลืนเพื่อ??ให้ตรงกับไฟล์ทุกตัวละคร 2 ตัวในรายชื่อ สนุกถ้าคุณมีไฟล์อื่น\n\rที่จะส่งผล??และยังส่งคืนไฟล์ตัวละครทั้ง 2 ตัว .. ดูว่าจะเกิดอะไรขึ้น? ชี้แจงแทนพฤติกรรมเชิงเส้นแน่นอนมีคุณสมบัติเป็น "พฤติกรรมรันไทม์แย่ลง" .. มันเป็นความแตกต่างระหว่างอัลกอริทึมในทางปฏิบัติและหนึ่งที่คุณเขียนเอกสารในวารสาร CS เชิงทฤษฎีเกี่ยวกับ

ทุกคนชอบตัวอย่างใช่มั้ย ไปเลย. สร้างโฟลเดอร์ชื่อ "test" และใช้สคริปต์ python นี้ในไดเรกทอรีเดียวกันกับโฟลเดอร์

#!/usr/bin/env python3
import itertools
dir = "test/"
filename_length = 3
options = "\a\b\t\n\v\f\r"

for filename in itertools.product(options, repeat=filename_length):
        open(dir + ''.join(filename), "a").close()

สิ่งนี้จะสร้างผลิตภัณฑ์ทั้งหมดของความยาว 3 สำหรับ 7 ตัวอักษร คณิตศาสตร์ระดับมัธยมศึกษาตอนปลายบอกเราว่าควรเป็น 343 ไฟล์ นั่นควรจะพิมพ์ได้อย่างรวดเร็วจริง ๆ ลองดู:

time for f in *; do stat --format='%n' "./$f" >/dev/null; done
real    0m0.508s
user    0m0.051s
sys 0m0.480s

ทีนี้ลองใช้วิธีแก้ปัญหาแรกของคุณกันเพราะฉันไม่สามารถรับมันได้

eval set -- $(ls -1qrR ././ | tr ' ' '?' |
sed -e '\|^\(\.\{,1\}\)/\.\(/.*\):|{' -e \
        's//\1\2/;\|/$|!s|.*|&/|;h;s/.*//;b}' -e \
        '/..*/!d;G;s/\(.*\)\n\(.*\)/\2\1/' -e \
        "s/'/'\\\''/g;s/.*/'&'/;s/?/'[\"?\$IFS\"]'/g" |
uniq)

สิ่งที่นี่เพื่อทำงานบน Linux มิ้นท์ 16 (ซึ่งฉันคิดว่าพูดไดรฟ์สำหรับการใช้งานของวิธีการนี้)

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

ดังนั้นตอนนี้จะนานแค่ไหน

time for f in $(ls -1q | tr " " "?") ; do stat --format='%n' "./$f" >/dev/null; done

ใช้? ฉันไม่รู้จริง ๆ ใช้เวลาสักครู่เพื่อตรวจสอบชื่อไฟล์ 343 ^ 343 - ฉันจะบอกคุณหลังจากการเสียชีวิตของเอกภพ


6
แน่นอนดังที่ได้กล่าวไว้ในความคิดเห็นภายใต้คำตอบอื่นข้อความ "... คุณแสดงให้เห็นว่าคุณสามารถสร้างโซลูชันที่แยกวิเคราะห์และให้ผลลัพธ์ที่ถูกต้อง ... " จริง ๆ แล้วไม่จริง
สัญลักษณ์แทน

26

ระบุความตั้งใจของ OP แล้ว

คำนำและเหตุผลดั้งเดิมของคำตอบอัพเดทเมื่อวันที่ 2015-05-18

mikeserv (สหกรณ์) กล่าวในการปรับปรุงใหม่ล่าสุดในคำถามของเขา: "ผมไม่คิดว่ามันเป็นความอัปยศที่ว่าครั้งแรกที่ผมถามคำถามนี้จะชี้ให้เห็นแหล่งที่มาของข้อมูลที่ผิด,และโชคไม่ดีที่คำตอบ upvoted ที่สุดที่นี่เป็นส่วนใหญ่เข้าใจผิด "

โอเค; ฉันรู้สึกว่ามันค่อนข้างน่าละอายที่ฉันใช้เวลามากมายในการพยายามหาวิธีที่จะอธิบายความหมายของฉันเท่านั้นที่จะพบว่าเมื่อฉันอ่านคำถามอีกครั้ง คำถามนี้จบลงด้วย "[สร้าง] การอภิปรายมากกว่าคำตอบ" และจบลงด้วยการชั่งในที่~ 18K ของข้อความ (สำหรับคำถามอยู่คนเดียวเพียงเพื่อให้มีความชัดเจน) ซึ่งจะยาวแม้สำหรับบล็อกโพสต์

แต่ StackExchange ไม่ใช่ Soapbox ของคุณและไม่ใช่บล็อกของคุณ อย่างไรก็ตามคุณได้ใช้อย่างน้อยสองอย่าง ผู้คนใช้เวลามากในการตอบ "To-Point-Out" ของคุณแทนการตอบคำถามจริงของผู้คน ณ จุดนี้ฉันจะตั้งค่าสถานะคำถามว่าไม่เหมาะสมสำหรับรูปแบบของเราเนื่องจาก OP ได้ระบุไว้อย่างชัดเจนว่ามันไม่ได้ตั้งใจจะเป็นคำถามเลย

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

เป็นกฎทั่วไป ...

โปรดให้อภัยขอบขรุขระที่เหลืออยู่ ฉันใช้เวลามากเกินไปในเรื่องนี้ไปแล้ว ... แทนที่จะอ้าง OP โดยตรง (ตามที่ตั้งใจไว้เดิม) ฉันจะพยายามสรุปและถอดความ

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

โพสต์ต้นฉบับถามหลายวิธีทำไมบทความต่าง ๆ ให้คำแนะนำเช่น«อย่าแยกวิเคราะห์lsผลลัพธ์»หรือ«คุณไม่ควรแยกวิเคราะห์lsผลลัพธ์»และอื่น ๆ

ข้อเสนอแนะของฉันในการแก้ไขปัญหานี้คือกรณีของข้อความประเภทนี้เป็นเพียงตัวอย่างของสำนวนที่ใช้ถ้อยคำในรูปแบบที่แตกต่างกันเล็กน้อยซึ่งการจับคู่สัมบูรณ์จะถูกจับคู่กับความต้องการ [เช่น«ไม่ [เคย] X » « [คุณควร] Y เสมอ, « [ใครควร] ไม่เคย Z »] เพื่อจัดทำงบที่ตั้งใจจะใช้เป็นกฎหรือแนวทางทั่วไปโดยเฉพาะอย่างยิ่งเมื่อมอบให้กับผู้ที่ยังใหม่กับเรื่องมากกว่าที่จะตั้งใจให้เป็นความจริงแน่นอนรูปแบบที่ชัดเจนของข้อความเหล่านั้นแม้จะมี

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

และนั่นคือเมื่อผู้เชี่ยวชาญอาจเลือกที่จะทำสิ่งที่ละเมิด "กฎ" แต่นั่นจะไม่ทำให้พวกเขาน้อยลง "กฎ"

และในหัวข้อที่ใกล้เคียง: ในมุมมองของฉันเพียงเพราะผู้เชี่ยวชาญอาจสามารถละเมิดกฎนี้โดยไม่ถูกตีอย่างสมบูรณ์ฉันไม่เห็นวิธีที่คุณสามารถบอกผู้เริ่มต้นว่า "บางครั้ง" โอเคที่จะแยกlsออกเพราะมันไม่ได้ หรืออย่างน้อยก็ไม่เหมาะสำหรับผู้เริ่มต้นที่จะทำเช่นนั้น

คุณมักจะเอาเบี้ยไปไว้ตรงกลาง ในการเปิดหนึ่งชิ้นหนึ่งขยับ; ปราสาทอย่างเร็วที่สุด; อัศวินก่อนบาทหลวง; อัศวินบนขอบนั้นช่างน่ากลัว และตรวจสอบให้แน่ใจเสมอว่าคุณสามารถดูการคำนวณของคุณได้ตลอดไป! (ขออภัยขอโทษที่เหนื่อยนั่นคือสำหรับหมากรุก StackExchange)

กฎหมายถึงจะแตก?

เมื่ออ่านบทความเกี่ยวกับเรื่องที่กำหนดเป้าหมายไว้หรือน่าจะอ่านโดยผู้เริ่มต้นบ่อยครั้งที่คุณจะเห็นสิ่งต่าง ๆ ดังนี้:

  • "คุณไม่ควรที่เคยทำเอ็กซ์"
  • "ไม่ทำ Q!"
  • "อย่าทำซี"
  • "เราควรทำ Y!"
  • "C ไม่ว่าอะไรจะเกิดขึ้น"

ในขณะที่ข้อความเหล่านี้ดูเหมือนจะระบุกฎที่แน่นอนและเป็นอมตะ แต่พวกเขาไม่ได้ นี่เป็นวิธีหนึ่งในการระบุกฎทั่วไป [อาคา "แนวทาง", "กฎแห่งหัวแม่มือ", "พื้นฐาน", ฯลฯ ) ซึ่งอย่างน้อยก็มีเนื้อหาที่เหมาะสมอย่างน้อยหนึ่งวิธีที่จะกล่าวถึงผู้เริ่มต้นที่อาจอ่านบทความเหล่านั้น อย่างไรก็ตามเพียงเพราะพวกเขาถูกระบุว่าเป็นสัมบูรณ์กฎอย่างแน่นอนไม่ผูกมืออาชีพและผู้เชี่ยวชาญ [ใครเป็นคนที่สรุปกฎดังกล่าวในสถานที่แรกเป็นวิธีการบันทึกและถ่ายทอดความรู้ที่ได้รับเมื่อพวกเขาจัดการกับการเกิดซ้ำ ปัญหาในยานโดยเฉพาะ]

แน่นอนว่ากฎเหล่านั้นจะไม่เปิดเผยว่าผู้เชี่ยวชาญจะจัดการกับปัญหาที่ซับซ้อนหรือซับซ้อนยิ่งขึ้นซึ่งกล่าวว่ากฎเหล่านั้นขัดแย้งกัน หรือความกังวลที่นำไปสู่กฎในตอนแรกก็ไม่ได้ใช้ ผู้เชี่ยวชาญไม่กลัวที่จะ (หรือไม่ควรกลัว!) เพียงแค่ทำลายกฎที่พวกเขารู้ว่าไม่มีเหตุผลในสถานการณ์เฉพาะ ผู้เชี่ยวชาญกำลังเผชิญกับความสมดุลของความเสี่ยงและความกังวลในงานฝีมืออยู่ตลอดเวลาและต้องใช้วิจารณญาณในการเลือกที่จะทำลายกฎเหล่านั้นโดยต้องรักษาสมดุลของปัจจัยต่าง ๆ และไม่สามารถพึ่งเพียงตารางของกฎที่จะปฏิบัติตาม ใช้Gotoเป็นตัวอย่าง: มีการยาวที่เกิดขึ้นในการอภิปรายไม่ว่าจะเป็นอันตราย (ใช่ไม่เคยใช้ gotos; D)

ข้อเสนอ Modal

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

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

ดังนั้นกฎทั่วไปไม่ใช่ข้อเสนอแบบโมดัลสัมบูรณ์ที่ปรากฏอยู่บนพื้นผิว แต่เป็นวิธีย่อของการให้กฎกับมาตรฐานสำเร็จรูปโดยนัยบางอย่างดังต่อไปนี้:

หากคุณไม่มีความสามารถที่จะบอกได้ว่าแนวทางนี้ไม่ถูกต้องในบางกรณีและพิสูจน์ด้วยตัวคุณเองว่าคุณถูกต้องแล้ว $ {RULE}

โดยแน่นอนคุณสามารถแทนที่ "ไม่แจงlsผลลัพธ์" แทน $ {RULE} :)

โอ้ใช่! สิ่งที่เกี่ยวกับการแยกวิเคราะห์lsผลลัพธ์?

เอาล่ะ ... ฉันคิดว่ามันค่อนข้างชัดเจนว่ากฎนี้เป็นกฎที่ดี ก่อนอื่นต้องเข้าใจกฎที่แท้จริงว่าสำนวนตามที่อธิบายไว้ข้างต้น ...

แต่ยิ่งไปกว่านั้นไม่ใช่แค่ว่าคุณจะต้องเก่งมากในการเขียนสคริปต์เชลล์เพื่อรู้ว่ามันสามารถใช้งานไม่ได้หรือไม่ในบางกรณี นอกจากนี้ยังใช้ความสามารถมากพอที่จะบอกคุณว่าผิดเมื่อคุณพยายามทำลายมันในการทดสอบ! และฉันพูดอย่างมั่นใจว่าผู้ชมส่วนใหญ่ที่เป็นไปได้ของบทความดังกล่าว (ให้คำแนะนำเช่น«อย่าแยกวิเคราะห์ผลลัพธ์ของls! ») ไม่สามารถทำสิ่งเหล่านั้นได้และผู้ที่มีทักษะดังกล่าวจะรู้ตัวว่า พวกเขาคิดออกเองและเพิกเฉยต่อกฎ

แต่ ... เพียงแค่ดูที่คำถามนี้และแม้แต่คนที่อาจมีทักษะคิดว่ามันเป็นการเรียกที่ไม่ดี และผู้เขียนคำถามใช้ความพยายามเพียงใดถึงจะเป็นตัวอย่างที่ดีที่สุดในปัจจุบัน! ฉันรับประกันว่าคุณจะมีปัญหาที่ยากเย็นแสนเข็ญ 99% ของผู้คนที่นั่นจะทำให้มันผิดและด้วยผลลัพธ์ที่แย่มาก ! แม้ว่าวิธีการที่ตัดสินใจจะกลายเป็นวิธีที่ดี จนกว่าจะมีการlsแยกความคิด(หรืออื่น ๆ ) กลายเป็นลูกบุญธรรมโดย IT / ผู้พัฒนาโดยรวมทนต่อการทดสอบจำนวนมาก (โดยเฉพาะการทดสอบเวลา) และในที่สุดก็สามารถที่จะเปลี่ยนเป็น 'เทคนิคทั่วไป' ได้ ผู้คนจำนวนมากอาจลองใช้และทำให้มันผิด ... ด้วยผลที่ตามมาหายนะ

ดังนั้นผมจะย้ำเป็นครั้งสุดท้าย .... ที่โดยเฉพาะอย่างยิ่งในกรณีนี้ , ที่เป็นเหตุผลที่ " ไม่เคยแยกlsเอาท์พุท!" เป็นวิธีที่เหมาะสมในการวลี

[ปรับปรุง 2014-05-18: ชี้แจงเหตุผลสำหรับคำตอบ (ด้านบน) เพื่อตอบสนองต่อความคิดเห็นจาก OP; การเพิ่มต่อไปนี้เป็นการตอบสนองการเพิ่มเติมของ OP จากคำถามเมื่อวาน]

[อัพเดต 2014-11-10: เพิ่มส่วนหัวและจัดโครงสร้างใหม่ / ปรับโครงสร้างเนื้อหาใหม่ และยัง: การจัดรูปแบบ, rewording, clarifying และ um ... "รัดกุม -Fying" ... ฉันตั้งใจที่จะทำความสะอาดแม้ว่ามันจะกลายเป็นการทำงานซ้ำ ฉันทิ้งมันไว้ในสถานะที่เป็นขอโทษดังนั้นฉันส่วนใหญ่พยายามที่จะให้มันมีคำสั่ง ฉันรู้สึกว่ามันสำคัญมากที่ต้องออกจากส่วนแรกโดยไม่ทำลาย ดังนั้นมีเพียงสองการเปลี่ยนแปลงเล็กน้อยที่นั่นซ้ำซ้อน 'แต่' ถูกลบและ 'ที่' เน้นไว้]

originally ตอนแรกฉันตั้งใจจะทำสิ่งนี้เป็นการอธิบายเฉพาะของฉันเอง แต่ตัดสินใจที่จะเพิ่มภาพสะท้อนอื่น ๆ

‡ดูhttps://unix.stackexchange.com/tourสำหรับแนวทางในการโพสต์


2
ไม่มีทางที่จะไม่สำนึกผิด นี่ไม่ใช่คำตอบสำหรับสิ่งใด
mikeserv

1
อืมมม ดีฉันไม่รู้ว่าคำตอบนี้จะเป็นที่น่าพอใจแต่ฉันอย่างไม่ได้คาดหวังว่ามันจะเป็นความขัดแย้ง และฉันก็ไม่ได้โต้แย้งว่า 'ไม่เคย' เป็นไปตามสำนวน; แต่นั่น "อย่าทำ X!" เป็นสำนวนการใช้งาน ฉันเห็นกรณีทั่วไปสองกรณีที่สามารถแสดงว่า 'ไม่เคย / ไม่แยกวิเคราะห์ls!' คือคำแนะนำที่ถูกต้อง: 1. แสดงให้เห็นถึง (เพื่อความพึงพอใจของคุณ) ว่าทุกกรณีการใช้งานที่หนึ่งอาจแยกวิเคราะห์lsออกมีโซลูชั่นอื่นที่มีอยู่ที่เหนือกว่าในบางวิธีโดยไม่ต้องทำเช่นนั้น 2. แสดงให้เห็นว่าในกรณีที่อ้างถึงคำสั่งนั้นไม่ได้เป็นตัวอักษรที่แท้จริง
shelleybutterfly

เมื่อมองคำถามของคุณอีกครั้งฉันเห็นว่าคุณพูดถึง "ไม่ ... " มากกว่า "ไม่ ... " ซึ่งเป็นการวิเคราะห์ของคุณดังนั้นฉันจะอธิบายให้ชัดเจนในจุดนั้นเช่นกัน ณ จุดนี้มีวิธีการแก้ปัญหาประเภทแรกอยู่แล้วซึ่งแสดงให้เห็น / อธิบายถึงความพึงพอใจของคุณดังนั้นฉันจึงไม่ได้เจาะลึกเรื่องนั้นมากนัก แต่ฉันจะพยายามชี้แจงคำตอบของฉันเล็กน้อย: เหมือนฉันบอกว่าฉันไม่ได้พยายามที่จะโต้เถียง (หรือเผชิญหน้า)!
shelleybutterfly

1
ฉันควรล้างโพสต์นั้น ยังไม่เคยเป็นไม่ได้วิธีการที่เหมาะสมกับวลีมัน มันไร้สาระเล็กน้อยที่ผู้คนคิดว่าพวกเขามีคุณสมบัติที่จะบอกคนอื่นไม่ได้หรือไม่ - เพียงแค่บอกพวกเขาว่าคุณไม่คิดว่ามันจะได้ผลและทำไม แต่คุณรู้ว่าอะไรจะได้ผลและทำไม lsเป็นโปรแกรมคอมพิวเตอร์ - คุณสามารถแยกเอาท์พุทคอมพิวเตอร์
mikeserv

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

16

เป็นไปได้หรือไม่ในการแยกวิเคราะห์ผลลัพธ์lsในบางกรณี? แน่ใจ แนวคิดของการแยกรายการหมายเลขไอโหนดจากไดเรกทอรีเป็นตัวอย่างที่ดี - ถ้าคุณรู้ว่าการlsสนับสนุนของการนำไปใช้งานของคุณ-qดังนั้นแต่ละไฟล์จะสร้างผลลัพธ์หนึ่งบรรทัดที่แน่นอนและทั้งหมดที่คุณต้องการคือหมายเลขไอโหนดแยกพวกมันออกจากls -Rai1qการส่งออกเป็นทางออกที่เป็นไปได้อย่างแน่นอน แน่นอนถ้าผู้เขียนไม่เห็นคำแนะนำเช่น "ไม่เคยแยกเอาท์พุทของ ls" มาก่อนเขาอาจจะไม่คิดถึงชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ในพวกเขาและอาจจะทิ้ง 'q' เป็นผลและ รหัสจะแตกอย่างละเอียดในกรณีขอบนั้นดังนั้นแม้ในกรณีที่lsผลลัพธ์ของการแยกวิเคราะห์มีเหตุผลคำแนะนำนี้ยังคงมีประโยชน์

จุดที่กว้างขึ้นคือเมื่อเป็นมือใหม่เปลือกสคริปต์พยายามที่จะมีการร่างสคริปต์ออก (ตัวอย่างเช่น) สิ่งที่เป็นไฟล์ที่ใหญ่ที่สุดในไดเรกทอรีหรือสิ่งที่เป็นไฟล์แก้ไขล่าสุดในไดเรกทอรีสัญชาตญาณแรกของเขาคือการแยกlsของ เอาท์พุท - เข้าใจได้เพราะlsเป็นหนึ่งในคำสั่งแรกที่มือใหม่เรียนรู้

น่าเสียดายที่สัญชาตญาณนั้นผิดและวิธีนั้นก็พัง น่าเสียดายยิ่งกว่านั้นมันใช้งานไม่ได้ - จะใช้เวลาส่วนใหญ่ แต่ไม่สามารถใช้งานได้ในบางกรณีที่บางคนอาจใช้ประโยชน์จากความรู้เกี่ยวกับโค้ด

newbie อาจคิดว่าls -s | sort -n | tail -n 1 | awk '{print $2}'เป็นวิธีการรับไฟล์ที่ใหญ่ที่สุดในไดเรกทอรี และใช้งานได้จนกว่าคุณจะมีไฟล์ที่มีช่องว่างในชื่อ

ตกลงแล้วจะเป็นls -s | sort -n | tail -n 1 | sed 's/[^ ]* *[0-9]* *//'อย่างไร ทำงานได้ดีจนกว่าคุณจะมีไฟล์ที่มีบรรทัดใหม่ในชื่อ

การเพิ่ม-qในการlsขัดแย้งช่วยเมื่อมีการขึ้นบรรทัดใหม่ในชื่อไฟล์หรือไม่? มันอาจดูเหมือนเป็นเช่นนั้นจนกว่าคุณจะมี 2 ไฟล์ที่แตกต่างกันซึ่งมีตัวอักษรที่ไม่สามารถพิมพ์ได้ในจุดเดียวกันในชื่อไฟล์จากนั้นlsเอาต์พุตจะไม่อนุญาตให้คุณแยกแยะว่าไฟล์ใดมีขนาดใหญ่ที่สุด ที่แย่กว่านั้นคือเพื่อขยาย "?" เขาอาจหันไปใช้กระสุนของเขาevalซึ่งจะทำให้เกิดปัญหาหากเขาพบไฟล์ชื่อเช่น

foo`/tmp/malicious_script`bar

ไม่--quoting-style=shellช่วย (ถ้าคุณlsแม้สนับสนุน)? ไม่ยังคงแสดงอยู่? สำหรับตัวละครที่ไม่สามารถพิมพ์ได้ดังนั้นมันยังคลุมเครือซึ่งการแข่งขันหลายรายการนั้นใหญ่ที่สุด --quoting-style=literal? ไม่เหมือนกัน --quoting-style=localeหรือ--quoting-style=cอาจช่วยได้ถ้าคุณต้องการพิมพ์ชื่อของไฟล์ที่ใหญ่ที่สุดอย่างไม่น่าสงสัย แต่อาจไม่ใช่ถ้าคุณต้องการทำบางสิ่งกับไฟล์ในภายหลัง - มันจะเป็นกลุ่มของรหัสเพื่อยกเลิกการอ้างอิงและกลับไปยังชื่อไฟล์จริงดังนั้น ที่คุณสามารถส่งต่อให้พูด gzip

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

หรือแม้แต่การใช้เครื่องมือเชลล์อื่น ๆ - จากส่วนบนของหัวฉันคิดว่านี่น่าจะเป็นการหลอกลวง:

find . -type f -printf "%s %f\0" | sort -nz | awk 'BEGIN{RS="\0"} END{sub(/[0-9]* /, "", $0); print}'

และควรมีอย่างน้อยพกพาตามที่เป็น--quoting-styleอยู่


โอ้จริงเกี่ยวกับขนาด - ฉันอาจจะทำอย่างนั้นถ้าฉันพยายาม - ฉันควร? อิ่มนะเหนื่อยหรือสิ่งทั้งหมดนี้ - ฉันชอบคำตอบของคุณเพราะคุณไม่ได้บอกว่าไม่สามารถหรือไม่หรือไม่แต่ที่จริงยกตัวอย่างอาจจะทำไมไม่และสามารถเทียบเคียงวิธีอื่น - ขอบคุณ
mikeserv

ฉันคิดว่าถ้าคุณลองคุณจะพบว่ามันยากกว่าที่คุณคิด ใช่ฉันขอแนะนำให้ลอง ฉันยินดีที่จะให้ชื่อไฟล์ที่จะทำลายสำหรับคุณตราบใดที่ฉันสามารถคิดถึงพวกเขา :)
godlygeek

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
terdon

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