Dòng mới trong biến bash


9

Tôi đang cố lưu trữ nhiều dòng trong một biến bash, nhưng dường như nó không hoạt động.

Ví dụ: nếu tôi liệt kê /binmột tệp trên mỗi dòng và lưu nó vào $LS, thì tôi chuyển $LSdưới dạng stdin sang wc, nó luôn trả về 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

Nếu tôi cố xuất ra màn hình, tôi nhận được nhiều kết quả khác nhau: echoin tất cả các dòng trên một dòng, trong khi printfchỉ in dòng đầu tiên:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

Vì vậy, một biến bash có thể chứa nhiều dòng?

Câu trả lời:


15

Bạn cần phải trích dẫn nó đôi (và bạn nên nhân đôi biến trích dẫn trong hầu hết các trường hợp):

echo "$LS"

Nhưng không sử dụng echo để in nội dung biến, thay vào đó sử dụng printf :

printf '%s\n' "$LS"

1
Chỉ cần đưa ra một điểm tốt hơn cho câu trả lời tổng thể: printf mong đợi một chuỗi định dạng là tham số đầu tiên của nó, đó là lý do tại sao họ không nhìn thấy những gì họ mong đợi. Nếu họ muốn xem các dòng mới trong đầu ra từ printf, hãy phân chia một cách tự nhiên trên các khoảng trắng, printf "%s\n" $LSsẽ làm điều đó.
Jeff Schaller

% LS phải là $ LS, btw (Tôi không thể chỉnh sửa ngắn như vậy)
Jeff Schaller

Cảm ơn! Tất nhiên nó cũng hoạt động với wcvà các lệnh khác:wc -l <<< "$LS"
Wizard79

1
@JeffSchaller: Không, bạn không nên bỏ biến trong trường hợp này, chỉ sử dụng printf '%s\n' "$LS"nếu bạn muốn xem dòng mới. Đó là gõ sai, sửa nó!
cuonglm

Tại sao bạn không nên sử dụng echo để in các biến?
123

9

Các dòng mới là trong biến. LS=$(ls -1)đặt biến LSthành đầu ra của ls -1(tạo ra đầu ra giống như ls, nhân tiện, ngoại trừ khi đầu ra đi đến một thiết bị đầu cuối), trừ đi các dòng mới.

Vấn đề là bạn đang xóa các dòng mới khi bạn in ra giá trị. Trong một tập lệnh shell, $LSkhông có nghĩa là giá trị của biến số LS, mà nó có nghĩa là cách lấy giá trị của LSnó, chia nó thành các từ theo IFSvà diễn giải từng từ như một mô hình toàn cầu. Để có được giá trị của LS, bạn cần phải viết "$LS", hoặc nói chung là đặt $LSgiữa các dấu ngoặc kép.

echo "$LS"in giá trị của LS, ngoại trừ trong một số shell diễn giải các ký tự dấu gạch chéo ngược và ngoại trừ một vài giá trị bắt đầu bằng -.

printf "$LS"in giá trị LSmiễn là nó không chứa bất kỳ ký tự phần trăm hoặc dấu gạch chéo ngược nào và (với hầu hết các triển khai) không bắt đầu bằng -.

Để in giá trị LSchính xác, sử dụng printf %s "$LS". Nếu bạn muốn một dòng mới ở cuối, sử dụng printf '%s\n' "$LS".

Lưu ý rằng $(ls), nói chung, không phải là danh sách các tập tin trong thư mục hiện tại. Điều này chỉ hoạt động khi bạn có đủ tên tập tin thuần hóa. Để có được danh sách tên tệp (trừ tệp chấm), bạn cần sử dụng ký tự đại diện : *. Kết quả là một danh sách các chuỗi, không phải là một chuỗi, vì vậy bạn không thể gán nó cho một biến chuỗi; bạn có thể sử dụng một biến mảng files=(*)trong shell hỗ trợ chúng (ksh93, bash, zsh).

Để biết thêm thông tin, hãy xem Tại sao tập lệnh shell của tôi bị nghẹt trên khoảng trắng hoặc các ký tự đặc biệt khác?


printf "$ LS" cũng không chịu nổi việc phân tích ngược ký tự, tương tự như echo
cnst

0

Đã nói ở trên

printf '%s\n' "$LS"

là giải pháp đúng duy nhất.

Hãy để tôi chứng minh rằng các giải pháp được đề xuất khác, cả với echoprintf, đơn giản là không hoạt động đúng:

$ mkdir t
$ cd t
$ touch \\n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \\n
$ cd ..
$ rmdir t
$

(Người ta cũng có thể kiểm tra V=$(printf 'line0:\\n\\n\nline1.\n') printf '%s\n' "$V"và biến thể.)

Trong khi \ntên tệp có thể không phổ biến, hãy lưu trữ git diffvào một biến và khả năng bạn gặp phải nghĩa đen \ntăng lên đáng kể.


Lưu ý rằng kshzshcũng hỗ trợ print -r -- "$LS"zshcũng có echo -E - $LS. cshecho $LS:qdù rằng không ra kí tự xuống dòng nếu $ LS là trống rỗng, và nhận được một giá trị multiline thành $ LS ở nơi đầu tiên là khá khó khăn trong csh.
Stéphane Chazelas
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.