Tôi đã có một giải pháp được viết cho việc tuân thủ vỏ POSIX, nhưng tôi đã thử nghiệm nó chỉ trong bash, vì vậy tôi không biết chắc là nó có di động hay không. Và tôi không biết zsh, vì vậy tôi đã không cố gắng làm cho nó thân thiện với zsh. Bạn đặt lệnh của bạn vào nó; truyền một lệnh làm đối số cho một lệnh khác là một thiết kế xấu * .
Tất nhiên mọi giải pháp cho vấn đề này cần biết thiết bị đầu cuối có bao nhiêu hàng và cột. Trong mã dưới đây, tôi đã giả định rằng bạn có thể dựa vào các biến LINES
và COLUMNS
môi trường ( less
nhìn vào). Các phương pháp đáng tin cậy hơn là:
- sử dụng
rows="${LINES:=$(tput lines)}"
và cols="${COLUMNS:=$(tput cols)}"
, theo đề xuất của AP , hoặc
- nhìn vào đầu ra từ
stty size
. Lưu ý rằng lệnh này phải có thiết bị đầu cuối làm đầu vào tiêu chuẩn của nó, vì vậy, nếu đó là trong tập lệnh và bạn đang chuyển sang tập lệnh, bạn sẽ phải nói stty size <&1
(trong bash) hoặc stty size < /dev/tty
. Nắm bắt đầu ra của nó thậm chí còn phức tạp hơn.
Thành phần bí mật: fold
lệnh sẽ ngắt các dòng dài theo cách màn hình sẽ có, do đó tập lệnh có thể xử lý các dòng dài một cách chính xác.
#!/bin/sh
buffer=$(mktemp)
rows="$LINES"
cols="$COLUMNS"
while true
do
IFS= read -r some_data
e=$? # 1 if EOF, 0 if normal, successful read.
printf "%s" "$some_data" >> "$buffer"
if [ "$e" = 0 ]
then
printf "\n" >> "$buffer"
fi
if [ $(fold -w"$cols" "$buffer" | wc -l) -lt "$rows" ]
then
if [ "$e" != 0 ]
then
cat "$buffer"
else
continue
fi
else
if [ "$e" != 0 ]
then
"${PAGER:="less"}" < "$buffer"
# The above is equivalent to
# cat "$buffer" | "${PAGER:="less"}"
# … but that’s a UUOC.
else
cat "$buffer" - | "${PAGER:="less"}"
fi
fi
break
done
rm "$buffer"
Để sử dụng điều này:
- Đặt ở trên vào một tập tin; chúng ta hãy giả sử bạn gọi nó
mypager
.
- (Tùy chọn) đặt nó vào một thư mục đó là đường dẫn tìm kiếm của bạn; ví dụ
$HOME/bin
.
- Làm cho nó thực thi bằng cách gõ
chmod +x mypager
.
- Sử dụng nó trong các lệnh như
ps ax | mypager
hoặc ls -la | mypager
.
Nếu bạn bỏ qua bước thứ hai (đưa tập lệnh vào một thư mục là đường dẫn tìm kiếm của bạn), bạn sẽ phải làm , nơi có thể là một đường dẫn tương đối như siêu phạm .ps ax | path_to_mypager/mypager
path_to_mypager
.
* Tại sao việc truyền một lệnh làm (các) đối số cho một lệnh khác là một thiết kế xấu?
I. Tính thẩm mỹ / Sự phù hợp với truyền thống / Triết lý Unix
Unix có một triết lý về Do One Thing và Do It Well . Ví dụ: nếu một chương trình sẽ hiển thị dữ liệu theo một cách nhất định (như máy nhắn tin làm), thì nó cũng không nên gọi cơ chế tạo dữ liệu. Đó là những gì đường ống dành cho.
Không có nhiều chương trình Unix thực thi các lệnh hoặc chương trình do người dùng chỉ định. Hãy xem xét một số điều đó:
- Shell, như trong
Well, chạy các lệnh do người dùng chỉ định là công việc của shell ; Đó là điều duy nhất mà cái vỏ làm được. (Tất nhiên tôi không nói rằng shell là một chương trình đơn giản.)
sh -c "command"
env
, nice
, nohup
, setsid
, su
, Và sudo
. Các chương trình này có điểm chung - tất cả đều tồn tại để chạy chương trình có môi trường thực thi được sửa đổi 1 . Họ phải làm việc theo cách họ làm, vì Unix thường không cho phép bạn thay đổi môi trường thực thi của một quy trình khác; bạn phải thay đổi quy trình của riêng mình, và sau đó fork
và / hoặc exec
.
_______
1 Tôi đang sử dụng môi trường thực thi cụm từ
theo nghĩa rộng, không chỉ đề cập đến các biến môi trường, mà còn xử lý các thuộc tính như nice
giá trị của Dinh, UID và GID, nhóm quy trình, ID phiên, thiết bị đầu cuối kiểm soát, tệp mở, thư mục làm việc , umask
giá trị, ulimit
s, bố trí tín hiệu,alarm
hẹn giờ, vv
- Các chương trình cho phép thoát khỏi vỏ sò. Ví dụ duy nhất nảy sinh trong đầu là
vi
/ vim
, mặc dù tôi khá chắc chắn rằng có những người khác. Đây là những cổ vật lịch sử. Họ có trước các hệ thống cửa sổ và thậm chí kiểm soát công việc; nếu bạn đang chỉnh sửa một tệp và bạn muốn làm một cái gì đó khác (như xem danh sách thư mục), bạn sẽ phải lưu tệp của mình và thoát khỏi trình chỉnh sửa để quay lại trình bao của bạn. Ngày nay, bạn có thể chuyển sang một cửa sổ khác hoặc sử dụng Ctrl+ Z(hoặc loại :suspend
) để quay lại trình bao của mình trong khi giữ cho trình soạn thảo của bạn tồn tại, do đó, thoát khỏi trình bao, bị cho là lỗi thời.
Tôi không tính các chương trình thực thi các chương trình (mã hóa cứng) khác để tận dụng khả năng của chúng thay vì sao chép chúng. Ví dụ, một số chương trình có thể thực thi diff
hoặc sort
. (Ví dụ: có những câu chuyện kể rằng các phiên bản đầu spell
được sử dụng sort -u
để lấy danh sách các từ được sử dụng trong tài liệu, và sau đó diff
- hoặc có lẽ comm
- để so sánh danh sách đó với danh sách từ trong từ điển và xác định từ nào trong tài liệu không có trong từ điển.)
II. Vấn đề thời gian
Cách tập lệnh của bạn được viết, RET="$($@)"
dòng không hoàn thành cho đến khi lệnh được gọi hoàn thành. Do đó, tập lệnh của bạn không thể bắt đầu hiển thị dữ liệu cho đến khi lệnh tạo nó hoàn thành. Có lẽ cách đơn giản nhất để khắc phục đó là làm cho lệnh tạo dữ liệu tách biệt với chương trình hiển thị dữ liệu (mặc dù có nhiều cách khác).
III. Lịch sử chỉ huy
Giả sử bạn chạy một số lệnh với đầu ra được xử lý bởi bộ lọc hiển thị của bạn và bạn nhìn vào đầu ra và quyết định rằng bạn muốn lưu đầu ra đó trong một tệp. Nếu bạn đã gõ (như một ví dụ giả định)
ps ax | mypager
sau đó bạn có thể gõ
!:1 > myfile
hoặc nhấn ↑và chỉnh sửa dòng thích hợp. Bây giờ, nếu bạn đã gõ
mypager "ps ax"
bạn vẫn có thể quay lại và chỉnh sửa lệnh đó ps ax > myfile
, nhưng nó không đơn giản như vậy.
Hoặc giả sử bạn quyết định rằng bạn muốn chạy ps uax
tiếp theo. Nếu bạn đã gõ ps ax | mypager
, bạn có thể làm
!:0 u!:*
Một lần nữa, với mypager "ps ax"
, nó vẫn có thể làm được, nhưng, có thể nói là khó hơn.
Ngoài ra, hãy nhìn vào hai lệnh: ps ax | mypager
và mypager "ps ax"
. Giả sử bạn chạy một history
danh sách một giờ sau đó. ISTM mà bạn phải xem xét kỹ hơn mypager "ps ax"
một chút để xem lệnh đang được thực thi là gì.
IV. Lệnh / trích dẫn phức tạp
echo {1..10000}
rõ ràng chỉ là một lệnh ví dụ;
ps ax
không tốt hơn nhiều Điều gì nếu bạn muốn làm điều gì đó chỉ là một chút chút thực tế hơn, như ps ax | grep oracle
? Nếu bạn gõ
mypager ps ax | grep oracle
nó sẽ chạy mypager ps ax
và dẫn đầu ra từ đó thông qua grep oracle
. Vì vậy, nếu đầu ra từ ps ax
dài 30 dòng,
mypager
sẽ gọi less
, ngay cả khi đầu ra từ ps ax | grep oracle
chỉ có 3 dòng. Có lẽ có những ví dụ sẽ thất bại trong một thời trang ấn tượng hơn.
Vì vậy, bạn phải làm những gì tôi đã thể hiện trước đó:
mypager "ps ax | grep oracle"
Nhưng RET="$($@)"
không thể xử lý điều đó. Tất nhiên, có nhiều cách để xử lý những việc như vậy, nhưng chúng không được khuyến khích.
Điều gì xảy ra nếu dòng lệnh mà đầu ra bạn muốn nắm bắt thậm chí còn phức tạp hơn; ví dụ,
lệnh 1 " arg 1 " | lệnh 2 ' arg 2 ' $ ' arg 3 '
nơi các đối số chứa kết hợp lộn xộn không gian, tab,
$
, |
, \
, <
, >
, *
, ;
, &
, [
, ]
, (
, )
, `
, và thậm chí có '
và "
. Một lệnh như thế có thể đủ cứng để gõ trực tiếp vào shell một cách chính xác. Bây giờ hãy tưởng tượng cơn ác mộng của việc phải trích dẫn nó để vượt qua nó như là một đối số mypager
.
wc -l
đang đếm các ký tự dòng mới, nhưng nếu nội dung là tất cả một dòng, nhưng kết thúc đủ để tắt thiết bị đầu cuối thì sao?