Cách thêm dòng mới vào các biến trong tập lệnh bash


64

Khi tôi làm

str="Hello World\n===========\n"

Tôi nhận được \nin ra quá. Làm thế nào tôi có thể có dòng mới sau đó?


1
Mặc dù câu trả lời ở đây rất hay, nhưng thực tế tôi nghĩ rằng bạn nên sử dụng một mảng cho loại điều này hầu hết thời gian.
evilsoup

Câu trả lời:


73

Trong bashbạn có thể sử dụng cú pháp

str=$'Hello World\n===========\n'

Các dấu ngoặc đơn đứng trước a $là một cú pháp mới cho phép chèn các chuỗi thoát trong chuỗi.

Ngoài ra printfnội dung cho phép lưu kết quả đầu ra vào một biến

printf -v str 'Hello World\n===========\n'

Cả hai giải pháp không yêu cầu một subshell.

Nếu trong phần sau bạn cần in chuỗi, bạn nên sử dụng dấu ngoặc kép, như trong ví dụ sau:

echo "$str"

bởi vì khi bạn in chuỗi không có dấu ngoặc kép, dòng mới được chuyển thành khoảng trắng.


1
Cú pháp str=$'Hello World\n===========\n'được gọi là gì? thay thế?
zengr

5
@zengr: Nó được gọi là trích dẫn ANSI-C , và nó cũng được hỗ trợ trong zshksh; tuy nhiên, nó không tuân thủ POSIX.
mkuity0

4
@ mkelement0, nó đến từ ksh93, cũng được hỗ trợ bởi zsh, bash, mksh và FreeBSD sh, và việc đưa nó vào phiên bản chính tiếp theo của POSIX đang được thảo luận
Stéphane Chazelas

1
Có vẻ như không hoạt động với dấu ngoặc kép? ví dụ str=$"My $PET eats:\n$PET food"? Cách tiếp cận này hoạt động cho dấu ngoặc kép
Brad park

31

Bạn có thể đặt các dòng mới theo nghĩa đen trong các dấu ngoặc đơn (trong bất kỳ vỏ kiểu Bourne / POSIX nào).

str='Hello World
===========
'

Đối với một chuỗi nhiều dòng, ở đây các tài liệu thường thuận tiện. Chuỗi được cung cấp như là đầu vào cho một lệnh.

mycommand <<'EOF'
Hello World
===========
EOF

Nếu bạn muốn lưu trữ chuỗi trong một biến, hãy sử dụng catlệnh thay thế lệnh. (Các) ký tự dòng mới ở cuối chuỗi sẽ bị tước bằng cách thay thế lệnh. Nếu bạn muốn giữ lại các dòng mới cuối cùng, hãy đặt một nút chặn ở cuối và loại bỏ nó sau đó. Trong các shell tương thích POSIX, bạn có thể viết str=$(cat <<'EOF'); str=${str%a}theo sau bởi heredoc thích hợp, nhưng bash yêu cầu heredoc xuất hiện trước dấu ngoặc đơn đóng.

str=$(cat <<'EOF'
Hello World
===========
a
EOF
); str=${str%a}

Trong ksh, bash và zsh, bạn có thể sử dụng $'…'biểu mẫu được trích dẫn để mở rộng dấu gạch chéo ngược bên trong dấu ngoặc kép.

str=$'Hello World\n===========\n'

1
Tôi đang sử dụng GNU bash 4.1.5 và str=$(cat <<'EOF')không hoạt động như hiện tại .. Các )nhu cầu phải được đặt ở dòng tiếp theo sau khi kết thúc tài liệu EOF.. nhưng ngay cả như vậy, nó bị mất dòng mới do Lệnh Thay thế.
Peter.O

@fred Điểm tốt, tôi đã giải thích về các dòng mới và mã hiển thị hoạt động trong bash. Tôi nghĩ rằng đó là một lỗi trong bash, mặc dù thành thật mà nói khi đọc lại thông số POSIX tôi thấy không rõ hành vi đó là bắt buộc khi << ở trong một thay thế lệnh và heredoc không.
Gilles 'SO- ngừng trở nên xấu xa'

Công cụ tuyệt vời; để duy trì các trường hợp theo dõi (và dẫn đầu) \ntrong bashkhi chụp tài liệu ở đây trong một biến, hãy xem xét IFS= read -r -d '' str <<'EOF'...như một cách thay thế cho phương pháp chặn (xem câu trả lời của tôi).
mkuity0

8

Bạn đang sử dụng "tiếng vang"? Hãy thử "echo -e".

echo -e "Hello World\n===========\n"

2
BTW. Bạn không cần \ n cuối cùng, vì echosẽ tự động thêm một cái, trừ khi bạn chỉ định -n. (Tuy nhiên, vấn đề chính của câu hỏi là làm thế nào để đưa các dòng mới này vào một biến).
Peter.O

+1 Trong tất cả các giải pháp, đây là giải pháp trực tiếp và đơn giản nhất.
Hải Vũ

echo -e không hoạt động trong OS X
Greg M. Krsak

2
@GregKrsak: Trong bash, echo -e không hoạt động trên OS X - đó là vì echomột bash dựng sẵn (chứ không phải là một thực thi bên ngoài) và nội dung đó hỗ trợ -e. (Như một dựng sẵn, nó sẽ hoạt động trên tất cả các nền tảng mà bash chạy trên; tình cờ, echo -elàm việc trong kshzshquá). Tuy nhiên, ngược lại, tiện ích bên ngoàiecho trên OS X - /bin/echo- thực sự không hỗ trợ -e.
mkuity0

4

