Đặt biến môi trường trước lệnh trong Bash không hoạt động cho lệnh thứ hai trong đường ống


351

Trong một shell nhất định, thông thường tôi sẽ đặt một biến hoặc biến và sau đó chạy lệnh. Gần đây tôi đã biết về khái niệm chuẩn bị một định nghĩa biến cho một lệnh:

FOO=bar somecommand someargs

Điều này hoạt động ... loại. Nó không hoạt động khi bạn thay đổi biến LC_ * (có vẻ như ảnh hưởng đến lệnh, nhưng không phải là đối số của nó, ví dụ, phạm vi char '[az]') hoặc khi đường ống đầu ra sang lệnh khác:

FOO=bar somecommand someargs | somecommand2  # somecommand2 is unaware of FOO

Tôi cũng có thể thêm somecommand2 bằng "FOO = bar", nó hoạt động, nhưng có thêm sự trùng lặp không mong muốn và nó không giúp ích cho các đối số được diễn giải tùy thuộc vào biến (ví dụ: '[az]').

Vì vậy, một cách tốt để làm điều này trên một dòng là gì?

Tôi đang suy nghĩ điều gì đó theo thứ tự:

FOO=bar (somecommand someargs | somecommand2)  # Doesn't actually work

Tôi đã nhận được rất nhiều câu trả lời tốt! Mục tiêu là giữ cho nó một lớp lót, tốt nhất là không sử dụng "xuất khẩu". Phương thức sử dụng lệnh gọi Bash là tổng thể tốt nhất, mặc dù phiên bản gốc có "xuất khẩu" trong đó nhỏ gọn hơn một chút. Phương pháp sử dụng chuyển hướng chứ không phải là một đường ống cũng thú vị.


1
(T=$(date) echo $T)sẽ hoạt động
vp_arth

Trong ngữ cảnh của các tập lệnh đa nền tảng (bao gồm các cửa sổ) hoặc các dự án dựa trên npm (js hoặc khác), bạn có thể muốn xem mô-đun cross-env .
Frank Nocke

5
Tôi đã hy vọng một trong những câu trả lời cũng sẽ giải thích tại sao loại công việc này chỉ có nghĩa là tại sao nó không tương đương với việc xuất biến trước khi gọi.
Brecht Machiels

4
Tại sao được giải thích ở đây: stackoverflow.com/questions/13998075/
trộm

Câu trả lời:


317
FOO=bar bash -c 'somecommand someargs | somecommand2'

2
Điều này đáp ứng các tiêu chí của tôi (một lớp lót mà không cần "xuất khẩu") ... Tôi có cách nào để làm điều này mà không gọi "bash -c" (ví dụ: sử dụng dấu ngoặc đơn sáng tạo)?
MartyMacGyver

1
@MartyMacGyver: Không ai mà tôi có thể nghĩ ra. Nó cũng không hoạt động với niềng răng xoăn.
Tạm dừng cho đến khi có thông báo mới.

7
Lưu ý rằng nếu bạn cần chạy somecommandsudo của mình, bạn cần truyền sudo -Ecờ để truyền các biến mặc dù. Bởi vì các biến có thể giới thiệu lỗ hổng. stackoverflow.com/a/8633575/1695680
ThorSummoner

11
Lưu ý rằng nếu lệnh của bạn đã có hai cấp báo giá thì phương thức này trở nên cực kỳ không thỏa đáng vì trích dẫn địa ngục. Trong tình huống đó xuất khẩu trong subshell là tốt hơn nhiều.
Pushpendre

Odd: trên OSX, FOO_X=foox bash -c 'echo $FOO_X'hoạt động như mong đợi nhưng với tên var cụ thể thì không thành công: DYLD_X=foox bash -c 'echo $DYLD_X'echos blank. cả hai đều sử dụng evalthay vìbash -c
mwag

209

Làm thế nào về việc xuất biến, nhưng chỉ bên trong lớp con?:

(export FOO=bar && somecommand someargs | somecommand2)

Keith có một điểm, để thực hiện vô điều kiện các lệnh, hãy làm điều này:

(export FOO=bar; somecommand someargs | somecommand2)

15
Tôi sẽ sử dụng ;hơn là &&; không có cách nào export FOO=barsẽ thất bại.
Keith Thompson

1
@MartyMacGyver: &&thực thi lệnh trái, sau đó thực thi lệnh phải chỉ khi lệnh trái thành công. ;thực hiện cả hai lệnh vô điều kiện. Các lô Windows ( cmd.exe) tương đương ;&.
Keith

2
Trong zsh tôi dường như không cần xuất cho phiên bản này: (FOO=XXX ; echo FOO=$FOO) ; echo FOO=$FOOsản lượng FOO=XXX\nFOO=\n.
hung hăng

3
@PopePoopinpants: tại sao không sử dụng source(aka .) trong trường hợp đó? Ngoài ra, backticks không nên được sử dụng nữa trong những ngày này và đây là một trong những lý do tại sao, sử dụng $(command)là waaaaay an toàn hơn.
0xC0000022L

3
Thật đơn giản, nhưng thật thanh lịch. Và tôi thích câu trả lời của bạn tốt hơn câu trả lời được chấp nhận, vì nó sẽ bắt đầu một lớp vỏ phụ bằng với câu hỏi hiện tại của tôi (có thể không bashnhưng có thể là một cái gì đó khác, ví dụ dash) và tôi không gặp rắc rối nếu tôi phải sử dụng dấu ngoặc kép trong lệnh args ( someargs).
Mecki

