Làm thế nào để thực hiện đầu ra của một lệnh trong trình bao hiện tại?


89

Tôi biết rõ về tiện ích source(hay còn gọi là .), tiện ích này sẽ lấy nội dung từ tệp và thực thi chúng trong trình bao hiện tại.

Bây giờ, tôi đang chuyển đổi một số văn bản thành các lệnh shell và sau đó chạy chúng, như sau:

$ ls | sed ... | sh

lschỉ là một ví dụ ngẫu nhiên, văn bản gốc có thể là bất cứ thứ gì. sedquá, chỉ là một ví dụ để chuyển đổi văn bản. Điều thú vị là sh. Tôi tẩu bất cứ thứ gì tôi có shvà nó chạy nó.

Vấn đề của tôi là, điều đó có nghĩa là bắt đầu một trình bao phụ mới. Tôi muốn các lệnh chạy trong trình bao hiện tại của mình. Giống như tôi có thể làm được source some-file, nếu tôi có các lệnh trong tệp văn bản.

Tôi không muốn tạo một tệp tạm thời vì cảm thấy bẩn.

Ngoài ra, tôi muốn bắt đầu trình bao phụ của mình với các đặc điểm giống hệt như trình bao hiện tại của tôi.

cập nhật

Ok, các giải pháp sử dụng backtick chắc chắn hoạt động, nhưng tôi thường phải làm điều này trong khi kiểm tra và thay đổi đầu ra, vì vậy tôi muốn có cách nào đó để chuyển kết quả thành một thứ gì đó cuối cùng.

cập nhật đáng buồn

Ah, /dev/stdinthứ trông rất đẹp, nhưng, trong một trường hợp phức tạp hơn, nó không hoạt động.

Vì vậy, tôi có cái này:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

Điều này đảm bảo tất cả .doccác tệp đều có phần mở rộng được viết thường.

Và tình cờ, có thể được xử lý với xargs, nhưng đó không phải là vấn đề.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

Vì vậy, khi tôi chạy cái trước, nó sẽ thoát ra ngay lập tức, không có gì xảy ra.


Lệnh phức tạp của bạn có hoạt động khi bạn chuyển đến một tệp tạm thời trước và sau đó tạo nguồn không? Nếu không, vấn đề với đầu ra được tạo là gì? Đầu ra của lệnh của bạn sẽ không hoạt động nếu tên tệp của bạn có khoảng trắng trong đó hoặc nếu một số chuỗi nhất định không được thoát đúng cách. Tôi muốn thêm báo giá tối thiểu khoảng $ 1 và $ 2.doc.
Kaleb Pederson

Có lý do chính đáng nào để phải chạy điều này trong trình bao ban đầu không? - những ví dụ này không thao tác với shell hiện tại, vì vậy bạn không thu được gì khi làm như vậy. Giải pháp nhanh chóng là bạn chuyển hướng đầu ra đến một tệp và nguồn tệp đó mặc dù
không phải là

@kaleb đầu ra chạy tốt. trong trường hợp cụ thể này, ngay cả khi tôi chuyển sang sh. tên tệp là không gian an toàn, nhưng cảm ơn bạn đã lưu ý. @nos git biến môi trường trên shell gốc. và một lần nữa, đây chỉ là những ví dụ. câu hỏi dành cho cuộc sống.
kch

source / dev / stdin không hoạt động với tôi khi cần các biến được chỉ định để gắn bó. geirha trên freenode bash chỉ cho tôi để mywiki.wooledge.org/BashFAQ/024 và đề nghị tôi thử một nguồn thay thế tiến trình <(lệnh) mà làm việc cho tôi
srcerer

Câu trả lời:


85
$ ls | sed ... | source /dev/stdin

CẬP NHẬT: Điều này hoạt động trong bash 4.0, cũng như tcsh và dấu gạch ngang (nếu bạn thay đổi sourcethành .). Rõ ràng đây là lỗi trong bash 3.2. Từ ghi chú phát hành bash 4.0 :

