ใช้ basename เพื่อวิเคราะห์รายการพา ธ ที่เก็บไว้ในไฟล์


9

ฉันใช้ Mac OSX และพยายามใช้บรรทัดคำสั่งเพื่อค้นหาจำนวนไฟล์ที่ฉันมีด้วยชื่อเดียวกัน

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

find ~ -type f -name "*" -print | basename | sort | uniq -d > duplicate_files

มันไม่ทำงาน! เมื่อฉันทำต่อไปนี้:

find ~ -type f -name "*" -print > duplicate_files

จากนั้น Duplicate_files จะมีพา ธ ของไฟล์ทั้งหมดของฉัน ดังนั้นฉันคิดว่าปัญหาเกิดขึ้นกับbasename- ไม่ยอมรับอินพุตมาตรฐาน ฉันลองทำสิ่งต่อไปนี้:

basename $(find ~ -type f -name "*" -print) > duplicate_files

แต่ดูเหมือนจะไม่ทำงานอีกครั้ง การค้นหาทางอินเทอร์เน็ตดูเหมือนจะไม่ทำให้เกิดความสุข ความคิดใด ๆ ยินดีต้อนรับมากที่สุด

คำตอบ:


16

basename ทำงานบนอาร์กิวเมนต์บรรทัดคำสั่งของมันมันไม่ได้อ่านจากอินพุตมาตรฐาน

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

find ~ -type f | sed 's!.*/!!' | sort | uniq -d

มันอาจมีประโยชน์มากกว่าในการติดตามตำแหน่งของไฟล์ การเรียงลำดับตามชื่อช่วยให้ค้นหาตำแหน่งที่ซ้ำได้ง่ายขึ้น แต่sortไม่มีตัวเลือกให้ใช้ฟิลด์สุดท้าย สิ่งที่คุณสามารถทำได้คือคัดลอก/ฟิลด์ -separated สุดท้ายไปยังจุดเริ่มต้นจากนั้นเรียงลำดับแล้วใช้การประมวลผลแบบ ad hoc awk เล็กน้อยเพื่อแยกและนำเสนอรายการที่ซ้ำกัน

find ~ -type f |
sed 's!.*/\(.*\)!\1/&!' |   # copy the last field to the beginning
sort -t/ -k1,1 |
cut -d/ -f2- |   # remove the extra first field (could be combined with awk below)
awk -F / '{
    if ($NF == name) {
        if (previous != "") {print previous; previous = ""}
        print
    } else {
        previous = $0
        name = $NF
    }
'

(โปรดทราบว่าฉันถือว่าชื่อไฟล์ของคุณไม่มีอักขระขึ้นบรรทัดใหม่)


ขอบคุณสุด ๆ นี่คือสิ่งที่ฉันพยายามจะทำ ... มีประโยชน์มาก
JohnB

7

ทำไมไม่ใช้findคุณสมบัติในตัวเพื่อส่งออกเพียงชื่อไฟล์:

find ~ -type f -printf '%f\n' | sort | uniq -c

(ถือว่า GNU find) หรืออย่างน้อยก็เป็นอย่างนี้:

find ~ -exec basename {} \; | sort | uniq -c

basename ไม่สามารถอ่านผ่านไพพ์หรือประมวลผลหลายไฟล์พร้อมกัน

PS ไม่จำเป็นต้องระบุ-name '*'หากคุณต้องการแสดงรายการไฟล์ทั้งหมด นี่เป็นตัวเลือกเริ่มต้น


ขอบคุณ - '-printf' ไม่ทำงานสำหรับ OS X UNIX
JohnB

basename: unknown primary or operatorและเมื่อผมลองรุ่นที่สองที่ฉันได้รับ ขอบคุณสำหรับเคล็ดลับใน-name "*"
JohnB

มันแปลกมาก ฉันสามารถดู-printfได้ในหน้า man posix เกี่ยวกับข้อผิดพลาดด้วยวิธีที่สองมันเป็นสาเหตุของการพิมพ์ผิดในคำตอบของฉัน แก้ไขแล้ว. คุณช่วยลองอีกครั้งได้ไหม
เร่ง

นอกจากนี้ยังมีฉันได้รับ-printf -printf: unknown primary or operatorนอกจากนี้เมื่อผมตรวจสอบยูนิกซ์ในหนังสืออ้างอิงกะลามันแสดงรายการเป็นตัวเลือกที่ GNU / Linux - ไม่พูดอะไรเกี่ยวกับ OSX
JohnB

1
ที่จริงแล้วแหล่งข้อมูลที่ดีที่สุดจะอยู่man findในคอนโซลของคุณ :)
เร่ง

4

ดูเหมือนว่าจะทำงานกับฉันใน OSX:

find ~ -type f -exec basename -a {} + | sort | uniq -d

ใช่ - นี่เป็นคำขอบคุณที่ยิ่งใหญ่ - จากความสนใจสิ่งที่+มีความหมายในคำสั่ง?
JohnB

2
สิ่งนี้มีประโยชน์หรือไม่โปรดพิจารณาการลงคะแนน
สงสัยใน

มันคือ - ฉันไม่สามารถลงคะแนนได้เพราะฉันต้องการชื่อเสียง 15 :-(
JohnB

@StephaneChazelas: ตามหน้า man สำหรับ BSD basenameปฏิบัติการสามารถใช้หลายสายเป็นอาร์กิวเมนต์ ฉันตรวจสอบ OSX สองครั้งมันใช้งานได้
rahmu

1
ขออภัยฉันยืนที่ถูกต้อง ฉันไม่ทราบว่าส่วนขยาย BSD นั้น อย่างไรก็ตามยังคงล้มเหลวหากมีไฟล์สองไฟล์ คุณจะต้องเพิ่ม-aตัวเลือกให้ครอบคลุมสำหรับกรณีนั้นด้วย
Stéphane Chazelas

2

ทางเลือกอื่น (สมมติว่าไม่มีบรรทัดใหม่ในชื่อไฟล์):

find ~ -type f | awk -F/ '{print $NF}' | sort | uniq -d

2

คุณสามารถใช้xargsกับbasenameเพื่อให้ได้ผลลัพธ์ที่ต้องการเช่นนี้

find ~ -type f -name "*" -print | xargs -l basename | sort | uniq -d > duplicate_files

0

ด้วยเวอร์ชันล่าสุดของbashที่จัดการกับอาเรย์แบบเชื่อมโยงต่อไปนี้จะจัดการชื่อพา ธ ด้วยการขึ้นบรรทัดใหม่:

#!/bin/bash

topdir=$HOME

shopt -s globstar  # enable the ** glob

declare -A count

# count the number of times each filename (base name) occurs
for pathname in "$topdir"/**; do
    # skip names that are not regular files (or not symbolic links to such files)
    [ ! -f "$pathname" ] && continue

    # get the base name
    filename=${pathname##*/}

    # add one to this base name's count
    count[$filename]=$(( ${count[$filename]} + 1 ))
done

# go through the collected names and print any name that
# has a count greater than one
for filename in "${!count[@]}"; do
    if [ "${count[$filename]}" -gt 1 ]; then
        printf 'Duplicate filename: %s\n' "$filename"
    fi
done

สิ่งนี้ไม่ใช้ยูทิลิตี้ภายนอก

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