Tại sao shell Lệnh thay thế lại ngấu nghiến một char mới theo dõi?


25

Theo ví dụ sau đây, và như trong câu hỏi gần đây của tôi Trong bash, dòng chữ char mới đã đi đâu? , Tôi muốn biết "tại sao" nó xảy ra

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

Tôi cho rằng phải có một số lý do rất quan trọng cho một hành động vỏ, cụ thể là Thay thế lệnh, để thực sự xóa một số dữ liệu khỏi đầu ra lệnh mà nó đang thay thế ...
nhưng tôi không thể hiểu được điều này, vì dường như nó là các phản đề của những gì nó được cho là phải làm .. tức là. để chuyển đầu ra của lệnh trở lại quy trình viết kịch bản ... Giữ lại một ký tự có vẻ kỳ lạ đối với tôi, nhưng tôi cho rằng có một lý do hợp lý cho nó ... Tôi rất muốn tìm hiểu lý do đó là gì .. .


Câu trả lời:


22

Bởi vì shell ban đầu không nhằm mục đích trở thành một ngôn ngữ lập trình đầy đủ.

Nó là khá khó khăn để loại bỏ một dấu vết \ntừ một số đầu ra lệnh. Tuy nhiên, với mục đích hiển thị, hầu như tất cả các lệnh đều kết thúc đầu ra của chúng \n, do đó, phải có một cách đơn giản để loại bỏ nó khi bạn muốn sử dụng nó trong một lệnh khác. Tự động loại bỏ với việc $()xây dựng là giải pháp được lựa chọn.

Vì vậy, có thể bạn sẽ chấp nhận câu hỏi này như một câu trả lời:

Bạn có thể tìm một cách đơn giản để xóa dấu vết \nnếu điều này không được thực hiện tự động trong lệnh sau không?

> echo The current date is "$(date)", have a good day!

Lưu ý rằng trích dẫn là cần thiết để ngăn chặn phá vỡ không gian đôi có thể xuất hiện trong các ngày được định dạng.


1
Nếu những gì bạn nói là đúng, thì tôi sẽ hoàn toàn choáng váng. Nếu có một shoptthay đổi hành vi mặc định được mô tả của bạn, thì tôi sẽ vui vẻ trở lại ... Tôi thấy khó có thể nghĩ rằng Bash / Linux / Unix sẽ làm ô nhiễm một chức năng quan trọng như "bắt lỗi thiết bị xuất chuẩn" chỉ vì datekhông có tùy chọn có / không có '\ n' ... Cách khắc phục rõ ràng là bash (hoặc shell khác) có một shoptcái này ... Bạn có biết cái nào không? .. và bạn có bất kỳ liên kết tham khảo nào nói về các vấn đề cá nhân và nơi ở của cái này không? ... và tại sao không datecó tùy chọn \ n .. Nó nên!
Peter.O

2
Thay thế lệnh xuất hiện trong phiên bản đầu tiên của vỏ Bourne trong "phiên bản thứ 7" của Unix vào năm 1979. Đó đã là một cuộc cách mạng lớn và tôi đoán (tôi không được sinh ra) rằng không ai quan tâm đến việc theo dõi '\ n' hay không. Có, bạn có thể đọc trang này trong vòng chưa đầy 10 phút và dường như không có gì thay đổi về việc thay thế lệnh cho đến lúc đó. Ngoại trừ $()cú pháp thay thế ưa thích .
Stéphane Gimenez

Cảm ơn .. Tôi nghĩ rằng đây có thể là một vấn đề di sản, và nó chắc chắn đang hình thành sự thật rằng tôi nên bắt đầu xem các Lệnh thay thế của mình cẩn thận hơn. Cho đến hôm nay tôi phải sống sót trên một lời cầu nguyện .. Một số "sự việc bí ẩn" mà tôi gặp phải đang bắt đầu có ý nghĩa bây giờ :) ... Vì vậy, trong trường hợp ngược lại với tư thế "hẹn hò" của bạn, nếu Tôi muốn giữ dấu vết của mình \ n, tôi phải viết mã như thế này :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. vậy thôi :)
Peter.O

PS là nhận xét trên của tôi: Tất nhiên, datesẽ hoạt động, nhưng tôi chỉ sử dụng datenhư một ví dụ về bất kỳ lệnh nào ..
Peter.O

'Câu hỏi' về ngày của bạn là sự thúc đẩy mạnh mẽ nhất đối với sự hiểu biết của tôi về 'tại sao' Lệnh thay thế thực hiện những gì nó làm, vì vậy đây là câu trả lời 'tốt nhất' cho câu hỏi của tôi ... và tôi chắc chắn cũng cần những câu trả lời hay khác ..
Peter.O

