จะค้นหาไฟล์ที่ไม่มีบรรทัดที่ท้ายได้อย่างไร?


9

ฉันมีไฟล์ในไดเรกทอรีย่อยของไดเรกทอรีปัจจุบันที่อาจมีหรือไม่มีบรรทัดใหม่ในตอนท้าย ฉันจะหาไฟล์ที่ไม่มีการขึ้นบรรทัดใหม่ได้อย่างไรในตอนท้าย

ฉันเคยลองแล้ว:

find . -name '*.styl' | while read file; do
    awk 'END{print}' $file | grep -E '^$' > /dev/null || echo $file;
done

แต่มันไม่ทำงาน พิมพ์เส้นก่อนที่จะสายใหม่ที่ว่างเปล่าเช่นเดียวกับawk 'END{print}' $filetail -n 1 $file


@don_crissti ฉันต้องการไฟล์ที่ไม่มีบรรทัดที่ต่อท้าย
jcubic

2
ฉันขอเหตุผลที่คุณต้องค้นหาไฟล์เหล่านั้นได้ไหม ฉันเดาว่ามันเกี่ยวข้องกับความจริงที่ว่าไฟล์ข้อความในยูนิกซ์ควรถูกยกเลิกด้วยการขึ้นบรรทัดใหม่ (vi จะ "เกือบเงียบ" เพิ่มหนึ่งเมื่อคุณบันทึกตัวอย่าง) และคำสั่ง (เชิงข้อความ) จะไม่สนใจ บรรทัดสุดท้ายหากไม่ได้ขึ้นบรรทัดใหม่ (wc, iirc .... แต่มีคนอื่น) และสิ่งนี้อาจช่วยได้
Olivier Dulac

awk 'END{print}' $file : สิ่งนี้จะไม่สนใจเนื้อหาของไฟล์ $ ทั้งหมดและหลังจากเสร็จสิ้นการวิเคราะห์ไฟล์ทั้งหมดที่มีอยู่ใน "$ file" จะเป็นการเพิ่มบรรทัดใหม่ เนื่องจากเป็นสิ่งเดียวที่คำสั่ง awk พิมพ์จึงสามารถถูกแทนที่ด้วย: printf '\n'(โดยไม่มี mentino ของ $ file เลย) และทำสิ่งเดียวกัน ฉันคิดว่านี่ไม่ใช่สิ่งที่คุณกำลังเล็ง (เช่น: พิมพ์บรรทัดสุดท้ายของไฟล์?)
Olivier Dulac

@don_crissti: หากอักขระตัวสุดท้ายของไฟล์ไม่ใช่บรรทัดใหม่ไฟล์นั้นจะไม่ได้เป็นไฟล์ unix TEXT ที่เป็นไปในทางบวก โปรดดูที่: unix.stackexchange.com/a/263919/27616 โปรดทราบว่าคำสั่งข้อความจำนวนมาก (ตัวอย่างเช่น) เพียงแค่ละเว้น "บรรทัด" สุดท้ายหากไม่ได้ขึ้นบรรทัดใหม่โดย
Olivier Dulac

1
@OlivierDulac: พิมพ์เพ่งพิศcและเพื่อไม่ FreeBSD แต่ฉันไม่ได้สังเกตเห็นว่ามันเป็นเอกสารการดำเนินการขึ้นอยู่กับ: gnu.org/software/gawk/manual/... ดังนั้นมันจะเกิดขึ้น แต่ไม่เสมอไป
dave_thompson_085

คำตอบ:


14

ในการชี้แจง\nตัวละครLF (aka หรือ newline) เป็นตัวคั่นบรรทัดไม่ใช่ตัวคั่นบรรทัด บรรทัดไม่เสร็จจนกว่าจะมีการยกเลิกด้วยอักขระขึ้นบรรทัดใหม่ ไฟล์ที่มีเพียงอย่างเดียวa\nbไม่ใช่ไฟล์ข้อความที่ถูกต้องเพราะมีอักขระหลังบรรทัดสุดท้าย aเหมือนกันสำหรับไฟล์ที่มีเพียง ไฟล์ที่มีa\nหนึ่งบรรทัดที่ไม่ว่างเปล่า

ดังนั้นไฟล์ที่ลงท้ายด้วยบรรทัดว่างอย่างน้อยหนึ่งบรรทัดจึงลงท้ายด้วยอักขระขึ้นบรรทัดใหม่สองตัวหรือมีอักขระขึ้นบรรทัดใหม่หนึ่งบรรทัด

ถ้า:

 tail -c 2 file | od -An -vtc

เอาต์พุต\nหรือ\n \nไฟล์นั้นมีบรรทัดว่างเปล่าต่อท้ายอย่างน้อยหนึ่งบรรทัด ถ้ามันไม่มีอะไรแสดงว่านั่นเป็นไฟล์ว่างเปล่าถ้ามันออก<anything-but-\0> \nมามันก็จะจบลงในบรรทัดที่ไม่ว่างเปล่า อย่างอื่นมันไม่ใช่ไฟล์ข้อความ

