มีอีกทางเลือกหนึ่งที่กระชับกว่าการ pip ไปยัง wc สำหรับการนับไฟล์ในไดเรกทอรี


12

ถ้าเป็นเช่นls -1 target_dir | wc -lนั้นฉันจะได้จำนวนไฟล์ในไดเรกทอรี ฉันพบว่ามันค่อนข้างยุ่งยาก มีวิธีที่สง่างามหรือรวบรัดมากขึ้นหรือไม่?


2
คุณไม่ต้องการ "-1" เมื่อทำการไพพ์ไปที่ wc
Steve

lsให้นับรวมแล้วแล้วจะเป็นls -l | head -1อย่างไร ทำให้เป็นชื่อแทนหากคุณต้องการบางสิ่งที่สั้นกว่า
Daniel Wagner

2
@DanielWagner เอาต์พุต "total: nnn" โดยls -lระบุขนาดรวมของไฟล์ไม่ใช่จำนวนไฟล์
David Richerby

2
โปรดจำไว้ว่าls | wc -lจะทำให้คุณนับผิดถ้าชื่อไฟล์ใด ๆ มีการขึ้นบรรทัดใหม่
chepner

สิ่งนี้ขึ้นอยู่กับระบบไฟล์และนับไดเรกทอรี + 2 ในไดเรกทอรี คำตอบมี 2 พิเศษ (ตามที่นับเองและผู้ปกครอง) stat -c %h .ให้ข้อมูลเหมือนกับls -ld . | cut -d" " -f 2
ctrl-alt-delor

คำตอบ:


12

สมมติว่าทุบตี 4+ (อูบุนตูที่รองรับเวอร์ชั่นใดก็ได้):

num_files() (
    shopt -s nullglob
    cd -P -- "${1-.}" || return
    set -- *
    echo "$#"
)

num_files [dir]เรียกว่าเป็น dirเป็นทางเลือกมิฉะนั้นจะใช้ไดเรกทอรีปัจจุบัน เวอร์ชันดั้งเดิมของคุณจะไม่นับไฟล์ที่ซ่อนดังนั้นจึงไม่เป็นเช่นนั้น หากคุณต้องการที่ก่อนshopt -s dotglobset -- *

ตัวอย่างดั้งเดิมของคุณไม่เพียง แต่นับไฟล์ทั่วไปเท่านั้น แต่ยังรวมถึงไดเรกทอรีและอุปกรณ์อื่น ๆ ด้วย - หากคุณต้องการไฟล์ปกติ (รวมถึง symlink ไปยังไฟล์ปกติ) คุณจะต้องตรวจสอบ:

num_files() (
    local count=0

    shopt -s nullglob
    cd -P -- "${1-.}" || return
    for file in *; do
        [[ -f $file ]] && let count++
    done
    echo "$count"
)

หากคุณมี GNU พบสิ่งนี้เป็นตัวเลือก (โปรดทราบว่านี่รวมถึงไฟล์ที่ซ่อนซึ่งคำสั่งดั้งเดิมของคุณไม่ได้ทำ):

num_files() {
    find "${1-.}" -maxdepth 1 -type f -printf x | wc -c
}

(เปลี่ยน-typeเป็น-xtypeถ้าคุณต้องการนับ symlink เป็นไฟล์ปกติ)


จะไม่setล้มเหลวหากมีไฟล์จำนวนมาก? ฉันคิดว่าคุณอาจต้องใช้xargsและบางข้อสรุปรหัสเพื่อให้งานในกรณีทั่วไป
l0b0

1
นอกจากนี้shopt -s dotglobหากคุณต้องการนับไฟล์.ที่จะนับ
Digital Trauma

