Làm cách nào tôi có thể làm cho toán tử backtick bash giữ dòng mới trong đầu ra?


36

Có cách nào để làm cho bash không ăn các dòng mới trong sự thay thế backtick?

Ví dụ:

var=`echo line one && echo line two`
echo $var

line one line two

Điều tôi muốn là

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two

Câu trả lời:


54

Đó không phải là một vấn đề với sự thay thế backticks, nhưng với echo; bạn phải trích dẫn biến để làm cho các ký tự điều khiển hoạt động:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two

Vì vậy, bash loại bỏ các dòng mới trong khi mở rộng một biến trên lệnh-lien? Và hành vi đó có thể bị vô hiệu hóa bằng cách trích dẫn biến?
Slowpoison

7
bashThực hiện thông số mở rộng trước khi thực hiện một lệnh, trích dẫn là cần thiết để thông báo bashmà bạn muốn in một chuỗi duy nhất và không 4 chữ ( line, one, linetwo). Hãy thử: echo a <many spaces> becho "a <many spaces> b". Hãy xem câu trả lời này quá.
cYrus

5

Mặc dù @cyrus là chính xác - nó không thực sự trả lời toàn bộ câu hỏi và không có lời giải thích nào về những gì đang xảy ra.

Vì vậy, hãy đi bộ qua nó.

Dòng mới trong chuỗi

Đầu tiên xác định chuỗi byte dự kiến:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Bây giờ, sử dụng Thay thế lệnh (Mục 3.5.4) để cố gắng bắt chuỗi byte này:

$ x=$( echo a; echo b; )

Sau đó, thực hiện một tiếng vang đơn giản để xác minh chuỗi byte:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Vì vậy, có vẻ như dòng mới đầu tiên đã được thay thế bằng một khoảng trắng, và dòng mới thứ hai vẫn còn nguyên. Nhưng tại sao ?

Hãy nhìn vào những gì đang thực sự xảy ra ở đây:

Đầu tiên, bash sẽ thực hiện Mở rộng tham số Shell (Phần 3.5.3)

Ký tự '$' giới thiệu mở rộng tham số, thay thế lệnh hoặc mở rộng số học. Tên tham số hoặc ký hiệu được mở rộng có thể được đặt trong dấu ngoặc nhọn, là tùy chọn nhưng phục vụ để bảo vệ biến được mở rộng khỏi các ký tự ngay sau ký tự có thể được hiểu là một phần của tên.

Sau đó, bash sẽ thực hiện Chia tách từ (Phần 3.5.7)

Shell quét kết quả mở rộng tham số, thay thế lệnh và mở rộng số học không xảy ra trong dấu ngoặc kép để tách từ.

Shell xử lý mỗi ký tự của $ IFS như một dấu phân cách và chia kết quả của các bản mở rộng khác thành các từ trên các ký tự này. Nếu IFS không được đặt hoặc giá trị của nó là chính xác, ...

Tiếp theo, bash sẽ coi nó như một Lệnh đơn giản (Mục 3.2.1)

Một lệnh đơn giản là loại lệnh thường gặp nhất. Nó chỉ là một chuỗi các từ được phân tách bằng khoảng trắng, được chấm dứt bởi một trong các toán tử điều khiển của shell (xem Định nghĩa). Từ đầu tiên thường chỉ định một lệnh sẽ được thực thi, phần còn lại của các từ là đối số của lệnh đó.

Định nghĩa của khoảng trống (Phần 2 - Định nghĩa)

trống Một ký tự khoảng trắng hoặc tab.

Cuối cùng, bash gọi lệnh nội bộ echo (Phần 4.2 - Lệnh Bash Buildin)

... Xuất ra các đối số, cách nhau bởi khoảng trắng, kết thúc bằng một dòng mới. ...

Vì vậy, để tóm tắt, các dòng mới được loại bỏ bằng Word Splting, và sau đó echo nhận được 2 arg, "a" và "b", sau đó xuất ra chúng cách nhau bởi khoảng trắng và kết thúc bằng một dòng mới.

Làm những gì @cyrus đề xuất (và loại bỏ dòng mới khỏi echo bằng -n) thì kết quả sẽ tốt hơn:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Dòng mới ở cuối chuỗi

Mặc dù vậy, nó vẫn chưa hoàn hảo, dòng mới đã biến mất. Nhìn gần hơn vào Thay thế lệnh (Mục 3.5.4) :

Bash thực hiện việc mở rộng bằng cách thực hiện lệnh và thay thế lệnh thay thế bằng đầu ra tiêu chuẩn của lệnh, với bất kỳ dòng mới nào bị xóa.

Bây giờ đã rõ lý do tại sao dòng mới biến mất, bash có thể bị lừa để giữ nó. Để làm điều này, thêm một chuỗi bổ sung vào cuối và loại bỏ nó khi sử dụng biến:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Thêm phần bổ sung vào cuối đầu ra và trích dẫn các biến:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46

0

Tôi đã có thể đưa ra bổ sung nhỏ của mình trong một nhận xét về câu trả lời của cYrus, nhưng tôi chưa thể bình luận về câu trả lời tôi nghĩ. Vì vậy, tôi sẽ sao chép một phần của cYrus câu trả lời của anh ấy cho đầy đủ.

Điều xảy ra trong dòng đầu tiên của mã của bạn là thực sự thay thế backticks ăn các dòng mới của đầu ra của lệnh được thay thế, như được ghi lại . Nhưng nó không ăn dòng mới giữa hai dòng của bạn.

Điều xảy ra trong dòng mã thứ hai là echo được thực thi với hai đối số, dòng thứ nhất và dòng thứ hai. Trích dẫn biến mở rộng sẽ khắc phục điều đó: echo "$var"Điều này sẽ duy trì dòng mới giữa dòng một và dòng hai của bạn. Và echosẽ thêm một dòng mới theo mặc định để mọi thứ đều ổn.

Bây giờ nếu bạn muốn duy trì tất cả các dòng mới trong đầu ra của một số thay thế lệnh, một cách giải quyết là thêm một dòng khác đầu ra, mà bạn có thể loại bỏ sau khi thay thế lệnh, xem tại đâ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.