ฉันจะค้นหาความกว้าง & ความสูงของหน้าต่างเทอร์มินัลได้อย่างไร


295

ตัวอย่างง่ายๆฉันต้องการเขียนสคริปต์ CLI ซึ่งสามารถพิมพ์=ข้ามความกว้างทั้งหมดของหน้าต่างเทอร์มินัล

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

หรือ

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

หรือ

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

ฉันสร้าง node.js lib ขนาดเล็กนี้เพื่อรับขนาดหน้าต่างที่ถูกต้องnpmjs.com/package/window-size
jonschlinkert

คำตอบ:


562
  • tput cols บอกจำนวนคอลัมน์
  • tput lines บอกจำนวนแถว

11
echo -e "lines\ncols"|tput -Sเพื่อรับทั้งบรรทัดและ cols ดู: linux.about.com/library/cmd/blcmdl1_tput.htm
nickl-

4
tputเป็นคำสั่งที่ยอดเยี่ยมพร้อมคำสั่งมากมายสำหรับการอ่านสถานะของเทอร์มินัลการควบคุมคุณสมบัติเคอร์เซอร์และข้อความเป็นต้น
Drew Noakes

2
นามแฝงที่มีประโยชน์เช่น: ซึ่งอาจส่งผลให้alias dim="echo $(tput cols)x$(tput lines)" 80x50
อธิการ

2
คำถาม & คำตอบนี้อาจเป็นของไซต์ unix หรือ superuser SE
mydoghasworms

2
@bishop คำสั่ง alias ที่คุณระบุจะได้รับการประเมินเมื่อเชลล์ได้รับแหล่งที่มา คุณต้องใช้เครื่องหมายคำพูดเดี่ยวสำหรับคำสั่ง alias เช่นเดียวกับ: alias dim='echo Terminal Dimensions: $(tput cols) columns x $(tput lines) rows'
brandonsimpkins

103

ในการทุบตีตัวแปรด้านสิ่งแวดล้อม$LINESและ$COLUMNSควรจะสามารถหลอกลวงได้ จะถูกตั้งค่าโดยอัตโนมัติเมื่อมีการเปลี่ยนแปลงขนาดเทอร์มินัล (เช่นสัญญาณSIGWINCH )


18
อย่างไรก็ตามตัวแปรสภาวะแวดล้อมเหล่านี้พร้อมใช้งานสำหรับการทุบตีเท่านั้นและไม่สามารถใช้กับโปรแกรมใด ๆ ที่ทำงานภายในการทุบตี (เช่น perl, python, ruby)
Br.Bill

9
ที่ไม่ได้ทำงานอะไรเลยนอกจากเซสชัน bash แบบโต้ตอบ (ถ้าคุณเรียกใช้สคริปต์มันจะไม่โต้ตอบอีกต่อไป) ที่เดียวที่คุณสามารถใช้ได้ในสคริปต์คือ prompt_command in bash
Doncho Gunchev

1
ที่จริงแล้วมันจะทำงานในสคริปต์ที่ไม่โต้ตอบถ้าคุณตั้งค่าcheckwinsizeตัวเลือก ตัวอย่างเช่นสคริปต์ที่ไม่โต้ตอบนี้จะพิมพ์ขนาดของเทอร์มินัลที่เรียกใช้:shopt -s checkwinsize; (:); echo $LINES $COLUMNS ( checkwinsizeตัวเลือกเริ่มต้นตัวแปรหลังจากรอให้ subshell เสร็จสิ้นเท่านั้นซึ่งเป็นสาเหตุที่เราต้องการ(:)คำสั่ง)
user3340662

