Xóa n byte đầu tiên của tệp


32

Tôi đã có một vấn đề cực đoan, và tất cả các giải pháp tôi có thể tưởng tượng đều phức tạp. Theo kinh nghiệm UNIX / Linux của tôi, phải có một cách dễ dàng.

Tôi muốn xóa 31 byte đầu tiên của mỗi tệp trong /foo/. Mỗi tập tin là đủ dài. Chà, tôi chắc chắn rằng ai đó sẽ cung cấp cho tôi một giải pháp cực kỳ dễ dàng mà tôi không thể tưởng tượng được. Có lẽ nào?


2
Bất kỳ giải pháp awk / sed / ed nào cũng sẽ được định hướng theo dòng, vì vậy nếu bạn không biết dòng đầu tiên sẽ có ít nhất 31 ký tự thì các biến chứng xảy ra.
glenn jackman

Câu trả lời:


28
for file in /foo/*
do
  if [ -f "$file" ]
  then
    dd if="$file" of="$file.truncated" bs=31 skip=1 && mv "$file.truncated" "$file"
  fi
done

hoặc nhanh hơn, nhờ đề xuất của Gilles:

for file in /foo/*
    do
      if [ -f $file ]
      then
        tail +32c $file > $file.truncated && mv $file.truncated $file
      fi
    done

Lưu ý: Đuôi Posix chỉ định "-c +32" thay vì "+ 32c" nhưng đuôi mặc định của Solaris không thích:

   $ /usr/bin/tail -c +32 /tmp/foo > /tmp/foo1
    tail: cannot open input

/usr/xpg4/bin/tail là tốt với cả hai cú pháp.


1
Gợi ý ddở đây là quá mức cần thiết, tailphù hợp hơn (đơn giản hơn, ít rủi ro hơn về một lỗi đánh máy giết người, không có thông điệp giả mạo trên stderr).
Gilles 'SO- ngừng trở nên xấu xa'

Bạn đúng rồi. Tôi thường tránh các lệnh nhằm xử lý tệp văn bản khi xử lý các tệp nhị phân có thể nhưng "tail + 32c" sẽ hoạt động ở đây.
jlliagre

1
@jlliagre: Bạn đã viết cut (không nên là đuôi? ... asis, nó không hoạt động với tôi ...
Peter.O

Tất nhiên, đó là đuôi. Xin lỗi về sự không phù hợp.
jlliagre

@jlliagre: Trên Solaris, bạn nên có /usr/xpg4/bintrước /usr/bintrên của bạn PATH, hoặc bạn sẽ bị mắc kẹt trong đầu những năm 1990. Nhiều thông báo (ví dụ GNU, BusyBox) không còn hỗ trợ +32ccú pháp lịch sử và lấy nó để có nghĩa là một tệp được gọi +32c(như POSIX yêu cầu).
Gilles 'SO- ngừng trở nên xấu xa'

12

Các lệnh sau cắt 31 byte đầu tiên từ $file(sử dụng $file~làm bản sao tạm thời):

dd if="$file" of="$file~" bs=1 skip=31
mv "$file~" "$file"

Bạn chỉ cần liệt kê hoặc findtất cả các tệp bên dưới /foo/và thực hiện hai ở trên cho mỗi $filetìm thấy.


1
Trao đổi bs và bỏ qua các giá trị sẽ tăng hiệu suất.
jlliagre

10

tail -c +32đầu ra của nó trừ đi 31 byte đầu tiên. (Có, đối số bị tắt bởi một.) Để chỉnh sửa tệp tại chỗ, hãy sử dụng miếng bọt biển trong một vòng lặp hoặc nếu bạn không có nó và không muốn làm phiền, hãy thực hiện công việc của nó trong trình bao:

for x in /foo/*; do tail -c +32 "$x" | sponge "$x"; done
for x in /foo/*; do tail -c +32 "$x" >"$x.new" && mv "$x.new" "$x"; done

Nếu các lệnh bị gián đoạn vì bất kỳ lý do gì (ví dụ như mất điện), có thể khó tìm ra nơi bạn rời đi. Viết các tệp mới vào một thư mục riêng sẽ giúp mọi việc dễ dàng hơn.

mkdir /foo.tmp
cd /foo
for x in *; do tail -c +42 -- "$x" >"/foo.tmp/$x" && rm -- "$x"; done
mv /foo.tmp/* /foo
rmdir /foo.tmp

Nếu các tệp thực sự lớn (như trong, đủ lớn để có hai bản sao của một bản duy nhất là một vấn đề), bạn có thể sử dụng một trong các kỹ thuật được đề cập trong chuỗi này .


2

Bạn có thể sử dụng Vim trong chế độ Ex:

for each in /foo/*
do
  ex -sc '%!tail -c+32' -cx "$each"
done
  1. % chọn tất cả các dòng

  2. ! chạy lệnh

  3. x lưu và đóng

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.