ตอนนี้เมื่อต้องการใช้เพื่อค้นหาไฟล์ที่ลงท้ายด้วยบรรทัดว่าง OK มีประสิทธิภาพ (โดยเฉพาะอย่างยิ่งสำหรับไฟล์ขนาดใหญ่) โดยที่จะอ่านสองไบต์สุดท้ายของไฟล์เท่านั้น ไม่สอดคล้องกันจากการใช้งานหนึ่งodไปยังอีกและเราจะต้องเรียกใช้หนึ่งtailและหนึ่งodต่อไฟล์

find . -type f -size +0 -exec gawk '
  ENDFILE{if ($0 == "") print FILENAME}' {} +

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

เป็นการดีที่คุณจะต้องมีเชลล์ที่สามารถอ่านไฟล์ได้เอง

ด้วยzsh:

zmodload zsh/system
for f (**/*(D.L+0)) {
  {
    sysseek -w end -2
    sysread
    [[ $REPLY = $'\n' || $REPLY = $'\n\n' ]] && print -r -- $f
  } < $f
}

วิธีการที่จะใช้วิธีการของคำตอบนี้จะรู้ว่าถ้าบางไฟล์ (s) are_textfiles () { nontext=0; rem="return 0 if all args are files with terminating newline, or n [=number of non-textfiles]" ; for f in "$@" ; do [ -f "$f" ] && { tail -c 1 "$f" | od -An -vtc | grep "\\n" ;} >/dev/null 2>&1 || ((nontext++)) ; done ; return $nontext ; }เป็นไฟล์ข้อความ: ใช้เป็น:if ( are_textfiles this that otherthing ) ; then echo all are text files ; else echo "are_textfiles returned : $?" ; fi
Olivier Dulac

6

ด้วยgnu sedและเปลือกเช่นzsh(หรือbashกับshopt -s globstar):

sed -ns '${/./F}' ./**/*.styl

วิธีนี้จะตรวจสอบว่าบรรทัดสุดท้ายของแต่ละไฟล์ไม่ว่างเปล่าหรือไม่หากเป็นเช่นนั้นจะพิมพ์ชื่อไฟล์
หากคุณต้องการตรงกันข้าม (ชื่อไฟล์พิมพ์หากบรรทัดสุดท้ายว่างเปล่า) เพียงแค่แทนที่/./ด้วย/^$/


1
ไม่เคยเห็น-sการกระทำมาก่อน ขอบคุณ GNU!
เกล็นแจ็

หมายเหตุ: ตัวเลือก F มีอยู่ในรุ่น sed 4.2.2 (22 ธันวาคม 2012)
ไอแซค

3

\nไฟล์ข้อความยกเลิกอย่างถูกต้องกับที่ว่างเปล่าปลายบรรทัดสุดท้ายในสอง

จากนั้นเราคาดหวังว่าจะต้องเท่ากับtail -c2$'\n\n'

การขยายคำสั่งที่น่าเศร้าลบการขึ้นบรรทัดใหม่ เราจะต้องปรับเปลี่ยนเล็กน้อย

f=filename
nl='
'
t=$(tail -c2 $f; printf x)  # capture the last two characters.
r="${nl}${nl}$"                 # regex for: "ends in two newlines".
[[ ${t%x} =~ $r ]] &&  echo "file $f ends in an empty line"

เราสามารถขยายได้เล็กน้อยเพื่อตรวจสอบว่าไฟล์ใดไม่สามารถมีบรรทัดใหม่ต่อท้าย:

nl='
'
nl=$'\n'
find . -type f -name '*.styl' | while read f; do
    t=$(tail -c2 $f; printf x); r1="${nl}$"; r2="${nl}${r1}"
    [[ ${t%x} =~ $r1 ]] || echo "file $f is missing a trailing newline"
    [[ ${t%x} =~ $r2 ]] && echo "$f"
done

โปรดทราบว่าสามารถขึ้นบรรทัดใหม่เป็นบางสิ่งบางอย่างเช่น$'\r\nถ้าจำเป็น
ในกรณีที่ยังเปลี่ยนไปtail -c2tail -c4


0
for file in *; do
    # Check if the file is readable to avoid clutter
    if cat "./$file" 2&>1 /dev/null; then
        # Compare the last character with a single newline character.
        if [ -n "$(tail -c 1 -- "./$file")" ]; then
            echo "$file"
        fi
        # Also report empty files.
        if [ $(wc -c  < "./$file") -eq 0 ]; then
            echo "$file"
        fi
    fi
done

1
มันใช้ไม่ได้กับไฟล์ที่ว่างเปล่า แต่ฉันสามารถใช้มันได้
jcubic

อาจมีข้อผิดพลาดเพิ่มเติมเนื่องจากการเปรียบเทียบสตริงไม่ทำงานตามที่ฉันคาดไว้ ฉันได้เพิ่มการตรวจสอบไฟล์ว่างเปล่า
Oskar Skog

อ๊ะละเว้นอักขระบรรทัดใหม่
Oskar Skog

พิจารณาอ่านได้มากขึ้นหรือถ้านี้ถูกทุบตีอย่างเดียวcat $file 2>&1 /dev/null cat $file &> /dev/null
แมว

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