Đã sửa một lỗi gây ra `. ' không thể đọc và thực hiện các lệnh từ các tệp không thông thường như thiết bị hoặc đường ống được đặt tên.


Đồng ý. Sau đó chuyển sang bash 4.0.
kch

Ồ, và vì bạn đang liệt kê các shell nên nó cũng hoạt động trong zsh.
kch

1
Tôi nghĩ điều này thật khéo léo cho đến khi tôi thử nó trong msys / mingw (nơi không có thư mục / dev (hoặc thậm chí cả thiết bị)! Tôi đã thử nhiều cách kết hợp eval, $ () và backticks ''. Tôi không thể làm cho bất cứ điều gì hoạt động , vì vậy cuối cùng tôi chỉ chuyển hướng đầu ra thành tệp tạm thời, lấy nguồn tệp tạm và xóa nó. Về cơ bản "sed ...> /tmp/$$.tmp &&. /tmp/$$.tmp && rm / tmp / $$. tmp ". Có ai có giải pháp msys / mingw mà không có tệp tạm thời không ???
chriv

10
Chỉ một nhận xét: Điều này dường như không xử lý các báo cáo xuất khẩu như mong đợi. Nếu chuỗi piped có câu lệnh xuất, biến exportet sau đó không có sẵn trong môi trường đầu cuối của bạn, trong khi với xuất eval cũng hoạt động hoàn hảo.
Phil

1
Do MacOS X thường có bash 3.2 (có thể Yosemite có 4.0?) Và nhu cầu về thủ thuật lastpipe trong trường hợp sử dụng của tôi (xuất tất cả các biến trong tệp bằng cách xử lý trước mỗi dòng với sed để thêm 'xuất'), tôi đã chọn để thực hiện eval "$(sed ...)"phương pháp tiếp cận - nhưng cảm ơn vì thông báo lỗi 3.2!
Alex Dupuy

133

Các evallệnh tồn tại cho mục đích này rất.

eval "$( ls | sed... )"

Thêm từ hướng dẫn sử dụng bash :

đánh giá

          eval [arguments]

Các đối số được nối với nhau thành một lệnh duy nhất, lệnh này sau đó được đọc và thực thi, và trạng thái thoát của nó được trả về là trạng thái thoát của eval. Nếu không có đối số hoặc chỉ có đối số trống, trạng thái trả về là không.


8
Vấn đề duy nhất ở đây là bạn có thể cần chèn; để tách các lệnh của mình. Bản thân tôi sử dụng phương pháp này để làm việc trên AIX, Sun, HP và Linux.
Tanktalus

Tanktalus, cảm ơn vì nhận xét đó, nó đã làm cho kịch bản của tôi hoạt động tốt. Trên máy tính của tôi, eval không tách các lệnh trên dòng mới và việc sử dụng mã nguồn không hoạt động ngay cả với dấu chấm phẩy. Đánh giá bằng dấu chấm phẩy là giải pháp. Tôi ước tôi có thể cho bạn một số điểm.
Milan Babuškov

2
@ MilanBabuškov: Tôi đã xếp hạng anh ấy cho bạn ;-)
Phil

3
Thật tuyệt vời. Tuy nhiên, đối với trường hợp sử dụng của tôi, tôi đã phải đặt dấu ngoặc kép xung quanh $ () của mình, như vậy: eval "$( ssh remote-host 'cat ~/.bash_profile' )"Lưu ý rằng tôi thực sự đang sử dụng bash 3.2.
Zachary Murray

3
Điều này làm việc cho tôi, nơi câu trả lời được chấp nhận không.
Dan Tenenbaum

37

Chà, tôi biết đây là một câu hỏi cũ, nhưng gần đây tôi đã phát hiện ra mình gặp cùng một vấn đề chính xác (đó là cách tôi đến đây).

Dù sao thì - tôi không thích source /dev/stdincâu trả lời, nhưng tôi nghĩ rằng tôi đã tìm thấy một câu trả lời hay hơn. Thực ra nó rất đơn giản:

echo ls -la | xargs xargs

Tốt đẹp? Trên thực tế, điều này vẫn không làm những gì bạn muốn, bởi vì nếu bạn có nhiều dòng, nó sẽ nối chúng thành một lệnh duy nhất thay vì chạy từng lệnh riêng biệt. Vì vậy, giải pháp tôi tìm thấy là:

ls | ... | xargs -L 1 xargs

các -L 1tùy chọn có nghĩa là bạn sử dụng (ít nhất) 1 dòng cho mỗi thực hiện lệnh. Lưu ý: nếu dòng của bạn kết thúc bằng dấu cách, nó sẽ được nối với dòng tiếp theo! Vì vậy, hãy đảm bảo mỗi dòng kết thúc bằng một khoảng trắng.

Cuối cùng, bạn có thể làm

ls | ... | xargs -L 1 xargs -t

để xem những lệnh nào được thực thi (-t là dài dòng).

Hy vọng ai đó đọc được điều này!


30

Hãy thử sử dụng thay thế quy trình , thay thế đầu ra của một lệnh bằng một tệp tạm thời mà sau đó có thể được lấy nguồn:

source <(echo id)

7
Đây là loại phép thuật nào?
paulotorrens

Đây cũng là suy nghĩ đầu tiên của tôi. Mặc dù tôi thích giải pháp eval hơn. @PauloTorrens <(xyz) chỉ cần thực thi xyz và thay thế <(xyz) bằng tên của một tệp sẽ ghi kết quả đầu ra của xyz. Nó thực sự dễ dàng để hiểu cách thức hoạt động này bằng cách làm ví dụ: echo <(echo id), cho sản lượng /dev/fd/12(12 là một ví dụ), cat <(echo id), cho đầu ra id, và sau đó source <(echo id)đưa ra kết quả tương tự như chỉ đơn giản là viếtid
netigger

2
@PauloTorrens, đây được gọi là quá trình thay thế . Xem các tài liệu được liên kết để biết giải thích chính thức, nhưng câu trả lời ngắn gọn là "<()" là một cú pháp đặc biệt được thiết kế cho những trường hợp như thế này.
Mark Stosberg

1
@MarkStosberg, bạn có biết đây là cú pháp đặc biệt hay chỉ là một vỏ con được (...)chuyển hướng?
paulotorrens

2
@PauloTorrens, Đây là một cú pháp đặc biệt chỉ để thay thế quy trình.
Mark Stosberg

6

Tôi tin rằng đây là "câu trả lời đúng" cho câu hỏi:

ls | sed ... | while read line; do $line; done

Đó là, người ta có thể đặt thành một whilevòng lặp; các readlệnh lệnh đưa một dòng từ của nó stdinvà chuyển nhượng nó vào biến $line. $linesau đó trở thành lệnh được thực hiện trong vòng lặp; và nó tiếp tục cho đến khi không còn dòng nào nữa trong đầu vào của nó.

Điều này vẫn sẽ không hoạt động với một số cấu trúc điều khiển (như một vòng lặp khác), nhưng nó phù hợp với hóa đơn trong trường hợp này.


5
`ls | sed ...`

Tôi cảm thấy mình ls | sed ... | source -sẽ xinh hơn, nhưng tiếc là sourcetôi không hiểu -nghĩa là gì stdin.


Sau khi xem câu trả lời của mark4o, không phải lúc nào nó cũng hiện rõ trên khuôn mặt của chúng ta?
kch

Đúng vậy. Tôi không bao giờ nhớ rằng thứ đó tồn tại.
hỗn loạn

1

Tôi nghĩ giải pháp của bạn là thay thế lệnh bằng dấu gạch ngược: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Xem phần 3.4.5


3
Nếu bạn đang sử dụng bash, $( )cú pháp có thể được ưu tiên hơn. 'Backticks Khóa học làm việc trong nhiều vỏ ...
dmckee --- cựu điều hành kitten

6
$ (lệnh) và $ ((1 + 1)) hoạt động trong tất cả các trình bao posix. Tôi nghĩ rằng nhiều vim đánh dấu chúng là lỗi cú pháp, nhưng đó chỉ là vì vim đang làm nổi bật cho shell Bourne ban đầu mà rất ít người sử dụng. Để lấy vim làm nổi bật một cách chính xác, hãy đặt điều này vào .vimrc của bạn: let g: is_posix = 1
pixelbeat

1

Để sử dụng giải pháp của mark4o trên bash 3.2 (macos), một chuỗi here có thể được sử dụng thay vì các đường ống như trong ví dụ này:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"

hoặc sử dụng dấu nền:. / dev / stdin <<< 'grep '^ alias' ~ / .profile`
William H. Hooper

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.