Variables d’environnement LINES et COLUMNS perdues dans un script

Considérer ce qui suit:

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:~$ 

Les variables $LINES et $COLUMNS sont des variables shell et non des variables d’environnement et ne sont donc pas exscopes vers le processus enfant (mais elles sont automatiquement mises à jour lorsque je redimensionne la fenêtre xterm, même si vous êtes connecté via ssh à partir d’un emplacement distant). Y a-t-il un moyen de laisser mon script connaître la taille actuelle du terminal?

EDIT: J’ai besoin de ceci comme solution de contournement pour résoudre ce problème : vi (ainsi que vim, less et des commandes similaires) gâche l’écran à chaque fois que je l’utilise. Changer le terminal n’est pas une option, et donc je cherche des solutions de contournement (faire défiler les lignes $LINES n’est certainement pas la solution parfaite, mais au moins c’est mieux que de perdre l’écran précédent)

Vous pouvez obtenir les lignes et les colonnes à partir de tput :

 #!/bin/bash lines=$(tput lines) columns=$(tput cols) echo "Lines: " $lines echo "Columns: " $columns 

Parce que cette question est populaire, je veux append une nouvelle réponse avec un peu d’informations supplémentaires.

Souvent, sur les systèmes modernes, les variables $COLUMNS et $LINES ne sont pas des variables d’environnement. Le shell définit ces valeurs dynamicment après chaque commande et nous ne pouvons généralement pas y accéder à partir de scripts non interactifs. Certains programmes respectent ces valeurs si nous les exportons , mais ce comportement n’est pas standardisé ou pris en charge universellement.

Bash définit ces variables dans la scope du processus (pas l’environnement) lorsque nous checkwinsize option checkwinsize utilisant:

 shopt -s checkwinsize 

De nombreux systèmes activent cette option pour nous dans un fichier de démarrage par défaut ou à l’échelle du système ( / etc / bashrc ou similaire). Nous devons donc nous souvenir que ces variables ne sont pas toujours disponibles. Sur certains systèmes, tels que Cygwin, cette option n’est pas activée pour nous, donc Bash ne définit pas $COLUMNS et $LINES moins d’exécuter la ligne ci-dessus ou de l’append à notre ~ / .bashrc .


Lors de l’écriture de scripts non interactifs, nous ne souhaitons généralement pas utiliser par défaut $LINES et $COLUMNS (mais nous pouvons les vérifier pour permettre à un utilisateur de remplacer manuellement la taille du terminal si vous le souhaitez).

Au lieu de cela, les utilitaires stty et tput fournissent des moyens portables pour déterminer la taille du terminal à partir d’un script (les commandes décrites ci-dessous sont en cours de normalisation pour POSIX ).

Comme indiqué dans la réponse acceptée par Puppe , nous pouvons utiliser tput pour collecter la taille du terminal de manière assez simple:

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

Alternativement, la requête de size pour stty nous donne le nombre de lignes et de colonnes du terminal en une seule étape (sortie en nombre de lignes suivi de deux espaces suivis du nombre de colonnes):

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

Le programme stty est généralement livré avec GNU Coreutils , nous pouvons donc souvent le trouver sur des systèmes sans tput . Je préfère parfois l’approche stty car nous invoquons une commande et un sous-shell moins nombreux (coûteux sur Cygwin), mais cela nécessite que nous analysions la sortie en lignes et en colonnes, ce qui peut être moins lisible:

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

Les deux approches décrites ci-dessus fonctionnent dans n’importe quel shell POSIX. Pour Bash en particulier, nous pouvons utiliser la substitution de processus pour simplifier l’exemple précédent:

 read lines columns < <(stty size) 

... qui est plus rapide que l'exemple de base, mais toujours plus lent que la première implémentation stty , du moins sur ma machine. En pratique, l'impact sur les performances est probablement négligeable: choisissez l'approche qui convient le mieux au programme (ou en fonction de la commande disponible sur le système cible).


Si, pour une raison quelconque, nous voulons toujours utiliser $LINES et $COLUMNS dans nos scripts, nous pouvons configurer Bash pour exporter ces variables dans l'environnement:

 trap 'export LINES COLUMNS' DEBUG 

Le piège Bash DEBUG s'exécute avant chaque saisie de la commande à l'invite, de sorte que nous pouvons l'utiliser pour exporter ces variables. En les réexportant avec chaque commande, nous nous assurons que les variables d'environnement restnt à jour si la taille du terminal change. Ajoutez cette ligne à .bashrc avec l'option checkwinsize ci-dessus. Cela fonctionne bien pour les scripts personnels, mais je ne recommande pas d'utiliser ces variables dans un script qui sera partagé.

 eval $( resize ) 

ce travail … (sur un terminal basé sur xterm)

 kill -s WINCH $$ 

définit les variables.

Avez-vous essayé de faire votre shebang dire:

 #!/bin/bash -i 

Exécuter l’ help export pourrait aider?

 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:~$ 

$LINES et $COLUMNS dans bash est juste un wrapper de type shell-y autour des ioctls du TTY, vous donnant la taille du TTY et les signaux envoyés par le terminal chaque fois que cette taille change.

Vous pouvez écrire un programme dans une autre langue qui appelle directement ces ioctls pour accéder aux dimensions TTY, puis utiliser ce programme.

EDIT: Eh bien, il s’avère que ce programme existe déjà, et s’appelle tput . Votez la tput basée sur le tput de Puppe .

Par souci d’achèvement, permettez-moi de mentionner que la définition de l’option ‘checkwinsize’ correspond exactement à ce que recherche l’OP, mais il y a un problème. Par défaut, il n’est pas défini dans les scripts non interactifs, mais vous pouvez choisir d’append la ligne suivante au début de tout script pour l’activer:

 shopt -s checkwinsize 

Malheureusement, les variables LINES et COLUMNS ne sont pas définies immédiatement lors de la définition de l’option (au moins la dernière fois que j’ai essayé). Au lieu de cela, vous devez forcer Bash à attendre qu’un sous-shell se termine, à quel point il définira ces variables. La solution complète de Bash uniquement à ce problème consiste donc à démarrer votre script avec la ligne suivante:

 shopt -s checkwinsize; (:;:) 

Vous pouvez ensuite utiliser les variables LINES et COLUMNS au contenu de votre cœur, et elles seront réinitialisées aux valeurs correctes chaque fois que le terminal est redimensionné, sans avoir à appeler aucun utilitaire externe.

 #!/bin/bash -i 

-i fonctionne maintenant avec la version 4.2.10 (1) de bash sur Ubuntu 11.10 .

 $ 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  This is free software; you are free to change and redissortingbute it. There is NO WARRANTY, to the extent permitted by law. 

Les nombres changent avec un redimensionnement de fenêtre; un piège révèle que le script reçoit un SIGWINCH.

Pourquoi ne pas utiliser les variables d’environnement sur la commande exec comme ceci:

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

Mon expérience est que vous devriez commencer le script par le ‘. script_to_run ‘form, au lieu de’ scritp_to_run ‘. Une simple vérification comme suit:

 '(( ${#COLUMNS} )) || { echo "Try start like '. name'" ; return 1 ; }