Làm thế nào tôi có thể xử lý dữ liệu nhị phân thô trong một ống bash?


15

Tôi có một hàm bash lấy một tệp làm tham số, xác minh tệp tồn tại, sau đó ghi bất cứ thứ gì ra khỏi stdin vào tệp. Giải pháp ngây thơ hoạt động tốt cho văn bản, nhưng tôi gặp vấn đề với dữ liệu nhị phân tùy ý.

echo -n '' >| "$file" #Truncate the file
while read lines
do  # Is there a better way to do this? I would like one...
    echo $lines >> "$file"
done

Câu trả lời:


15

Cách của bạn là thêm ngắt dòng cho mọi thứ mà nó viết trong không gian của bất kỳ dấu phân cách ( $IFS) nào đang sử dụng để phân chia phần đọc. Thay vì chia nó thành các dòng mới, chỉ cần lấy toàn bộ và chuyển nó đi cùng. Bạn có thể giảm toàn bộ bit mã ở trên xuống đây:

 cat - > $file

Bạn không cần bit rút ngắn, điều này sẽ cắt bớt và ghi toàn bộ luồng STDIN ra nó.

Chỉnh sửa: Nếu bạn đang sử dụng zsh, bạn chỉ có thể sử dụng > $filethay cho con mèo. Bạn đang chuyển hướng đến một tập tin và cắt bớt nó, nhưng nếu có bất cứ điều gì đang chờ đợi ở đó chờ đợi một cái gì đó chấp nhận STDIN, nó sẽ được đọc tại thời điểm đó. Tôi nghĩ rằng bạn có thể làm một cái gì đó như thế này với bash nhưng bạn sẽ phải thiết lập một số chế độ đặc biệt.


Tôi không thể làm cho ví dụ chuyển hướng stdin hoạt động, nhưng thay đổi ví dụ con mèo thành> | (Tôi có bộ noclobber) hoạt động như một lá bùa. Cảm ơn vì đã làm cho ngày của tôi ^. ^
David Souther

+1 cho phiên bản không có mèo. Luôn luôn tránh những con mèo vô dụng;)
rozcietrzewiacz

@rozcietrzewiacz: Đúng, ngoại trừ đó là một suy nghĩ lại và tôi đã sai. Đây có thể không phải là một cách sử dụng mèo vô dụng. Điều duy nhất bạn có thể làm là > $file. Điều này chỉ hoạt động như điều đầu tiên tìm kiếm stdin trong tập lệnh shell cha. Về cơ bản, tất cả các mã của David có thể được giảm xuống thành một ký tự duy nhất, nhưng tôi nghĩ rằng mã cat -này thanh lịch hơn và ít rắc rối hơn vì nó được hiểu trong tầm nhìn.
Caleb

Đôi khi tôi kết hợp bốn hoặc năm catgiây với nhau, chỉ để làm phiền những kẻ cuồng tín UUOC
Michael Mrozek

@MichaelMrozek: Đôi khi tôi đặt tên cho các tệp dữ liệu của mình catchỉ để những người khăng khăng sử dụng nó nhất thiết phải tập thể dục tinh thần để đọc mã. Đặt tên ống cũng là mục tiêu tốt.
Caleb

7

Để đọc tệp văn bản theo nghĩa đen, không sử dụng đơn giản read, xử lý đầu ra theo hai cách:

  • readgiải thích \như một nhân vật thoát; sử dụng read -rđể tắt cái này
  • readchia thành các từ trên các ký tự trong $IFS; đặt IFSthành một chuỗi trống để tắt cái này

Thành ngữ thông thường để xử lý một dòng tệp văn bản theo dòng là

while IFS= read -r line; do 

Để giải thích về thành ngữ này, hãy xem Tại sao được while IFS= readsử dụng thường xuyên như vậy, thay vì IFS=; while read..? .

Để viết một chuỗi theo nghĩa đen, đừng chỉ sử dụng đơn giản echo, mà xử lý chuỗi theo hai cách:

  • Trên một số shell, echoxử lý dấu gạch chéo ngược thoát. (Trên bash, tùy thuộc vào việc xpg_echotùy chọn có được đặt hay không.)
  • Một vài chuỗi được coi là tùy chọn, ví dụ -nhoặc -e(bộ chính xác phụ thuộc vào vỏ).

Một cách di động để in một chuỗi theo nghĩa đen là với printf. (Không có cách nào tốt hơn trong bash, trừ khi bạn biết đầu vào của mình không giống như một tùy chọn echo.) Sử dụng mẫu đầu tiên để in chuỗi chính xác và mẫu thứ hai nếu bạn muốn thêm một dòng mới.

printf %s "$line"
printf '%s\n' "$line"

Điều này chỉ phù hợp để xử lý văn bản , bởi vì:

  • Hầu hết các shell sẽ sặc trên các ký tự null trong đầu vào.
  • Khi bạn đã đọc dòng cuối cùng, bạn không có cách nào để biết liệu có một dòng mới ở cuối hay không. (Một số shell cũ hơn có thể gặp rắc rối lớn hơn nếu đầu vào không kết thúc bằng một dòng mới.)

Bạn không thể xử lý dữ liệu nhị phân trong hệ vỏ, nhưng các phiên bản hiện đại của tiện ích trên hầu hết các đơn vị có thể đối phó với dữ liệu tùy ý. Để chuyển tất cả đầu vào thông qua đầu ra, sử dụng cat. Đi trên một tiếp tuyến, echo -n ''là một cách phức tạp và không di động để làm gì; echo -nsẽ tốt như vậy (hoặc không phụ thuộc vào vỏ), và :đơn giản hơn và hoàn toàn di động.

: >| "$file"
cat >>"$file"

hoặc, đơn giản hơn,

cat >|"$file"

Trong một tập lệnh, bạn thường không cần sử dụng >|noclobberbị tắt theo mặc định.


cảm ơn vì đã chỉ ra xpg_echo, đó thực sự là một vấn đề mà tôi gặp phải ở một nơi khác trong mã của mình và thậm chí không nhận ra. Re noclobber, tôi có thói quen bật nó trong bashrc của tôi.
David Souther

0

Điều này sẽ làm chính xác những gì bạn muốn:

( while read -r -d '' ; do
    printf %s'\0' "${REPLY}" ;
  done ;

  # When read hits EOF, it returns non-zero which exits the while loop.
  # That data still needs to be output:
  printf %s "${REPLY}"
) >> ${file}

Hãy lưu ý việc sử dụng bộ nhớ mặc dù. Điều này đọc đầu vào trong một cách phân cách null.

Nếu không có byte \0 rỗng trong đầu vào thì trước tiên bash sẽ cần đọc toàn bộ nội dung đầu vào vào bộ nhớ, sau đó xuất nó.

Về bước cắt ngắn của bạn:

echo -n '' >| "$file" #Truncate the file

đơn giản hơn nhiều và tương đương là:

> ${file}   #Truncate the file
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.