Làm thế nào để chuyển giá trị của một biến vào stdin của một lệnh?


105

Tôi đang viết một tập lệnh shell nên hơi an toàn, tức là không truyền dữ liệu an toàn thông qua các tham số của lệnh và tốt nhất là không sử dụng các tệp tạm thời. Làm cách nào để truyền một biến vào stdin của lệnh? Hoặc, nếu không thể, làm thế nào để sử dụng chính xác các tệp tạm thời cho tác vụ đó?


Kẻ tấn công có thể thay đổi $PATH? Vì vậy, đó catcó thể được thay thế be/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Câu trả lời:


74

Một cái gì đó đơn giản như:

echo "$blah" | my_cmd

7
Cẩn thận với khoảng trắng trong biến của bạn: echo "$blah"tốt hơn.
Jonathan Leffler

13
Hãy xem cách bạn xử lý blah=-n, blah=-e... sử dụng printfthay thế. unix.stackexchange.com/questions/65803/…
Camilo Martin

1
Điều này có vẻ như nó sẽ dễ vỡ và dễ xảy ra lỗi, phải không?
ThorSummoner

20
6 năm trôi qua, nếu tôi có thể xóa câu trả lời này, tôi sẽ (nhưng tôi có thể không phải vì nó được chấp nhận ...)
Oliver Charlesworth

1
@OliverCharlesworth, tại sao không chỉnh sửa nó để sử dụng printf '%s\n' "$blah"? Với một thay đổi đó (tránh nhiều cạm bẫy trong echothông số kỹ thuật, câu trả lời tuyệt vời của Stephane đi vào chi tiết tại Tại sao lại printftốt hơn echo? Trên Unix & Linux , hoặc phần SỬ DỤNG ỨNG DỤNG của echothông số kỹ thuật đề cập ngắn gọn hơn), đó là một sự hoàn hảo câu trả lời hợp lý.
Charles Duffy

192

Chuyển một giá trị đến stdintrong bash đơn giản như sau:

your-command <<< "$your_variable"

Luôn đảm bảo rằng bạn đặt dấu ngoặc kép xung quanh các biểu thức biến!

Hãy thận trọng, điều này có thể chỉ hoạt động trong bashvà sẽ không hoạt động sh.


39
Các chuỗi <<<này không được đảm bảo là có sẵn, chúng không có trong cơ sở POSIX, theo như tôi biết. Chúng sẽ hoạt động như mong đợi, miễn là bạn chỉ chạy chúng vào bash. Tôi chỉ đề cập đến nó, bởi vì họ OP nói "shell" chứ không phải "bash" cụ thể. Mặc dù anh ấy đã gắn thẻ câu hỏi với bash... vì vậy đây rõ ràng là một câu trả lời có thể chấp nhận được.
JM Becker

Mặc dù có thể đúng như vậy, nhưng thông tin nhạy cảm có khả năng bị rò rỉ dễ dàng hơn nếu sử dụng echophương pháp do tạo đường ống. Lệnh chuỗi này sẽ được xử lý hoàn toàn bởi bash, mặc dù trong tình huống này (như đã lưu ý), bạn nên biết rằng nó sẽ hoạt động trên cơ sở dữ liệu của bạn, nếu không, bất kỳ thông báo lỗi nào được tạo ra do không hỗ trợ chuỗi đây cũng sẽ làm rò rỉ thông tin đã nêu.
Steven Lu

@StevenLu Wait, echođược tích hợp sẵn. Không có đường ống ở đó, phải không? Đó là Unix, nhưng không thể là Unix nhiều như vậy .
Camilo Martin

4
@StevenLu printf '%s\n' "$var"tạo ra kết quả tương tự echo "$var"nhưng sẽ không bị phá vỡ nếu, ví dụ var=-en, và thậm chí không yêu cầu bash.
Camilo Martin

1
Cũng lưu ý rằng một dòng mới được nối vào chuỗi cho các chuỗi ở đây.
pdr

