bash LINES 和 COLUMNS 环境变量在脚本中丢失

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1780483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:42:55  来源:igfitidea点击:

LINES and COLUMNS environmental variables lost in a script

bashshellterminalenvironment-variables

提问by Davide

Consider the following:

考虑以下:

me@mine:~$ cat a.sh 
#!/bin/bash
echo "Lines: " $LINES
echo "Columns: " $COLUMNS
me@mine:~$ ./a.sh 
Lines: 
Columns: 
me@mine:~$ echo "Lines: " $LINES
Lines:  52
me@mine:~$ echo "Columns: " $COLUMNS
Columns:  157
me@mine:~$ 

The variables $LINESand $COLUMNSare shell variables, notenvironmental variables, and thus are not exported to the child process (but they are automatically updated when I resize the xterm window, even when logged in via ssh from a remote location). Is there a way in which I can let my script know the current terminal size?

变量$LINES$COLUMNS是 shell 变量,而不是环境变量,因此不会导出到子进程(但是当我调整 xterm 窗口的大小时,即使从远程位置通过 ssh 登录,它们也会自动更新)。有没有办法让我的脚本知道当前的终端大小?

EDIT: I need this as a workaround do this problem: vi (as well as vim, less, and similar commands) messes up the screen every time I use it. Changing the terminal is not an option, and thus I'm looking for workarounds (scrolling down $LINESlines surely is not the perfect solution, but at least is better than losing the previous screen)

编辑:我需要这个作为解决这个问题的解决方法:vi(以及 vim、less 和类似命令)每次使用时都会弄乱屏幕。更改终端不是一种选择,因此我正在寻找解决方法(向下滚动$LINES行肯定不是完美的解决方案,但至少比丢失前一个屏幕要好)

回答by Puppe

You could get the lines and columns from tput:

您可以从tput以下位置获取行和列:

#!/bin/bash

lines=$(tput lines)
columns=$(tput cols)

echo "Lines: " $lines
echo "Columns: " $columns

回答by Cy Rossignol

Because this question is popular, I want to add a newer answer with a bit of additional information.

因为这个问题很受欢迎,所以我想添加一个带有一些额外信息的新答案。

Often, on modern systems, the $COLUMNSand $LINESvariables are notenvironment variables. The shell sets these values dynamically after each command and we usually cannot access them from non-interactive scripts. Some programs respect these values if we exportthem, but this behavior isn't standardized or universally supported.

通常,在现代系统上,$COLUMNS$LINES变量不是环境变量。shell 在每个命令之后动态设置这些值,我们通常无法从非交互式脚本访问它们。如果我们导出这些值,一些程序会尊重这些值,但这种行为并未标准化或普遍支持。

Bash sets these variables in the scope of the process(not the environment) when we enable the checkwinsizeoption using:

当我们使用以下选项启用选项时,Bash 在进程范围内(而不是环境)设置这些变量checkwinsize

shopt -s checkwinsize 

Many systems enable this option for us in a default or system-wide startup file (/etc/bashrcor similar), so we need to remember that these variables may not always be available. On some systems, such as Cygwin, this option is notenabled for us, so Bash doesn't set $COLUMNSand $LINESunless we execute the line above or add it to our ~/.bashrc.

许多系统在默认或系统范围的启动文件(/etc/bashrc或类似文件)中为我们启用此选项,因此我们需要记住这些变量可能并不总是可用。在某些系统上,例如 Cygwin,我们启用此选项,因此不会设置 Bash $COLUMNS$LINES除非我们执行上面的行或将其添加到我们的~/.bashrc 中

Portable Approaches

便携式方法

When writing non-interactive scripts, we usually don't want to rely on $LINESand $COLUMNSby default (but we cancheck these to allow a user to override the terminal size manually if desired).

在编写非交互式脚本时,我们通常不想依赖$LINES$COLUMNS默认(但我们可以检查这些以允许用户在需要时手动覆盖终端大小)。

Instead, the sttyand tpututilities provide portablemeans to determine the terminal size from a script (the commands described below are currently undergoing standardization for POSIX).

