Làm cách nào để tôi bắt stdin đến một biến mà không tước bất kỳ dòng mới nào?


9

Trong một kịch bản shell ...

Làm cách nào để tôi bắt stdin đến một biến mà không tước bất kỳ dòng mới nào?

Ngay bây giờ tôi đã thử:

var=`cat`
var=`tee`
var=$(tee)

Trong mọi trường hợp $varsẽ không có dòng mới của luồng đầu vào. Cảm ơn.

CSONG: Nếu không có dòng mới trong đầu vào, thì giải pháp không được thêm vào .

CẬP NHẬT TRONG ÁNH SÁNG ĐÁP ÁN ĐƯỢC CHẤP NHẬN:

Giải pháp cuối cùng mà tôi đã sử dụng trong mã của mình như sau:

function filter() {
    #do lots of sed operations
    #see https://github.com/gistya/expandr for full code
}

GIT_INPUT=`cat; echo x`
FILTERED_OUTPUT=$(printf '%s' "$GIT_INPUT" | filter)
FILTERED_OUTPUT=${FILTERED_OUTPUT%x}
printf '%s' "$FILTERED_OUTPUT"

Nếu bạn muốn xem mã đầy đủ, vui lòng xem trang github cho expandr , một chút mã nguồn mở git lọc từ khóa mở rộng shell script mà tôi được phát triển cho mục đích an ninh thông tin. Theo các quy tắc được thiết lập trong các tệp .gitattribut (có thể là chi nhánh cụ thể) và git config , git ống từng tệp thông qua tập lệnh shell extendr.sh bất cứ khi nào kiểm tra nó vào hoặc ra khỏi kho lưu trữ. (Đó là lý do tại sao việc lưu giữ bất kỳ dòng mới nào hoặc thiếu thông tin là rất quan trọng.) Điều này cho phép bạn xóa thông tin nhạy cảm và trao đổi trong các bộ giá trị cụ thể khác nhau của môi trường để kiểm tra, dàn dựng và các nhánh sống.


những gì bạn làm ở đây là không cần thiết. filtermất stdin- nó chạy sed. Bạn bắt stdinvào $GIT_INPUTsau đó in nó trở lại stdoutqua một đường ống đến filtervà bắt nó stdoutvào $FILTERED_OUTPUTvà sau đó in lại stdout. Tất cả 4 dòng ở cuối ví dụ của bạn ở trên có thể được thay thế bằng chỉ này : filter. Không có ý xúc phạm ở đây, chỉ là ... bạn đang làm việc quá sức. Bạn không cần các biến shell trong hầu hết thời gian - chỉ cần hướng đầu vào đến đúng nơi và chuyển nó vào.
mikeerv

Không, những gì tôi làm ở đây là cần thiết bởi vì nếu tôi chỉ làm filter, thì nó sẽ thêm các ký tự dòng mới vào cuối của bất kỳ luồng đầu vào nào không kết thúc trong dòng mới ban đầu. Trong thực tế, ban đầu tôi chỉ làm filternhưng gặp phải vấn đề đó dẫn tôi đến giải pháp này bởi vì "không phải luôn luôn thêm dòng mới" hay "luôn luôn loại bỏ dòng mới" là giải pháp có thể chấp nhận được.
CommaToast

sedcó thể sẽ làm thêm dòng mới - nhưng bạn nên xử lý nó filterkhông phải với tất cả phần còn lại. Và tất cả các chức năng mà bạn có về cơ bản đều làm điều tương tự - a sed s///. Bạn đang sử dụng vỏ để dữ liệu ống nó đã được lưu trong bộ nhớ của nó để sedsao cho sedcó thể thay thế mà dữ liệu với dữ liệu khác mà vỏ đã được lưu trữ trong bộ nhớ của nó để sedcan ống nó trở lại vỏ. Tại sao không chỉ [ "$var" = "$condition" ] && var=new_value? Tôi cũng không nhận được các mảng - bạn đang lưu trữ tên mảng [0]sau đó sử dụng sedđể thay thế nó bằng giá trị trong [1]? Có lẽ trò chuyện?
mikeerv

@mikeerv - Lợi ích của việc di chuyển mã đó vào bên trong là filtergì? Nó hoạt động hoàn hảo như vốn có. Về cách mã trong liên kết của tôi hoạt động và lý do tại sao tôi thiết lập mã theo cách tôi đã làm, vâng, hãy nói về nó trong phòng trò chuyện.
Dấu phẩy

Câu trả lời:


7

Các dòng mới theo dõi được loại bỏ trước khi giá trị được lưu trữ trong biến. Bạn có thể muốn làm một cái gì đó như:

var=`cat; echo x`

và sử dụng ${var%x}thay vì $var. Ví dụ:

printf "%s" "${var%x}"

Lưu ý rằng điều này giải quyết vấn đề dòng mới, nhưng không phải là byte byte (nếu đầu vào tiêu chuẩn không phải là văn bản), vì theo thay thế lệnh POSIX :

Nếu đầu ra chứa bất kỳ byte rỗng nào, hành vi không được chỉ định.

Nhưng việc triển khai shell có thể bảo toàn byte rỗng.


Các tệp văn bản thường chứa byte rỗng? Tôi không thể hiểu tại sao họ lại như vậy. Nhưng kịch bản mà bạn vừa đề cập có vẻ không hiệu quả.
Dấu phẩy

@CommaToast Tệp văn bản không chứa byte rỗng. Nhưng câu hỏi chỉ nói stdin / luồng đầu vào, có thể không phải là văn bản trong trường hợp chung nhất.
vinc17

