Làm cách nào để tìm chiều rộng và chiều cao của cửa sổ đầu cuối?


295

Một ví dụ đơn giản, tôi muốn viết một tập lệnh CLI có thể in =trên toàn bộ chiều rộng của cửa sổ terminal.

#!/usr/bin/env php
<?php
echo str_repeat('=', ???);

hoặc là

#!/usr/bin/env python
print '=' * ???

hoặc là

#!/usr/bin/env bash
x=0
while [ $x -lt ??? ]; do echo -n '='; let x=$x+1 done; echo

Tôi đã tạo lib node.js nhỏ này để liên tục nhận được kích thước cửa sổ chính xác npmjs.com/package/window-size
jonschlinkert

Câu trả lời:


562
  • tput cols cho bạn biết số lượng cột.
  • tput lines cho bạn biết số lượng hàng.

11
echo -e "lines\ncols"|tput -Sđể có được cả dòng và cols, hãy xem: linux.about.com/l Library / cmd / blcmdl1_tput.htmlm
nickl-

4
tputlà một lệnh tuyệt vời với rất nhiều lệnh để đọc trạng thái của thiết bị đầu cuối, kiểm soát các thuộc tính con trỏ và văn bản, v.v.
Drew Noakes

2
Bí danh tiện dụng, ví dụ : alias dim="echo $(tput cols)x$(tput lines)", có thể dẫn đến 80x50.
giám mục

2
Câu hỏi và trả lời này có lẽ thuộc về các trang web SE unix hoặc superuser.
mydoghasworms

2
@bishop lệnh bí danh bạn cung cấp được đánh giá khi shell được lấy nguồn. Bạn cần sử dụng dấu ngoặc đơn cho lệnh bí danh. Giống như vậy: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins

103

Trong bash, các biến $LINES$COLUMNSmôi trường sẽ có thể thực hiện thủ thuật. Sẽ được đặt tự động khi có bất kỳ thay đổi nào về kích thước thiết bị đầu cuối. (tức là tín hiệu SIGWINCH )


18
Tuy nhiên, các biến môi trường này chỉ có sẵn cho bash, và không có bất kỳ chương trình nào chạy bên trong bash (như perl, python, ruby).
Br.Bill

9
Điều đó không hoạt động trong bất cứ điều gì ngoại trừ phiên bash tương tác (nếu bạn chạy tập lệnh thì nó không còn tương tác nữa). Nơi duy nhất bạn có thể sử dụng nó trong một tập lệnh là prompt_command trong bash.
Doncho Gunchev

1
Trên thực tế, nó hoạt động trong các tập lệnh không tương tác, nếu bạn đặt checkwinsizetùy chọn. Ví dụ: tập lệnh không tương tác này sẽ in các kích thước của thiết bị đầu cuối mà nó được chạy: shopt -s checkwinsize; (:); echo $LINES $COLUMNS( checkwinsizetùy chọn chỉ khởi tạo các biến sau khi chờ một chuỗi con kết thúc, đó là lý do tại sao chúng ta cần (:)câu lệnh)
user3340662