Nếu bạn cần dòng mới trong tập lệnh của mình nhiều lần, bạn có thể khai báo một biến toàn cục đang giữ một dòng mới. Bằng cách đó, bạn có thể sử dụng nó trong chuỗi trích dẫn kép (mở rộng biến).

NL=$'\n'
str="Hello World${NL} and here is a variable $PATH ===========${NL}"

Tại sao sẽ $''cần một subshell?
Mat

Xin lỗi, tôi đọc sai một câu trả lời.
pieaveragy

Tôi có thể yêu cầu giải thích từ downvoters?
pieaveragy

Đẹp! Điều này có thể được bao gồm trong các biến sử dụng dấu ngoặc kép, ví dụ:"My dog eats:${NL}dog food"
Brad Parks

Điều này làm việc cho tôi. Khi sử dụng tập lệnh bash để tổng hợp một chuỗi được tự động sao chép vào bảng tạm để dán tiếp theo ở nơi khác, phương pháp này hoạt động để thêm dòng mới vào kết quả đầu ra.
Ville

3

Từ tất cả các cuộc thảo luận, đây là cách đơn giản nhất đối với tôi:

bash$ str="Hello World
==========="
bash$ echo "$str"
Hello World
===========

Lệnh echo phải sử dụng dấu ngoặc kép .


3

Để bổ sung cho các câu trả lời tuyệt vời hiện có:

Nếu bạn đang sử dụng bashvà bạn thích sử dụng các dòng mới thực sự để dễ đọc , readlà một tùy chọn khác để ghi lại một tài liệu ở đây trong một biến , mà (giống như các giải pháp khác ở đây) không yêu cầu sử dụng một mạng con.

# Reads a here-doc, trimming leading and trailing whitespace.
# Use `IFS= read ...` to preserve it (the trailing \n, here).
read -r -d '' str <<'EOF'   # Use `IFS= read ...` to preserve the trailing \n
Hello World
===========
EOF
# Test: output the variable enclosed in "[...]", to show the value's boundaries.
$ echo "$str"
[Hello World
===========]
  • -rđảm bảo rằng readkhông diễn giải đầu vào (theo mặc định, nó sẽ xử lý dấu gạch chéo ngược đặc biệt, nhưng điều đó hiếm khi cần thiết).

  • -d ''đặt dấu phân cách "bản ghi" thành một chuỗi trống, khiến readcho việc đọc toàn bộ đầu vào cùng một lúc (thay vì chỉ một dòng).

Lưu ý rằng bằng cách để $IFSmặc định $' \t\n'( dấu phân cách trường bên trong) ở mặc định, ( khoảng trắng , tab, dòng mới), mọi khoảng trắng ở đầu và cuối được cắt bớt từ giá trị được gán $str, bao gồm dòng mới ở đây.
(Lưu ý rằng ngay cả khi phần thân của tài liệu ở đây bắt đầu trên dòng sau dấu phân cách bắt đầu ( 'EOF'ở đây), nó không chứa dòng mới hàng đầu ).

Thông thường, đây là hành vi mong muốn, nhưng nếu bạn muốn dòng mới đó, hãy sử dụng IFS= read -r -d ''thay vì chỉ read -r -d '', nhưng lưu ý rằng bất kỳ khoảng trắng hàng đầu và dấu nào đều được giữ nguyên.
(Lưu ý rằng việc trả trước IFS= trực tiếp cho readlệnh có nghĩa là việc gán chỉ có hiệu lực trong suốt lệnh đó, do đó không cần khôi phục giá trị trước đó.)


Sử dụng tài liệu ở đây cũng cho phép bạn tùy ý sử dụng thụt lề để đặt chuỗi đa dòng cho dễ đọc:

# Caveat: indentation must be actual *tab* characters - spaces won't work.
read -r -d '' str <<-'EOF' # NOTE: Only works if the indentation uses actual tab (\t) chars.
    Hello World
    ===========
EOF
# Output the variable enclosed in "[...]", to show the value's boundaries.
# Note how the leading tabs were stripped.
$ echo "$str"
[Hello World
===========]

Đặt -giữa <<và dấu phân cách mở ở đây-doc ( 'EOF', ở đây) làm cho các ký tự tab hàng đầu bị xóa khỏi thân tài liệu ở đây và thậm chí là dấu phân cách đóng, nhưng lưu ý rằng điều này chỉ hoạt động với các ký tự tab thực tế , không phải dấu cách, vì vậy nếu biên tập viên dịch phím nhấn vào không gian, cần thêm công việc.


-1

bạn cần làm theo cách này:

STR=$(echo -ne "Hello World\n===========\n")

Cập nhật:

Như Fred đã chỉ ra, theo cách này, bạn sẽ mất dấu "\ n". Để gán biến với các chuỗi dấu gạch chéo ngược được mở rộng, hãy làm:

STR=$'Hello World\n===========\n\n'

Hãy kiểm tra nó:

echo "[[$STR]]"

cho chúng tôi ngay bây giờ:

[[Hello World
===========

]]

Lưu ý rằng $ '' khác với $ "". Thứ hai không dịch theo ngôn ngữ hiện tại. Đối với deital xem phần QUOTING trong man bash.


-2
#!/bin/bash

result=""

foo="FOO"
bar="BAR"

result+=$(printf '%s' "$foo")$'\n'
result+=$(printf '%s' "$bar")$'\n'

echo "$result"
printf '%s' "$result"

đầu ra:

FOO
BAR

FOO
BAR

1
Tại sao không đơn giản result+=$foo$'\n'? $(printf %s "$foo")sẽ cắt các ký tự dòng mới trong $foonếu có.
Stéphane Chazelas

Không có lời giải thích cho mã, nó khác có vẻ tốt với tôi.
một cái gì đó
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.