printf가 있는 가운데 텍스트

printf가 있는 가운데 텍스트
printf "%*s\n" $(((${#fname}+$COLUMNS)/2)) "$fname"

다음 오류가 발생합니다.

line 9: (7+)/2: syntax error: operand expected (error token is ")/2")

이것은 터미널에서는 작동하지만 내 스크립트에서는 작동하지 않습니다. 어떤 아이디어가 있나요?

답변1

모든 쉘이 $COLUMNS변수를 터미널 너비로 설정하는 것은 아닙니다.

bash5.0 이전 버전에서는 스크립트가 아닌 대화형인 경우에만 설정합니다. 그러나 버전 4.3부터는 shopt -s checkwinsize.

두 경우 모두 그러나 반전이 있습니다. 비대화형 쉘에서 해당 옵션을 활성화하면(5.0부터 기본적으로 활성화됨) $COLUMNS/ $LINES하위 프로세스가 기다리고 종료될 때까지 설정되지 않습니다( NEWS소스 항목에 언급되어 있음 ).포그라운드 작업이 종료된 후, 비대화형 쉘에는 기본적으로 작업 제어가 없다는 점을 고려하면 약간 오해의 소지가 있습니다. 따라서 해당 변수를 사용하기 전에 외부 명령이나 하위 쉘이 동기적으로 실행되었는지 확인해야 합니다.

#! /bin/bash -
shopt -s checkwinsize # for versions 4.3 and 4.4
(:) # start a synchronous subshell that runs the null command
echo "$COLUMNS $LINE"

또한 stderr이 터미널로 이동하는 경우에만 발생하므로(그렇지 않은 경우 설정되지 않은 상태로 유지) 화면 너비를 결정할 수 없는 경우 더 정상적인 기본값을 사용하는 것과 $COLUMNS같은 것을 사용할 수 있습니다 .${COLUMNS:-80}bash

또는 터미널에서 실행 중인 경우 비대화형인 경우에도 zsh항상 설정되는 것으로 전환할 수 있습니다 ( 그렇지 않으면 기본값은 80입니다). 또는 Bourne과 유사한 셸에서 for 대신 if 출력에서 ​​설정되도록 사용할 수 있습니다. 이전에 설정되지 않았거나 비어 있었습니다.$COLUMNS$COLUMNS${COLUMNS:=$(tput cols)}$COLUMNS$COLUMNStput cols

tput cols시스템에서 작동하지 않으면 시도해 볼 수 있습니다 . </dev/tty stty size | awk '{print $2}'또는zsh -c 'print $COLUMNS'

그러나 한 번 $COLUMNS그런 식으로 설정하면 터미널 크기가 조정될 때마다 업데이트되지 않으므로 주의하십시오. 따라서 $(tput cols)대신 항상 사용하여 스크립트에서 가운데 ​​정렬된 텍스트를 인쇄할 때마다 터미널 크기가 쿼리되도록 할 수 있습니다.

또한 주의하세요printf '%*s'이외의 쉘에서는 주어진 수만큼 텍스트를 채웁니다 zsh.fish바이트~ 아니다문자, 따라서 이 접근 방식은 UTF-8을 사용하는 로케일에서 US-ASCII 문자(가능한 모든 문자의 0.011%)로 제한되는 단일 바이트, 단일 너비 문자를 포함하는 텍스트를 채우는 데에만 사용할 수 있습니다.

zsh대신 을 사용하는 경우 eft 및 ight 패딩 매개변수 확장 플래그를 대신 bash사용할 수 있습니다 (플래그를 사용하여 너비가 0이거나 너비가 2인 문자도 처리할 수 있음 ).lrm

print -r -- ${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}

왼쪽과 오른쪽 모두(화면 오른쪽 가장자리)까지 채워집니다. 다음을 사용하여 오른쪽 패딩(모든 후행 공백과 함께)을 제거할 수 있습니다.

set -o extendedglob
print -r -- ${${(ml[COLUMNS/2]r[COLUMNS-COLUMNS/2])fname}%%[[:space:]]#}

색상 지정/굵게/눈에 띄는... 이스케이프 시퀀스가 ​​포함된 텍스트를 중앙에 배치하는 것은 더 복잡합니다. 가장 쉬운 방법은 문자열 너비를 얻기 전에 제거하는 것입니다. 예를 들어, zsh, 사용그 접근 방식문자열 너비를 결정하고 너비가 0이거나 너비가 2개인 문자를 처리합니다.

varwidth() (( ${(P)#1} * 3 - ${#${(ml[${(P)#1} * 2])${(P)1}}} ))
functions -Ms varwidth

varwidth_without_formatting() {
  set -o localoptions -o extendedglob
  local without_formatting=${(P)1//$'\e'\[[0-9;]#m}
  (( varwidth(without_formatting) ))
}
functions -Ms varwidth_without_formatting

center() {
  local text
  for text do
    print -r -- ${(l[(COLUMNS-varwidth_without_formatting(text))/2])}$text
  done
}

center $'\e[31mred\e[1;39mbold\e[m' \
       ${(%):-%F{green}Blah%F{yellow}blah%F{magenta}blah%f}

¹ 대부분의 시스템에서는 가장 일반적인 경우에 도움이 될 주석에 @zevzek이 보여준 것처럼 SIGWINCH 신호에 대한 처리기를 설치할 수 있습니다.

답변2

COLUMNS 변수 대신 외부 소스에서 값을 가져올 수 있습니다.

tput cols 

stty size | cut '-d ' -f1

답변3

다음을 시도해 보세요:

read WindowHeight WindowWidth<<<$(stty size)
printf "%$(((${#fname}+${WindowWidth})/2))s" "$fname"

COLUMNS는 스크립트에서 자동으로 설정되지 않으므로 stty를 사용하여 현재 창 크기를 얻는 것이 가장 좋습니다. 이것은 여러 쉘(bash, ksh, zsh 포함)에서 작동합니다.

관련 정보