$LINES$COLUMNSđược cập nhật sau khi SIGWINCHđược gửi, thực sự sau khi thực hiện bất kỳ lệnh tương tác nào. Nếu bạn cố gắng cập nhật PS1với trap SIGWINCHbạn không thể sử dụng $LINES$COLUMNS, họ giữ giá trị cũ ((
gavenkoa

LINESCOLUMNSchỉ được đặt làm biến shell bằng bash. Bash sẽ không đặt chúng làm biến môi trường , trừ khi bạn xuất các biến shell này.
Markus Kuhn

67

Và có stty, từ coreutils

$ stty size
60 120 # <= sample output

Nó sẽ in số lượng hàng và cột, hoặc chiều cao và chiều rộng, tương ứng.

Sau đó, bạn có thể sử dụng cuthoặcawk để trích xuất phần bạn muốn.

Đó là stty size | cut -d" " -f1cho chiều cao / dòng và stty size | cut -d" " -f2cho chiều rộng / cột


Kiểu này không thể hoạt động với PIPE, đề nghị sử dụng kiểu tput.
liuyang1

6
vấn đề với tput là nó không phải lúc nào cũng có sẵn trong khi stty có sẵn trong mỗi tty. cảm ơn vì thông tin đó
iRaS

16
yes = | head -n$(($(tput lines) * $COLUMNS)) | tr -d '\n'

3
Không phải là một câu trả lời trực tiếp cho câu hỏi, nhưng một kịch bản demo tuyệt vời.
Chris Trang

Thật là một ví dụ tuyệt vời!
Kurt Zhong

1
Làm thế quái nào tôi bỏ lỡ trlệnh trong tất cả các năm? (facepalm)
Marco Massenzio

Ngôn ngữ này là gì? Nó trông giống như một kịch bản shell với lỗi cú pháp. (Trong vỏ, bạn không thể có khoảng trắng xung quanh dấu bằng, và đường ống đầu tiên có vẻ không đúng chỗ.)
tripleee

2
yes '='sẽ tạo ra một số lượng vô hạn các dòng '=' và các lệnh sau sắp xếp đủ để lấp đầy thiết bị đầu cuối
pixelbeat

12

Để làm điều này trong môi trường Windows CLI, cách tốt nhất tôi có thể tìm thấy là sử dụng lệnh mode và phân tích đầu ra.

function getTerminalSizeOnWindows() {
  $output = array();
  $size = array('width'=>0,'height'=>0);
  exec('mode',$output);
  foreach($output as $line) {
    $matches = array();
    $w = preg_match('/^\s*columns\:?\s*(\d+)\s*$/i',$line,$matches);
    if($w) {
      $size['width'] = intval($matches[1]);
    } else {
      $h = preg_match('/^\s*lines\:?\s*(\d+)\s*$/i',$line,$matches);
      if($h) {
        $size['height'] = intval($matches[1]);
      }
    }
    if($size['width'] AND $size['height']) {
      break;
    }
  }
  return $size;
}

Tôi hy vọng nó hữu ích!

GHI CHÚ : Chiều cao được trả về là số dòng trong bộ đệm, nó không phải là số dòng có thể nhìn thấy trong cửa sổ. Có lựa chọn nào tốt hơn ngoài kia không?


3
Lưu ý một vấn đề với điều này: đầu ra của lệnh này là đặc trưng cho miền địa phương. Nói cách khác, điều này sẽ không hoạt động như trên một ngôn ngữ Windows khác. Đây là những gì tôi nhận được trên Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin

Đã thêm một câu trả lời với một giải pháp cho điều đó. +1 mặc dù!
Camilo Martin

10

Trên POSIX, cuối cùng bạn muốn thực hiện cuộc gọi TIOCGWINSZ(Nhận WINdow SiZe) ioctl(). Hầu hết các ngôn ngữ nên có một số loại bao bọc cho điều đó. Ví dụ: trong Perl bạn có thể sử dụng Term :: Size :

use Term::Size qw( chars );

my ( $columns, $rows ) = chars \*STDOUT;

1
Cảm ơn vì điều này - đã dẫn tôi đi đúng hướng. Elixir: :io.columnsErlang: io:columns(). erlang.org/doc/man/io.html#columns-0
Henrik N

2
Không có TIOCGWINSZtrong tiêu chuẩn POSIX và ioctl()chỉ được xác định cho tính năng STREAM lỗi thời.
osvein

4

Như tôi đã đề cập trong câu trả lời của lyceus, mã của anh ta sẽ thất bại trên Windows không phải tiếng Anh vì khi đó đầu ra của modecó thể không chứa các "cột" hoặc "dòng":

                                         chế độ đầu ra lệnh

Bạn có thể tìm đúng chuỗi con mà không cần tìm văn bản:

 preg_match('/---+(\n[^|]+?){2}(?<cols>\d+)/', `mode`, $matches);
 $cols = $matches['cols'];

Lưu ý rằng tôi thậm chí không bận tâm đến các dòng vì nó không đáng tin cậy (và tôi thực sự không quan tâm đến chúng).

Chỉnh sửa: Theo nhận xét về Windows 8 (oh bạn ...), tôi nghĩ rằng điều này có thể đáng tin cậy hơn:

 preg_match('/CON.*:(\n[^|]+?){3}(?<cols>\d+)/', `mode`, $matches);
 $cols = $matches['cols'];

Mặc dù vậy, hãy kiểm tra nó vì tôi đã không kiểm tra nó.


Phương pháp của bạn không hoạt động trong Win8. Tôi nhận được nhiều hơn một ---dòng. i.imgur.com/4x02dqT.png
mở

@Mark Vâng, thật tuyệt, đó chỉ là Đ BEP. Cảm ơn bạn Windows. <3 (trên một lưu ý phù hợp hơn: Tôi sẽ tìm hiểu cách khắc phục điều đó ... khi Windows 9 xuất hiện: P).
Camilo Martin

Đây là cách tôi làm nó : $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. Và sau đó tôi chỉ thay thế mọi thứ trừ những con số.
Alexanderr Makov

@AleksandrMakov Tôi tự hỏi chuyện gì sẽ xảy ra nếu có ngôn ngữ với thứ tự như thế CON device status:nào? Có lẽ phù hợp với một cái gì đó như CON.*:sẽ làm việc tốt hơn.
Camilo Martin

1
@Mark Tôi đã thực sự tự đặt câu hỏi cho chính mình điều đó. Tại sao tôi làm điều đó? Nghi ngờ, tôi chỉ cho rằng có một số lý do và đã đi với nó, lol.
Camilo Martin

1

Lấy cảm hứng từ câu trả lời của @ pixelbeat, đây là một thanh ngang được tạo ra bằng cách tputsử dụng sai một chút printfđệm / điền vàtr

printf "%0$(tput cols)d" 0|tr '0' '='

0

Có một số trường hợp hàng / DÒNG và cột của bạn không khớp với kích thước thực của "thiết bị đầu cuối" đang được sử dụng. Có lẽ bạn có thể không có sẵn "tput" hoặc "stty".

Đây là một hàm bash bạn có thể sử dụng để kiểm tra kích thước một cách trực quan. Điều này sẽ làm việc lên đến 140 cột x 80 hàng. Bạn có thể điều chỉnh mức tối đa khi cần thiết.

function term_size
{
    local i=0 digits='' tens_fmt='' tens_args=()
    for i in {80..8}
    do
        echo $i $(( i - 2 ))
    done
    echo "If columns below wrap, LINES is first number in highest line above,"
    echo "If truncated, LINES is second number."
    for i in {1..14}
    do
        digits="${digits}1234567890"
        tens_fmt="${tens_fmt}%10d"
        tens_args=("${tens_args[@]}" $i)
    done
    printf "$tens_fmt\n" "${tens_args[@]}"
    echo "$digits"
}
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.