$LINESและ$COLUMNSได้รับการอัปเดตหลังจากที่SIGWINCHถูกส่งจริงหลังจากการดำเนินการคำสั่งแบบโต้ตอบใด ๆ หากคุณพยายามอัพเดทPS1ด้วยtrap SIGWINCHคุณไม่สามารถใช้งานได้$LINESและ$COLUMNSจะเก็บค่าเก่าไว้ ((
gavenkoa

LINES และ COLUMNSถูกตั้งค่าเป็นตัวแปรเชลล์โดยการทุบตีเท่านั้น Bash จะไม่ตั้งค่าเป็นตัวแปรสภาพแวดล้อมเว้นแต่ว่าคุณจะส่งออกตัวแปรเชลล์เหล่านี้
Markus Kuhn

67

และsttyจากcoreutils

$ stty size
60 120 # <= sample output

มันจะพิมพ์จำนวนแถวและคอลัมน์หรือความสูงและความกว้างตามลำดับ

จากนั้นคุณสามารถใช้อย่างใดอย่างหนึ่งcutหรือawkแยกส่วนที่คุณต้องการ

นั่นstty size | cut -d" " -f1สำหรับความสูง / เส้นและstty size | cut -d" " -f2สำหรับความกว้าง / คอลัมน์


สไตล์นี้ใช้ไม่ได้กับ PIPE แนะนำให้ใช้สไตล์ tput
liuyang1

6
ปัญหาเกี่ยวกับ tput คือมันไม่สามารถใช้ได้เสมอในขณะที่ stty พร้อมใช้งานในทุก ๆ tty ขอบคุณสำหรับข้อมูล!
iRaS

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

3
ไม่ใช่คำตอบสำหรับคำถามโดยตรง แต่เป็นสคริปต์สาธิตที่ยอดเยี่ยม
Chris หน้า

เป็นตัวอย่างที่ดีมาก!
Kurt Zhong

1
ฉันห่าพลาดtrคำสั่งมาหลายปีแล้วเหรอ? (facepalm)
Marco Massenzio

นี่คือภาษาอะไร? ดูเหมือนรางสคริปต์เชลล์ที่มีข้อผิดพลาดทางไวยากรณ์ (ในเปลือกคุณไม่สามารถมีช่องว่างรอบ ๆ ที่ได้รับมอบหมายเท่ากับและท่อครั้งแรกที่ดูเหมือนออกจากสถานที่.)
tripleee

2
yes '='จะส่งออกเป็นจำนวนอนันต์ของบรรทัด '=' และคำสั่งต่อไปนี้จะจัดระเบียบให้เพียงพอเพื่อเติมขั้ว
pixelbeat

12

เมื่อต้องการทำสิ่งนี้ในสภาพแวดล้อม Windows CLI วิธีที่ดีที่สุดที่ฉันสามารถหาได้คือใช้คำสั่งโหมดและแยกวิเคราะห์ผลลัพธ์

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;
}

ฉันหวังว่ามันจะมีประโยชน์!

หมายเหตุ : ความสูงที่ส่งคืนคือจำนวนบรรทัดในบัฟเฟอร์ไม่ใช่จำนวนบรรทัดที่มองเห็นได้ภายในหน้าต่าง มีตัวเลือกที่ดีกว่านี้ไหม?


3
จดบันทึกปัญหาด้วยสิ่งนี้: ผลลัพธ์ของคำสั่งนี้เป็นภาษาเฉพาะ กล่าวอีกนัยหนึ่งสิ่งนี้จะไม่ทำงานตามที่เป็นอยู่ในโลแคล Windows อื่น นี่คือสิ่งที่ฉันได้รับบน Windows 7: i.imgur.com/Wrr7sWY.png
Camilo Martin

เพิ่มคำตอบด้วยโซลูชันที่ +1 แม้ว่า!
Camilo Martin

10

เมื่อวันที่ POSIX ท้ายที่สุดคุณต้องการที่จะอัญเชิญTIOCGWINSZ(ดูขนาดของหน้าต่าง) ioctl()โทร ภาษาส่วนใหญ่ควรมีเสื้อคลุมบางอย่างสำหรับสิ่งนั้น เช่นใน Perl คุณสามารถใช้คำ :: ขนาด :

use Term::Size qw( chars );

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

1
ขอบคุณสำหรับสิ่งนี้ - พาฉันไปในทิศทางที่ถูกต้อง Elixir: :io.columnsErlang: io:columns(). erlang.org/doc/man/io.html#columns-0
Henrik N

2
ไม่มีTIOCGWINSZใน POSIX มาตรฐานและioctl()กำหนดไว้สำหรับคุณลักษณะ STREAMS ที่ล้าสมัยเท่านั้น
osvein

4

ดังที่ฉันกล่าวถึงในคำตอบ lyceus รหัสของเขาจะล้มเหลวใน Windows ที่ไม่ใช่ภาษาอังกฤษเพราะผลลัพธ์ของmodeอาจไม่ประกอบด้วยสตริง "คอลัมน์" หรือ "บรรทัด" substrings:

                                         เอาต์พุตคำสั่งโหมด

คุณสามารถค้นหาสตริงย่อยที่ถูกต้องโดยไม่ต้องค้นหาข้อความ:

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

โปรดทราบว่าฉันไม่ได้รำคาญกับสายเพราะมันไม่น่าเชื่อถือ (และจริง ๆ แล้วฉันไม่สนใจพวกเขา)

แก้ไข:ตามความคิดเห็นเกี่ยวกับ Windows 8 (โอ้คุณ ... ) ฉันคิดว่านี่น่าเชื่อถือมากขึ้น:

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

ลองทดสอบดูเพราะฉันไม่ได้ทดสอบ


วิธีการของคุณไม่ทำงานใน Win8 ฉันได้มากกว่าหนึ่ง---บรรทัด i.imgur.com/4x02dqT.png
mpen

@ มาร์คดีมากนั่นเป็นเพียงแค่ความสวยงาม ขอบคุณ Windows <3 (ในหมายเหตุที่เกี่ยวข้องเพิ่มเติม: ฉันจะดูวิธีการแก้ไข ... เมื่อ Windows 9 ออกมา: P)
Camilo Martin

นี่คือวิธีที่ฉันทำ: $mode = `mode`; list($rows, $cols) = array_slice(preg_split('/\n/', substr($mode, strpos($mode, 'CON:'))), 2, 2);. แล้วฉันก็แทนที่ทุกอย่างยกเว้นตัวเลข
Aleksandr Makov

@AleksandrMakov ผมสงสัยว่าเกิดอะไรขึ้นถ้ามีภาษาที่มีการสั่งซื้อเช่นCON device status:? บางทีการจับคู่สิ่งที่ชอบCON.*:จะทำงานได้ดีขึ้น
Camilo Martin

1
@ มาร์คฉันจริง ๆ แล้วถามตัวเองว่าสิ่งที่แน่นอน ทำไมฉันถึงทำอย่างนั้น? สงสัยฉันแค่คิดว่ามีเหตุผลบางอย่างและไปด้วยฮ่า ๆ
Camilo Martin

1

แรงบันดาลใจจากคำตอบของ @ pixelbeat นี่คือแถบแนวนอนที่นำมาซึ่งการดำรงอยู่โดยtputใช้ในทางที่ผิดเล็กน้อยของprintfการใส่ / เติมและtr

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

0

มีบางกรณีที่แถว / แถวและคอลัมน์ของคุณไม่ตรงกับขนาดที่แท้จริงของ "เทอร์มินัล" ที่ใช้ บางทีคุณอาจไม่มี "tput" หรือ "stty"

นี่คือฟังก์ชั่นทุบตีที่คุณสามารถใช้เพื่อตรวจสอบขนาดสายตา สิ่งนี้จะทำงานได้ถึง 140 คอลัมน์ x 80 แถว คุณสามารถปรับค่าสูงสุดตามต้องการ

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"
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.