Làm cách nào để xóa dòng cuối cùng của tất cả các tệp khỏi thư mục?


17

Tôi có nhiều tệp văn bản trong một thư mục và tôi muốn xóa dòng cuối cùng của mỗi tệp trong thư mục.

Tôi làm nó như thế nào?


6
Bạn đã thử những gì? unix.stackexchange.com/help/how-to-ask : "Chia sẻ nghiên cứu của bạn giúp mọi người. Hãy cho chúng tôi biết bạn đã tìm thấy gì và tại sao nó không đáp ứng nhu cầu của bạn. Điều này chứng tỏ rằng bạn đã dành thời gian để cố gắng tự giúp mình , nó tiết kiệm cho chúng tôi khỏi việc nhắc lại câu trả lời rõ ràng, và trên hết, nó giúp bạn có được câu trả lời cụ thể và phù hợp hơn! "
Patrick

Tại sao suy nghĩ của ai đó vô tình áp dụng điều đó vào / etc có chất lượng cảm ứng cringe rất đặc biệt :)
rackandboneman

Câu trả lời:


4

Nếu bạn có quyền truy cập vim, bạn có thể sử dụng:

for file in ./*
do
  if [ -f "${file}" ]
  then
    vim -c '$d' -c "wq" "${file}"
  fi
done

16

Bạn có thể sử dụng oneliner đẹp này nếu bạn có GNU sed.

 sed -i '$ d' ./*

Nó sẽ xóa dòng cuối cùng của mỗi tệp không bị ẩn trong thư mục hiện tại. Switch -ifor GNU sedcó nghĩa là hoạt động tại chỗ và ra '$ d'lệnh sedxóa dòng cuối cùng ( $nghĩa dlà xóa lần cuối và nghĩa là xóa).


3
Điều này sẽ phát sinh lỗi (và không làm gì cả) nếu thư mục chứa bất kỳ thứ gì ngoài tệp thông thường, ví dụ: thư mục khác ...
Najib Idrissi

1
@StefanR Bạn đang sử dụng -imột GNUism, vì vậy đây là moot, nhưng tôi sẽ mất đi bộ râu cũ nếu tôi không chỉ ra rằng một số phiên bản cũ hơn sedkhông cho phép bạn đặt bất kỳ khoảng trắng nào giữa $d(hoặc, nói chung, giữa mẫu và lệnh).
zwol

1
@zwol Như tôi đã viết, điều này sẽ dẫn đến một lỗi , không phải là một cảnh báo và sed sẽ từ bỏ khi nó đạt đến tập tin đó (ít nhất là với phiên bản sed tôi có). Các tập tin tiếp theo sẽ không được xử lý. Vứt bỏ các thông báo lỗi sẽ là một ý tưởng tồi tệ vì bạn thậm chí sẽ không biết nó đã xảy ra! Với zsh bạn có thể sử dụng *(.)để toàn cầu cho các tệp thông thường, tôi không biết về các shell khác.
Najib Idrissi

@NajibIdrissi Hmm, bạn nói đúng. Điều đó làm tôi ngạc nhiên; Tôi đã dự kiến ​​nó sẽ phàn nàn về thư mục nhưng sau đó chuyển sang tập tin tiếp theo trên dòng lệnh. Trên thực tế, tôi nghĩ rằng tôi sẽ báo cáo đó là một lỗi.
zwol

@don_crissti Tôi cũng có GNU sed v4.3 ... Tôi không biết phải nói gì với bạn, tôi mới thử nghiệm lại. gist.github.com/nidrissi/66fad6be334234f5dbb41c539d84d61e
Najib Idrissi

11

Các câu trả lời khác đều có vấn đề nếu thư mục chứa thứ gì đó không phải là tệp thông thường hoặc tệp có dấu cách / dòng mới trong tên tệp. Đây là một cái gì đó hoạt động bất kể:

find "$dir" -type f -exec sed -i '$d' '{}' '+'
  • find "$dir" -type f: tìm các tập tin trong thư mục $dir
    • -type f đó là những tập tin thường xuyên;
    • -exec thực hiện lệnh trên mỗi tập tin được tìm thấy
    • sed -i: chỉnh sửa các tập tin tại chỗ;
    • '$d': xóa ( d) dòng cuối cùng ( $).
    • '+': Tell find để tiếp tục thêm đối số vào sed(hiệu quả hơn một chút so với chạy lệnh cho từng tệp riêng biệt, nhờ @zwol).

Nếu bạn không muốn đi vào thư mục con, thì bạn có thể thêm đối số -maxdepth 1vào find.


1
Hmm, nhưng không giống như các câu trả lời khác, điều này rơi vào các thư mục con. (Ngoài ra, với các phiên bản hiện tại của findđiều này được viết hiệu quả hơn find $dir -type f -exec sed -i '$d' '{}' '+'.)
zwol

@zwol Cảm ơn, tôi đã thêm nó vào câu trả lời.
Najib Idrissi

-print0không có mặt trong lệnh đầy đủ, tại sao lại đưa nó vào phần giải thích?
Ruslan

1
Ngoài ra, -depth 0không hoạt động (findutils 4.4.2), thay vào đó nên -maxdepth 1.
Ruslan

@Ruslan Tôi đã có một phiên bản đầu tiên nơi tôi sử dụng xargs nhưng sau đó nhớ -exec.
Najib Idrissi

9

Sử dụng GNU sed -i '$d'có nghĩa là đọc toàn bộ tệp và tạo một bản sao của tệp mà không có dòng cuối cùng, trong khi việc cắt bớt tệp tại chỗ sẽ hiệu quả hơn (ít nhất là đối với các tệp lớn).

Với GNU truncate, bạn có thể làm:

for file in ./*; do
  [ -f "$file" ] &&
    length=$(tail -n 1 "$file" | wc -c) &&
    [ "$length" -gt 0 ] &&
    truncate -s "-$length" "$file"
done

Nếu các tệp tương đối nhỏ, điều đó có thể sẽ kém hiệu quả hơn vì nó chạy một số lệnh trên mỗi tệp.

Lưu ý rằng đối với các tệp chứa byte bổ sung sau ký tự dòng mới cuối cùng (sau dòng cuối cùng) hoặc nói cách khác nếu chúng có dòng cuối không được phân tách , thì tùy thuộc vào việc tailtriển khai, tail -n 1sẽ chỉ trả về các byte bổ sung đó (như GNU tail), hoặc dòng cuối cùng (được phân cách chính xác) và các byte bổ sung đó.


Bạn cần một |wc -ctrong các tailcuộc gọi? (hoặc a ${#length})
Jeff Schaller

@JeffSchaller. Giáo sư. wc -c đã được dự định thực sự. ${#length}sẽ không hoạt động vì nó đếm các ký tự, không phải byte và $(...)sẽ loại bỏ ký tự dòng mới, do đó ${#...}sẽ bị tắt bởi một ký tự ngay cả khi tất cả các ký tự là một byte.
Stéphane Chazelas

6

Một cách tiếp cận di động hơn:

for f in ./*
do
test -f "$f" && ed -s "$f" <<\IN
d
w
q
IN
done

Tôi không nghĩ rằng điều này cần bất kỳ lời giải thích ... ngoại trừ có lẽ đó trong trường hợp này dcũng giống như $dtừ edbởi Selects mặc định dòng cuối cùng.
Điều này sẽ không tìm kiếm đệ quy và sẽ không xử lý các tệp ẩn (còn gọi là dotfiles).
Nếu bạn muốn chỉnh sửa chúng, hãy xem Cách khớp * với các tệp ẩn trong một thư mục


Đẹp! Nếu bạn thay đổi [[]]để []sau đó nó sẽ hoàn toàn POSIX tuân thủ. ( [[ ... ]]là một chủ nghĩa Bashism.)
Wildcard

@Wildcard - cảm ơn, đã thay đổi (mặc dù [[không phải là bashism )
don_crissti

Một phi-POSIX, tôi nên nói. :)
tự đại diện

3

Một lớp lót tuân thủ POSIX cho tất cả các tệp đệ quy bắt đầu trong thư mục hiện tại, bao gồm các tệp dấu chấm:

find . -type f -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

Chỉ dành cho .txtcác tệp, không đệ quy:

find . -path '*/*/*' -prune -o -type f -name '*.txt' -exec sh -c 'for f; do printf "\$d\nx\n" | ex "$f"; done' sh {} +

Cũng thấy:

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.