19

Đó là một phần của tiêu chuẩn :

Shell sẽ mở rộng thay thế lệnh bằng cách thực thi lệnh trong môi trường lớp con (xem Môi trường thực thi Shell ) và thay thế lệnh thay thế (văn bản lệnh cộng với dấu "$ ()" hoặc backquote kèm theo đầu ra tiêu chuẩn của lệnh, loại bỏ trình tự của một hoặc nhiều <newline> s ở cuối thay thế.

Tất nhiên, tiêu chuẩn có thể được viết theo cách này bởi vì đó là cách kshnó đã làm hoặc một cái gì đó, nhưng nó là tiêu chuẩn, và đó là hành vi được ghi lại. Nếu bạn không thích nó, hãy sử dụng Perl hoặc một cái gì đó khác sẽ cho phép bạn giữ các dòng mới.


Một bản tóm tắt hay .. Cảm ơn .. và các liên kết chắc chắn đã giúp
Peter.O

4
Tiêu chuẩn là như vậy bởi vì đó là cách vỏ Bourne đã làm nó và mọi vỏ khác kể từ đó.
Gilles 'SO- ngừng trở nên xấu xa'

6

Vâng, nó có ý nghĩa với tôi. Dòng mới chỉ ở đó ở vị trí đầu tiên, trong đầu ra lệnh thông thường, để lời nhắc xuất hiện sau khi lệnh hoàn thành trên một dòng mới. Dòng mới không phải là một phần của đầu ra ban đầu trong hầu hết các trường hợp, nó ở đó để thu gọn màn hình. Khi bạn phân tích cú pháp đầu ra từ một lệnh, dòng mới ở cuối thường gây rắc rối. Tại sao wclệnh xuất ra 2 dòng văn bản? Ồ, không, nó xuất ra một dòng theo sau một dòng mới. Khi bạn phân tích cú pháp, wcbạn không muốn lo lắng về thực tế là có 2 dòng đầu ra - thực sự không có, chỉ có một.


@EightBitTony: Câu hỏi của tôi không liên quan đến việc hiển thị một cái gì đó trong thiết bị đầu cuối .. Điều đó khá đơn giản. Nếu có một dòng mới để hiển thị, thì nó sẽ hiển thị dòng mới .. Điểm đang được đề cập ở đây, đó là một dòng \ntrong bản gốc stdoutbị loại bỏ bởi Thay thế lệnh. I E. dữ liệu gốc của tôi (dữ liệu rất quan trọng) đã bị xóa các dòng mới, ngay cả khi dữ liệu được gói trong "dấu ngoặc kép". Tôi hiểu một cách hợp lý cách thức và lý do tại sao Word Splting hoạt động, nhưng tôi hoàn toàn không biết tại sao các Lệnh thay thế chỉ tách ra các dòng mới và chỉ theo dõi .
Peter.O

1
Không có gì bạn đã nói thay đổi quan điểm của tôi. Thường xuyên hơn không, dòng mới cuối cùng trong bất kỳ đầu ra nào hoàn toàn dành cho mục đích hiển thị, không phải là một phần của dữ liệu.
EightBitTony

Tôi đồng ý với quan điểm của bạn và có tất cả ... Có, dòng mới ở cuối đầu ra để thuận tiện ... nhưng vấn đề của tôi không liên quan gì đến điều đó ... Tôi đang hỏi: Tại sao Lệnh thay thế buộc phải gỡ bỏ nó ? ... Đây là hai vấn đề khác nhau .. Có lẽ câu hỏi của tôi nên là: Có cách giải quyết nào không? . bởi vì phải viết mã cho vấn đề này bằng cách thêm một char dummy trailing, thật tệ! ... Tôi sẽ làm điều đó nếu tôi phải làm, nhưng đây là lần đầu tiên tôi bị cản trở bởi bash (và tôi đang ở trong trạng thái sốc :) .. Nó làm rất tốt ...
Peter.O

Như đã đề cập trong câu trả lời khác, đó là một phần của tiêu chuẩn. Đối với tôi, tôi nghĩ nó làm theo cách đó bởi vì khi bạn xử lý dữ liệu sau khi xử lý, bạn chỉ muốn xem dữ liệu chứ không phải là dòng mới tiện lợi. Đó là bởi vì trình bao này hy vọng bạn sẽ xử lý dữ liệu trong một đường ống và dòng mới không phải là dữ liệu. Hơn nữa và chúng ta cần phải đưa điều này đến một cuộc thảo luận.
EightBitTony

Cảm ơn .. Tôi đã có ý tưởng khá tốt ngay bây giờ .. Có vẻ như đó là một trường hợp Thay thế Lệnh chỉ đơn giản là dựa vào việc bỏ các dòng mới, thay vì duy trì chúng .. Phải có một sự khôn ngoan trong động thái mà tôi không "cảm thấy" "Tuy nhiên, nhưng tôi đã từng nghĩ rằng việc sử dụng phổ biến tất cả các chữ cái lo-case trong tên tệp là hơi lạ / không cần thiết, nhưng tôi đã nghĩ chỉ một ngày khác rằng nó thực sự là một hệ thống khá thoải mái .. Có lẽ tôi sẽ xem (sâu hơn) vần điệu và lý do của hành vi thay thế chỉ huy, một ngày nào đó ...
Peter.O

1

Tôi đã chạy vào cùng một vấn đề, và tìm thấy ví dụ dưới đây. Có vẻ như trích dẫn đã giúp tình hình, ít nhất nó đã làm cho tôi:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh

2
Đây là những gì đang xảy ra ở đây. Giả sử bạn có một biến $str, chứa chuỗi "a b c". Nếu bạn vượt qua $strđể echomà không có dấu ngoặc kép ( echo $str), echosẽ nhận được a, bcnhư ba đối số riêng biệt. Shell của bạn không quan tâm đến khoảng trắng ở giữa các đối số. echosau đó sẽ in các đối số đó với các khoảng trắng phân tách từng đối số để bạn có được "a b c". Nếu bạn truyền biến cho echodấu ngoặc kép quanh nó ( echo "$str"), shell sẽ xem nó là một đối số và echonhận nó như vậy, vì vậy khoảng trắng không bị thu gọn. Điều tương tự áp dụng cho các dòng mới.

1
Vấn đề mà người đăng ban đầu đang gặp phải là các dòng mới (chỉ những dòng cuối cùng) của các lệnh của anh ta đang biến mất. Trừ khi thông qua -ncờ, echothêm một dòng mới vào đầu ra của nó, vì vậy cả hai tiếng vang trong ví dụ của bạn đều có dòng mới. Tuy nhiên, nếu bạn thử echo -n $dir_listinghoặc echo -n "$dir_listing", bạn sẽ thấy rằng không có dòng mới nào được in có hoặc không có dấu ngoặc kép $dir_listing, điều này cho thấy vấn đề là do sự thay thế lệnh.

2
tldp là một xấu nơi lỗi thời để tìm hiểu về kịch bản shell. Thay vào đó hãy thử Wooledge Bash Wiki. mywiki.wooledge.org/BashGuide
Wildcard

-1

Tại sao echo "hello "(không có dấu ngoặc kép) ngấu nghiến không gian? giá trị của các ký tự IFS được shell xem là các dấu phân cách, do đó, các ký tự dấu (không phân định bất cứ thứ gì) đang bị xóa. commend thay thế chỉ đơn giản là thực hiện commends trong một shell con, vì vậy nó tuân theo logic tương tự.


@Philomath: Cả hai x="hello  "x="hello     "sẽ mất tất cả các khoảng trống ở cuối nếu được hiển thị qua echo $x... Tuy nhiên, chỉ dòng mới cuối cùng của Lệnh Substituton bị mất.
Peter.O

Thật kỳ lạ, tôi không thấy bất kỳ sự khác biệt nào trong bash của mình (xác minh với xxd)
Philomath

Tôi chỉ checke về điều này ... Nó xóa tất cả các dòng mới ... Tôi đã thử nghiệm với IFS khi tôi có ấn tượng đó, vì vậy hãy hủy bỏ cái đó :) .. nhưng câu hỏi vẫn còn ... Đây là một phần cơ bản phân tách từ để cô đọng nhiều không gian vào một không gian duy nhất và để loại bỏ hoàn toàn khoảng trắng hàng đầu và dấu .. Tôi có thể hiểu điều đó, nhưng tôi chắc chắn không hiểu tại sao, với Thay thế lệnh, nó chỉ thoát ra và không phải tất cả khoảng trắng (như với việc tách từ) và tại sao chỉ có dấu kết thúc? .. và trên hết: "Thay thế lệnh" chỉ thay thế một phần .. Tại sao?
Peter.O

4
IFSkhông có gì để làm với tước bỏ dòng mới vào cuối thay thế lệnh.
Gilles 'SO- ngừng trở nên xấu xa'
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.