相反,sttytput实用程序提供了从脚本确定终端大小的便携方法(下面描述的命令目前正在为 POSIX 进行标准化)。

As shown in the accepted answer by Puppe, we can use tputto gather the terminal size in a pretty straightforward manner:

Puppe接受的答案所示,我们可以使用tput一种非常简单的方式来收集终端大小:

lines=$(tput lines)
columns=$(tput cols)

Alternatively, the sizequery for sttygives us the number of terminal rows and columns in one step (output as the number of lines followed by two spaces followed by the number of columns):

或者,size查询 forstty为我们提供一步中终端的行数和列数(输出为行数后跟两个空格后跟列数):

size=$(stty size)  # "40  80" for example 

The sttyprogram usually ships with GNU Coreutils, so we can often find it on systems without tput. I sometimes prefer the sttyapproach because we invoke one fewer command and subshell (expensive on Cygwin), but it does require that we parse the output into rows and columns, which may be less readable:

stty程序通常随GNU Coreutils 一起提供,因此我们经常可以在没有tput. 我有时更喜欢这种stty方法,因为我们调用的命令和子 shell 少了一个(在 Cygwin 上很昂贵),但它确实需要我们将输出解析为行和列,这可能不太可读:

lines=${size% *}
columns=${size#* }

Both of the approaches described above work in any POSIX shell.

上述两种方法都适用于任何 POSIX shell。

Non-portable Approaches

非便携式方法

If we don't care about portability, Bash supports process substitutionto simplify the previous example:

如果我们不关心可移植性,Bash 支持进程替换来简化前面的例子:

read lines columns < <(stty size) 

...which runs faster than the tputexample, but still slower than the first sttyimplementation, at least on my machine. In practice, the performance impact is probably negligible—choose the approach that works best for the program (or based on which command is available on the target system).

...运行速度比tput示例快,但仍然比第一个stty实现慢,至少在我的机器上。在实践中,性能影响可能可以忽略不计——选择最适合程序的方法(或基于目标系统上可用的命令)。

For Bash versions 4.3 and later, we can exploit the checkwinsizeoption to avoid a dependency on an another program. When we enable this option in a script, Bash will set $LINESand $COLUMNSlike it does for an interactive prompt aftera child process exits:

对于 Bash 4.3 及更高版本,我们可以利用该checkwinsize选项来避免对另一个程序的依赖。当我们在脚本中启用此选项时,Bash 将子进程退出设置$LINES$COLUMNS像交互式提示一样:

#!/bin/bash
shopt -s checkwinsize
cat /dev/null # Refresh LINES and COLUMNS

...like when a subshell exits:

...就像当子shell退出时:

shopt -s checkwinsize
(: Refresh LINES and COLUMNS)

Bash fetches the terminal size after everyexternal command invocation if we enable this option, so we may want to turn it back off after initializing the variables:

如果启用此选项,Bash 会在每次外部命令调用后获取终端大小,因此我们可能希望在初始化变量后将其关闭:

shopt -u checkwinsize

If, for some reason, we still want to use $LINESand $COLUMNSfrom the environment in our scripts, we can configure Bash to export these variables to the environment:

如果出于某种原因,我们仍然想在脚本中使用$LINES$COLUMNS来自环境,我们可以配置 Bash 将这些变量导出到环境中:

trap 'export LINES COLUMNS' DEBUG

The Bash DEBUGtrap executes before each command entered at the prompt, so we can use it to export these variables. By re-exporting them with each command, we ensure that the environment variables remain up-to-date if the terminal size changes. Add this line to .bashrcalong with the checkwinsizeoption shown above. It works fine for personal scripts, but I don't recommend using these variables in any script that will be shared.

BashDEBUG陷阱在提示符下输入的每个命令之前执行,因此我们可以使用它来导出这些变量。通过使用每个命令重新导出它们,我们确保在终端大小发生变化时环境变量保持最新。将此行与上面显示的选项一起添加到.bashrccheckwinsize中。它适用于个人脚本,但我不建议在任何将共享的脚本中使用这些变量。

回答by Anthony

eval $( resize )

does that job...(on xterm-based terminal)

做那个工作...(在基于 xterm 的终端上)

回答by elo

kill -s WINCH $$

does set the variables.

确实设置了变量。

回答by Marc Coiffier

For the sake of completion, let me mention that setting the 'checkwinsize' option is exactly what the OP is looking for, but there is a catch. It is by default unset in non-interactive scripts, but you can elect to add the following line at the beginning of any script to enable it :

为了完成起见,让我提一下,设置 'checkwinsize' 选项正是 OP 正在寻找的,但有一个问题。默认情况下,它在非交互式脚本中是未设置的,但您可以选择在任何脚本的开头添加以下行以启用它:

shopt -s checkwinsize

Unfortunately, the LINES and COLUMNS variables are not set immediately upon setting the option (at least the last time I tried). Instead, you need to force Bash to wait for a subshell to complete, at which point it will set those variables. The full Bash-only solution to this problem is thus to start your script with the following line :

不幸的是,LINES 和 COLUMNS 变量在设置选项后不会立即设置(至少在我最后一次尝试时)。相反,您需要强制 Bash 等待子 shell 完成,此时它将设置这些变量。因此,此问题的完整 Bash 解决方案是使用以下行启动您的脚本:

shopt -s checkwinsize; (:;:)

You can then use the LINES and COLUMNS variables to your heart's content, and they will be reset to the correct values each time the terminal is resized, without needing to call any external utilities.

然后,您可以根据自己的需要使用 LINES 和 COLUMNS 变量,每次调整终端大小时,它们将被重置为正确的值,而无需调用任何外部实用程序。

回答by Paused until further notice.

Have you tried making your shebang say:

你有没有试过让你的shebang说:

#!/bin/bash -i

回答by SwordFish

Running help exportmight help?

跑步help export可能有帮助吗?

me@mine:~$ cat a.sh 
#!/bin/bash
echo "Lines: " $LINES
echo "Columns: " $COLUMNS
me@mine:~$ ./a.sh 
Lines: 
Columns: 
me@mine:~$ echo "Lines: " $LINES
Lines:  52
me@mine:~$ echo "Columns: " $COLUMNS
Columns:  157
me@mine:~$ export LINES COLUMNS
me@mine:~$ ./a.sh 
Lines:  52
Columns:  157
me@mine:~$ 

回答by ndim

$LINESand $COLUMNSin bash is just a shell-y wrapper around the TTY ioctls giving you the size of the TTY and the signals sent by the terminal every time that size changes.

$LINES$COLUMNS在bash就要到了TTY的ioctl给你TTY大小的外壳-Y包装和终端每次发送的大小变化的信号。

You could write a program in some other language which calls those ioctls directly to get to the TTY dimensions, and then use that program.

您可以使用其他语言编写程序,直接调用这些 ioctl 以获取 TTY 维度,然后使用该程序。

EDIT: Well, turns out that program already exists, and is called tput. Vote up Puppe's tputbased answer.

编辑:嗯,原来该程序已经存在,并被称为tput. 投票支持Puppe 的tput答案

回答by Seganku

#!/bin/bash -i

-iworks now with bash 4.2.10(1)-releaseon Ubuntu 11.10.

-i现在在Ubuntu 11.10上与bash 4.2.10(1)-release 一起使用。

$ cat show_dimensions.sh 
#!/bin/bash -i
printf "COLUMNS = %d\n" $COLUMNS
printf "LINES = %d\n" $LINES

$ ./show_dimensions.sh 
COLUMNS = 150
LINES = 101

$ bash --version
GNU bash, version 4.2.10(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

The numbers do change with a window resize; a trap reveals the script is getting a SIGWINCH.

数字会随着窗口大小的调整而改变;一个陷阱显示脚本正在收到 SIGWINCH。

回答by Classsic

Why not use enviroment variables on exec command like this:

为什么不在这样的 exec 命令上使用环境变量:

docker exec -ti -e LINES=$LINES -e COLUMNS=$COLUMNS  container /bin/bash