19

Lưu ý rằng các echo "$var" | commandhoạt động ' có nghĩa là đầu vào tiêu chuẩn bị giới hạn ở (các) dòng được lặp lại. Nếu bạn cũng muốn thiết bị đầu cuối được kết nối, thì bạn sẽ cần phải khéo léo hơn:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Điều này có nghĩa là (các) dòng đầu tiên sẽ là nội dung của $varnhưng phần còn lại sẽ đến từ catviệc đọc đầu vào chuẩn của nó. Nếu lệnh không làm bất cứ điều gì quá lạ mắt (hãy thử bật chỉnh sửa dòng lệnh hoặc chạy tương tự vim) thì nó sẽ ổn. Nếu không, bạn cần phải thực sự ưa thích - tôi nghĩ expecthoặc một trong các dẫn xuất của nó có khả năng phù hợp.

Các ký hiệu dòng lệnh trên thực tế giống hệt nhau - nhưng dấu chấm phẩy thứ hai là cần thiết với dấu ngoặc nhọn trong khi nó không có dấu ngoặc đơn.


( echo "$LIST"; cat - ) | sed 1qcái này phù hợp với tôi nhưng tôi cần nhấn ctrl d khi chạy tập lệnh này?
Gert Cuykens

Đúng; các cat -tiếp tục đọc từ bàn phím cho đến khi EOF hoặc ngắt, vì vậy bạn cần phải nói cho nó EOF bằng cách gõ control-D.
Jonathan Leffler

có cách nào xung quanh EOF không? sed cần mèo
Gert Cuykens

Bạn không cần phải sử dụng catgì cả nếu không muốn nhập đầu cuối, như trong dòng đầu tiên. Hoặc bạn có thể sử dụng catđể liệt kê một tệp. Hoặc ... Nếu bạn muốn lệnh đọc đầu vào đầu cuối, bạn phải cho nó biết khi nào nó đã đến cuối đầu vào. Hoặc bạn có thể sử dụng ( echo "$var"; sed /quit/q - ) | command; điều này tiếp tục cho đến khi bạn nhập một dòng có chứa 'thoát'. Bạn có thể sáng tạo không ngừng với cách bạn xử lý nó. Hãy cẩn thận với truyền thuyết đô thị cũ về một chương trình ngừng hoạt động khi người dùng bắt đầu làm việc với Ecuador. Họ gõ tên thủ đô, Quito, và chương trình đã thoát.
Jonathan Leffler

Nếu em nói vậy thì được thôi. Nhưng tại sao không chỉ echo "$LIST" | sed 1q | ...? Tất cả phụ thuộc vào những gì bạn đang có. Các <<EOFký hiệu nên yêu cầu một EOF vào lúc bắt đầu của một dòng trên đâu đó riêng của mình xuống tập tin. Tôi nghĩ là tôi sẽ không sử dụng nó - nhưng tôi vẫn không chắc bạn đang cố gắng đạt được điều gì. Một đơn giản echo "$LIST" | commandhoặc echo $LIST | commandcó thể sẽ đủ trong thực tế (và điều quan trọng là bạn phải biết tầm quan trọng của sự khác biệt giữa hai).
Jonathan Leffler

16

Tôi thích câu trả lời của Martin, nhưng nó có một số vấn đề tùy thuộc vào những gì có trong biến. Điều này

your-command <<< """$your_variable"""

sẽ tốt hơn nếu biến của bạn chứa "hoặc!


4
Nhưng tại sao? Ngoài ra, tôi không thể tái tạo bất kỳ vấn đề nào: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"hoạt động tốt đối với tôi. Chính xác thì cả ba "làm gì? AFAIK bạn chỉ đang thêm vào trước và thêm một chuỗi trống.
phk

Hãy thử một cái gì đó thực tế. Giống như lệnh của bạn là ssh somehost. và biến của bạn là một tập lệnh shell.
Robert Jacobs,

16
(cat <<END
$passwd
END
) | command