1
@ l0b0 ฉันไม่คิดว่าจะล้มเหลวภายใต้สถานการณ์เช่นนี้เนื่องจากเราไม่ได้ทำจริงset execหากต้องการปัญญาในระบบของฉันgetconf ARG_MAXให้ผลตอบแทน 262,144 แต่ถ้าฉันทำtest_arg_max() { set -- {1..262145}; echo $#; }; test_arg_maxมันก็ตอบอย่างมีความสุข 262145
kojiro

@DavidRicherby -maxdepthไม่ใช่ POSIX
Chris Down

4
@MichaelMartinez การเขียนโค้ดที่ชัดเจนไม่ใช่การแทนที่การเขียนโค้ดที่ถูกต้อง
Chris Down

3

f=(target_dir/*);echo ${#f[*]}

ทำงานอย่างถูกต้องสำหรับไฟล์ที่มีช่องว่างบรรทัดใหม่ ฯลฯ ในชื่อ


คุณสามารถให้บริบทได้ไหม ควรไปในสคริปต์ทุบตี?
codecowboy

มันสามารถ. คุณสามารถวางมันลงในเปลือกได้โดยตรง เวอร์ชันนั้นคิดว่าคุณต้องการไดเรกทอรีปัจจุบัน ฉันได้แก้ไขแล้วดังนั้นจึงใกล้กับคำถามของคุณมากขึ้น โดยพื้นฐานแล้วมันสร้างตัวแปรเชลล์อาเรย์ที่มีไฟล์ทั้งหมดในไดเรกทอรีจากนั้นพิมพ์จำนวนของอาเรย์นั้น ควรทำงานในเชลล์ที่มีอาร์เรย์ - bash, ksh, zsh ฯลฯ - แต่อาจไม่ใช่ sh / ash / dash ธรรมดา
Aaron Davies

2

lsเป็นหลายคอลัมน์เฉพาะเมื่อมันส่งออกโดยตรงไปยังสถานีคุณสามารถลบตัวเลือก "-1" คุณสามารถลบwcตัวเลือก "-l" อ่านค่าแรกเท่านั้น (วิธีแก้ปัญหาแบบขี้เกียจไม่ใช้สำหรับหลักฐานทางกฎหมาย การสืบสวนคดีอาชญากรรมภารกิจสำคัญยุทธวิธีปฏิบัติการ .. )

ls target | wc 

5
สิ่งนี้ล้มเหลวสำหรับชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่
l0b0

@Emmanuel คุณจะต้องแยกวิเคราะห์ผลลัพธ์ของคุณwcเพื่อให้ได้จำนวนไฟล์ในกรณีที่ไม่สำคัญดังนั้นนี่จะเป็นวิธีแก้ปัญหาได้อย่างไร
l0b0

@Emmanuel สิ่งนี้อาจล้มเหลวได้หากtargetเป็นรูปกลมซึ่งเมื่อขยายแล้วจะรวมถึงบางสิ่งที่เริ่มต้นด้วยเครื่องหมายยัติภังค์ ยกตัวอย่างเช่นทำให้ไดเรกทอรีใหม่ไปเป็นมันและไม่touch -- {1,2,3,-a}.txt && ls *|wc(NB ใช้rm -- *.txtในการลบไฟล์เหล่านั้น.)
เดวิด Richerby

คุณหมายถึงwc -lอะไร มิฉะนั้นคุณจะได้รับการขึ้นบรรทัดใหม่คำและไบต์ของlsเอาต์พุต นั่นคือสิ่งที่ David Richerby พูดว่า: คุณต้องแยกมันอีกครั้ง
erik

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

2

หากคุณกำลังรวบรัด (มากกว่าความถูกต้องแน่นอนเมื่อจัดการกับไฟล์ที่มีบรรทัดใหม่ในชื่อของพวกเขา ฯลฯ ) ฉันขอแนะนำให้ใช้นามแฝงwc -lเป็นlc("การนับบรรทัด"):

$ alias lc='wc -l'
$ ls target_dir|lc

ดังที่คนอื่น ๆ ได้กล่าวไว้คุณไม่จำเป็นต้องใช้-1ตัวเลือกlsเนื่องจากเป็นอัตโนมัติเมื่อlsเขียนไปที่ไพพ์ (เว้นแต่คุณจะใช้lsนามแฝงว่าจะใช้โหมดคอลัมน์เสมอฉันเคยเห็นมาก่อน แต่ไม่บ่อยนัก)

lcนามแฝงค่อนข้างสะดวกในทั่วไปและสำหรับคำถามนี้ถ้าคุณมองไปที่ "นับไดเรกทอรีปัจจุบัน" กรณีls|lcเป็นเรื่องที่สั้นที่สุดเท่าที่คุณจะได้รับ


2

จนถึงตอนนี้แอรอนเป็นวิธีเดียวที่สั้นกระชับกว่าของคุณเอง แนวทางที่ถูกต้องมากขึ้นอาจมีลักษณะดังนี้:

ls -aR1q | grep -Ecv '^\./|/$|^$'

ที่แสดงรายการไฟล์ทั้งหมดซ้ำ - ไม่ใช่ไดเรกทอรี - หนึ่งไฟล์ต่อบรรทัดรวมถึง. doc ไฟล์ใต้ไดเรกทอรีปัจจุบันโดยใช้ shell globs ตามความจำเป็นเพื่อแทนที่อักขระที่ไม่สามารถพิมพ์ได้ grep กรองรายการไดเรกทอรีหลักหรือ .. หรือ * / หรือบรรทัดว่าง - ดังนั้นควรมีเพียงหนึ่งบรรทัดต่อไฟล์ - จำนวนรวมที่ grep ส่งคืนให้คุณ หากคุณต้องการรวมไดเรกทอรีลูกไว้ด้วย:

ls -aR1q | grep -Ecv '^\.{1,2}/|^$'

ลบ-Rในกรณีใดกรณีหนึ่งถ้าคุณไม่ต้องการผลลัพธ์ซ้ำ


1
findฉันมักจะชอบที่จะทำชนิดของสิ่งที่มี หากคุณต้องการเพียงการนับสิ่งนี้ควรใช้งานได้: find -mindepth 1 -maxdepth 1 -printf '\n'|wc -l(ลบการควบคุมความลึกเพื่อให้ได้ผลลัพธ์แบบเรียกซ้ำ)
Aaron Davies

@AaronDavies - มันใช้งานไม่ได้ ใส่บรรทัดใหม่ในชื่อไฟล์เหล่านั้นและดูด้วยตัวคุณเอง นอกจากนี้ในการทำสิ่งเดียวกันกับที่คุณทำ: find . \! -name . -prune | wc -l- ซึ่งยังใช้งานไม่ได้แน่นอน
mikeserv

1
ฉันไม่ทำตาม - printfคำสั่งพิมพ์สตริงคงที่ (ขึ้นบรรทัดใหม่) ที่ไม่รวมชื่อไฟล์เลยดังนั้นผลลัพธ์จึงไม่ขึ้นอยู่กับชื่อไฟล์แปลก ๆ เคล็ดลับนี้ไม่สามารถทำได้เลยด้วยสิ่งfindที่ไม่สนับสนุนprintfแน่นอน
Aaron Davies

@AaronDavies - โอ้จริง ฉันคิดว่ารวมชื่อไฟล์แล้ว แต่สามารถทำได้อย่างแน่นอนแม้ว่า:find .//. \!. -name . -prune | grep -c '^\.//\.'
mikeserv

! สดใส /เป็นตัวละครอื่น ๆ ที่ไม่สามารถปรากฏในชื่อไฟล์ได้.//.ลำดับจึงรับประกันได้ว่าจะปรากฏหนึ่งครั้งสำหรับทุกไฟล์ใช่ไหม คำถามคู่แม้ว่า - ทำไม.//.และทำไม-prune? สิ่งนี้จะแตกต่างจากเมื่อfind . \! -name . | grep -c '^\.'ใด (ฉันถือว่า.คุณ\!.เป็นตัวพิมพ์ผิด)
Aaron Davies
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.