45

Bạn cũng có thể sử dụng eval:

FOO=bar eval 'somecommand someargs | somecommand2'

Vì câu trả lời evalnày dường như không làm hài lòng tất cả mọi người, hãy để tôi làm rõ một điều: khi được sử dụng như văn bản, với các trích dẫn duy nhất , nó hoàn toàn an toàn. Điều này là tốt vì nó sẽ không khởi chạy một quy trình bên ngoài (như câu trả lời được chấp nhận) cũng như sẽ không thực hiện các lệnh trong một mạng con phụ (như câu trả lời khác).

Khi chúng tôi nhận được một vài lượt xem thường xuyên, có lẽ tốt để đưa ra một thay thế cho eval sẽ làm hài lòng tất cả mọi người, và có tất cả lợi ích (và thậm chí nhiều hơn nữa!) Của evaltrò lừa nhanh này . Chỉ cần sử dụng một chức năng! Xác định hàm với tất cả các lệnh của bạn:

mypipe() {
    somecommand someargs | somecommand2
}

và thực hiện nó với các biến môi trường của bạn như thế này:

FOO=bar mypipe

7
@ Alfe: Bạn cũng đã downvote câu trả lời được chấp nhận? bởi vì nó thể hiện những vấn đề tương tự như thế giới eval.
gniourf_gniourf

10
@ Alfe: tiếc là tôi không đồng ý với bài phê bình của bạn. Lệnh này là hoàn toàn an toàn. Bạn thực sự nghe giống như một anh chàng đã từng đọc evallà xấu xa mà không hiểu những gì xấu xa eval. Và có lẽ bạn không thực sự hiểu câu trả lời này (và thực sự không có gì sai với nó). Trên cùng một cấp độ: bạn sẽ nói điều đó lslà xấu bởi vì for file in $(ls), là xấu? (và vâng, bạn đã không đánh giá thấp câu trả lời được chấp nhận và bạn cũng không để lại nhận xét nào). SO là một nơi kỳ lạ và vô lý đôi khi.
gniourf_gniourf

7
@ Alfe: khi tôi nói Bạn thực sự nghe giống như một anh chàng đã từng đọc evallà xấu xa mà không hiểu điều gì xấu eval, tôi đang đề cập đến câu của bạn: Câu trả lời này thiếu tất cả các cảnh báo và giải thích cần thiết khi nói về eval. evalkhông xấu hay nguy hiểm; không nhiều hơn bash -c.
gniourf_gniourf

1
Bỏ phiếu sang một bên, bình luận được cung cấp @Alfe bằng cách nào đó ngụ ý rằng câu trả lời được chấp nhận bằng cách nào đó an toàn hơn. Điều gì sẽ hữu ích hơn sẽ là cho bạn để mô tả những gì bạn tin là không an toàn về việc sử dụng eval. Trong câu trả lời được cung cấp, các đối số đã được trích dẫn bảo vệ khỏi sự mở rộng biến, vì vậy tôi thấy không có vấn đề gì với câu trả lời.
Brett Ryan

Tôi đã xóa các bình luận của mình để tập trung mối quan tâm của mình vào một bình luận mới: evallà một vấn đề bảo mật nói chung (như bash -cnhưng ít rõ ràng hơn), vì vậy những nguy hiểm cần được đề cập trong một câu trả lời đề xuất sử dụng nó. Người dùng bất cẩn có thể lấy câu trả lời ( FOO=bar eval …) và áp dụng nó vào tình huống của họ để nó gây ra vấn đề. Nhưng rõ ràng điều quan trọng hơn đối với người trả lời là tìm hiểu xem liệu tôi có hạ thấp câu trả lời của anh ấy và / hoặc khác không để cải thiện bất cứ điều gì. Như tôi đã viết trước đây, sự công bằng không phải là mối quan tâm chính; là không hề thua kém so với bất kỳ câu trả lời cho khác cũng là bất kể.
Alfe

12

Sử dụng env.

Ví dụ , env FOO=BAR command. Lưu ý rằng các biến môi trường sẽ được khôi phục / không thay đổi một lần nữa khi commandkết thúc thực thi.

Chỉ cần cẩn thận về việc thay thế shell xảy ra, tức là nếu bạn muốn tham chiếu $FOOrõ ràng trên cùng một dòng lệnh, bạn có thể cần phải thoát nó để trình thông dịch shell của bạn không thực hiện thay thế trước khi nó chạy env.

$ export FOO=BAR
$ env FOO=FUBAR bash -c 'echo $FOO'
FUBAR
$ echo $FOO
BAR

-5

Sử dụng tập lệnh shell:

#!/bin/bash
# myscript
FOO=bar
somecommand someargs | somecommand2

> ./myscript

13
Bạn vẫn cần export; mặt khác $FOOsẽ là biến shell, không phải là biến môi trường và do đó không thể nhìn thấy somecommandhoặc somecommand2.
Keith Thompson

Nó hoạt động nhưng nó đánh bại mục đích có lệnh một dòng (Tôi đang cố gắng học nhiều cách sáng tạo hơn để tránh nhiều lớp và / hoặc tập lệnh cho các trường hợp tương đối đơn giản). Và những gì @Keith đã nói, mặc dù ít nhất việc xuất khẩu sẽ nằm trong phạm vi kịch bản.
MartyMacGyver
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.