Các cat không phải là thực sự cần thiết, nhưng nó giúp để cấu trúc mã tốt hơn và cho phép bạn sử dụng nhiều lệnh trong ngoặc là đầu vào cho lệnh của bạn.


Nhưng phương pháp này cho phép bạn vượt qua nhiều dòng lệnh của bạn và cũng cho phép không gian trong$passwd
PoltoS

4
Đây là câu trả lời tốt nhất cho đến nay mà không làm rò rỉ nội dung thay đổi vào đường ống hoặc quá trình rình mò.
user2688272

8

Theo câu trả lời của Martin, có một tính năng cơ bản được gọi là Here Strings (bản thân nó là một biến thể của tính năng Here Documents được hỗ trợ rộng rãi hơn ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Chuỗi đây

Một biến thể của tài liệu ở đây, định dạng là:

<<< word

Từ được mở rộng và cung cấp cho lệnh trên đầu vào chuẩn của nó.

Lưu ý rằng Here Strings dường như chỉ ở chế độ bash, vì vậy, để cải thiện tính di động, bạn có thể nên sử dụng tính năng Here Documents ban đầu, theo câu trả lời của PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Hoặc, một biến thể đơn giản hơn của ở trên:

(cmd <<EOF
$variable
EOF
)

Bạn có thể bỏ qua (), trừ khi bạn muốn chuyển hướng này sang các lệnh khác.


5

Cách mạnh mẽ và di động này đã xuất hiện trong các bình luận. Nó phải là một câu trả lời độc lập.

printf '%s' "$var" | my_cmd

hoặc là

printf '%s\n' "$var" | my_cmd

Ghi chú:

  • Nó tốt hơn echo, lý do là ở đây: Tại sao printftốt hơn echo?
  • printf "$var"sai. Đối số đầu tiên là định dạng trong đó các chuỗi khác nhau thích %shoặc \nđược diễn giải . Để chuyển quyền biến, nó không được hiểu là định dạng.
  • Thông thường các biến không chứa các dòng mới ở cuối. Lệnh cũ (with %s) chuyển biến như cũ. Tuy nhiên, các công cụ làm việc với văn bản có thể bỏ qua hoặc phàn nàn về một dòng không hoàn chỉnh (xem Tại sao tệp văn bản phải kết thúc bằng một dòng mới? ). Vì vậy, bạn có thể muốn lệnh sau (with %s\n) gắn một ký tự dòng mới vào nội dung của biến. Sự thật không rõ ràng:

    • Đây chuỗi trong Bash (<<<"$var" my_cmd ) không nối một dòng mới.
    • Bất kỳ phương thức nào thêm vào một dòng mới dẫn đến stdin không rỗng my_cmd, ngay cả khi biến trống hoặc không xác định.

4

Thử cái này:

echo "$variable" | command

nhưng nội dung $ biến sẽ không hiển thị trong ví dụ như đầu ra của ps -ukhi tiếng vọng đang chạy?

2
không, nó sẽ không. echolà một built-in, vì vậy không có quá trình để hiển thị trong ps
unbeli

2
Cẩn thận với khoảng trắng trong biến của bạn: echo "$variable"tốt hơn.
Jonathan Leffler

đó là đúng, bash sẽ ăn gian đôi, vỏ thông minh sẽ không
unbeli

-1

Cứ làm đi:

printf "$my_var" | my_cmd

Nếu var không chứa khoảng trắng thì dấu ngoặc kép có thể bị bỏ qua.
Nếu sử dụng bash thì bạn cũng có thể làm:

echo -n "$my_var" | my_cmd

Tránh sử dụng echo mà không có -n vì nó sẽ đặt vraiable có thêm dấu ngắt dòng ở cuối.


1
không hoạt động nếu $ my_var là %s, printf sẽ không in bất cứ thứ gì. my_var="%s"; printf "$my_var"; - có thể thử printf "%s" "$my_var" | my_cmd ?
hanshenrik 21/09/18
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.