web-dev-qa-db-fra.com

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, des variables d'environnement non, et ne sont donc pas exportées vers le processus enfant (mais elles sont automatiquement mises à jour lorsque je redimensionne la fenêtre xterm, même lorsque vous êtes connecté). via ssh depuis un emplacement distant). Existe-t-il un moyen de communiquer à mon script la taille actuelle du terminal?

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

56
Davide

Vous pouvez obtenir les lignes et les colonnes de tput:

#!/bin/bash

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

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

Étant donné que cette question est populaire, je souhaite ajouter une réponse plus récente avec quelques informations supplémentaires.

Souvent, sur les systèmes modernes, les variables $COLUMNS et $LINES sont des variables d'environnement not. Le shell définit ces valeurs dynamiquement 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 ni normalisé ni universellement pris en charge.

Bash définit ces variables dans la portée de process (pas de l'environnement) lorsque nous activons l'option checkwinsize à l'aide de: 

shopt -s checkwinsize 

De nombreux systèmes activent cette option pour nous dans un fichier de démarrage par défaut ou au niveau du système (/ etc/bashrc ou similaire), nous devons donc nous rappeler que ces variables peuvent ne pas toujours être disponibles. Sur certains systèmes, tels que Cygwin, cette option est activée pour nous pas. Par conséquent, Bash ne définit pas $COLUMNS et $LINES sauf si nous exécutons la ligne ci-dessus ou si nous l'ajoutons à notre commande. ~/.bashrc.


Lorsque vous écrivez des scripts non interactifs, nous ne souhaitons généralement pas nous appuyer sur les codes $LINES et $COLUMNS par défaut (mais nous pouvons les vérifier pour permettre à un utilisateur de remplacer manuellement la taille du terminal, le cas échéant).

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

Comme le montre 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 cols)

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

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

Le programme stty est généralement livré avec GNU Coreutils , de sorte que nous pouvons souvent le trouver sur des systèmes sans tput. Je préfère parfois l'approche stty car nous appelons une commande et un sous-shell de moins (coûteux pour Cygwin), mais cela nécessite d'analyser le résultat en lignes et en colonnes, ce qui risque d'ê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 process substitution pour simplifier l'exemple précédent: 

read lines columns < <(stty size) 

... qui est plus rapide que l'exemple tput, 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 souhaitons toujours utiliser $LINES et $COLUMNS dans nos scripts, nous pouvons configurer Bash pour exporter ces variables vers l'environnement:

trap 'export LINES COLUMNS' DEBUG

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

17
Cy Rossignol
eval $( resize )

fait ce travail ... (sur un terminal basé sur xterm)

6
Anthony
kill -s WINCH $$

définit les variables.

5
elo

Par souci d’achèvement, permettez-moi de mentionner que la définition de l’option 'checkwinsize' correspond exactement à ce que recherche le PO, mais il ya un problème. Il est par défaut non défini dans les scripts non interactifs, mais vous pouvez choisir d'ajouter 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 la fin du sous-shell, auquel cas il définira ces variables. La solution complète de ce problème uniquement par Bash consiste donc à démarrer votre script avec la ligne suivante: 

shopt -s checkwinsize; (:;:)

Vous pouvez ensuite utiliser les variables LINES et COLUMNS pour le contenu de votre cœur. Elles seront réinitialisées aux valeurs correctes chaque fois que le terminal est redimensionné, sans avoir à appeler des utilitaires externes.

3
Marc Coiffier

Lancer 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:~$ 
2
SwordFish

Avez-vous essayé de faire dire à votre Shebang:

#!/bin/bash -i
2
Dennis Williamson

$LINES et $COLUMNS in bash est simplement un wrapper Shell-y autour des ioctls TTY, qui vous donne 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 ces ioctls directement 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 Réponse de Puppe basée sur la tput .

1
ndim
#!/bin/bash -i

-i fonctionne maintenant avec bash 4.2.10 (1) -release on 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 <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.

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

1
Seganku

Pourquoi ne pas utiliser les variables d'environnement dans la commande exec comme ceci:

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