ĐỒNG Ý. Chà, tôi đã thử nó từ dòng lệnh và nó không làm gì cả, và từ trong chính kịch bản của tôi, đề xuất của bạn không thành công vì nó thêm "..." vào cuối tệp. Ngoài ra nếu không có dòng mới ở đó, thì nó vẫn thêm một dòng.
CommaToast

@CommaToast "..." chỉ là một ví dụ. Tôi đã làm rõ câu trả lời của mình. Không có dòng mới nào được thêm vào (xem văn bản trước "..." trong ví dụ).
vinc17

1
Chà, vỏ sò không nên che giấu mọi thứ, điều đó không tuyệt. Những quả đạn pháo phải được bắn đi. Tôi không thích nó khi máy tính của tôi nghĩ rằng nó biết nhiều hơn tôi.
CommaToast

4

Bạn có thể sử dụng tích readhợp sẵn để thực hiện việc này:

$ IFS='' read -d '' -r foo < <(echo bar)

$ echo "<$foo>"
<bar
>

Đối với tập lệnh để đọc STDIN, nó chỉ đơn giản là:

IFS='' read -d '' -r foo

 

Tôi không chắc chắn những gì shell này sẽ làm việc mặc dù. Nhưng hoạt động tốt trong cả bash và zsh.


Không -dphải cũng không phải là sự thay thế quá trình ( <(...)) là di động; mã này sẽ không hoạt động dash, ví dụ.
chepner

Vâng, sự thay thế quá trình không phải là một phần của câu trả lời, đó chỉ là một phần của ví dụ cho thấy rằng nó hoạt động. Đối với -d, đó là lý do tại sao tôi đặt từ chối trách nhiệm ở phía dưới. OP không chỉ định vỏ.
Patrick

@chepner - trong khi phong cách hơi khác một chút, khái niệm chắc chắn có tác dụng dash. Bạn chỉ cần sử dụng <<HEREDOC\n$(gen input)\nHEREDOC\n- trong dash- sử dụng các đường ống cho heredocs giống như cách các shell khác sử dụng chúng để thay thế quá trình - điều đó không có gì khác biệt. Các read -dđiều chỉ được xác định một dấu phân cách - bạn có thể làm một chục cách tương tự - chỉ cần chắc chắn về điều đó. Mặc dù bạn sẽ cần một số đuôi để gen input.
mikeerv

Bạn đặt IFS = '' để nó không đặt khoảng trắng ở giữa các dòng nó đọc trong hả? Thủ thuật hay.
CommaToast

Trên thực tế trong trường hợp này IFS=''có lẽ không cần thiết. Điều đó có nghĩa là readsẽ không sụp đổ không gian. Nhưng khi nó đọc thành một biến duy nhất, nó không có tác dụng (mà tôi có thể nhớ lại). Nhưng tôi chỉ cảm thấy an toàn hơn khi để nó vào :-)
Patrick

2

Bạn có thể làm như:

input | { var=$(sed '$s/$/./'); var=${var%.}; }

Bất cứ điều gì bạn làm đều $varbiến mất ngay khi bạn bước ra khỏi { current shell ; }nhóm đó. Nhưng nó cũng có thể hoạt động như sau:

var=$(input | sed '$s/$/./'); var=${var%.}

1
Cần lưu ý rằng với giải pháp đầu tiên, tức là phải sử dụng $vartrong { ... }nhóm, không phải lúc nào cũng có thể. Chẳng hạn, nếu lệnh này được chạy bên trong một vòng lặp và người ta cần $varbên ngoài vòng lặp.
vinc17

@ vinc17 - nếu nó là một vòng lặp Tôi mong muốn sử dụng, sau đó tôi sẽ sử dụng nó ở vị trí của {}niềng răng Nó là sự thật - và được ghi nhận một cách rõ ràng trong câu trả lời - rằng giá trị cho $varrất có thể biến mất hoàn toàn khi { current shell; }nhóm là đóng cửa. Có một số nhiều cách rõ ràng để nói nó hơn, Dù bạn làm gì $varbiến mất ...?
mikeerv

@ vinc17 - có lẽ là cách tốt nhất, mặc dù:input | sed "s/'"'/&"&"&/g;s/.*/process2 '"'-> &'/" | sh
mikeerv

1
Ngoài ra còn có _variableschức năng bash_completionlưu trữ kết quả của sự thay thế lệnh trong một biến toàn cục COMPREPLY. Nếu một giải pháp đường ống đã được sử dụng để giữ dòng mới, kết quả sẽ bị mất. Trong câu trả lời của bạn, người ta có ấn tượng rằng cả hai giải pháp đều tốt như nhau. Ngoài ra, cần lưu ý rằng hành vi của giải pháp đường ống phụ thuộc rất nhiều vào trình bao: người dùng có thể kiểm tra echo foo | { var=$(sed '$s/$/./'); var=${var%.}; } ; echo $varvới ksh93 và zsh và nghĩ rằng nó ổn, trong khi mã này có lỗi.
vinc17

1
Bạn đã không nói "nó không hoạt động". Bạn vừa nói " $varbiến mất" (điều này thực sự không đúng vì điều này phụ thuộc vào trình bao - hành vi không được xác định bởi POSIX), đây là một câu khá trung tính. Giải pháp thứ hai tốt hơn bởi vì nó không gặp phải vấn đề này và hành vi của nó là nhất quán trong tất cả các vỏ POSIX.
vinc17
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.