printf
멀티바이트 문자가 포함된 문자열과 관련된 출력 형식을 지정하려고 할 때 printf
리터럴 문자는 계산하지 않고 바이트 수만 계산하므로 싱글바이트 문자와 멀티바이트 문자를 혼합하면 텍스트 형식이 복잡해집니다. 어려운. 예를 들어:
$ cat script
#!/bin/bash
declare -a a b
a+=("0")
a+=("00")
a+=("000")
a+=("0000")
a+=("00000")
b+=("0")
b+=("├─00")
b+=("├─000")
b+=("├─0000")
b+=("└─00000")
printf "%-15s|\n" "${a[@]}" "${b[@]}"
$ ./script
0 |
00 |
000 |
0000 |
00000 |
0 |
├─00 |
├─000 |
├─0000 |
└─00000 |
다양한 제안 해결 방법을 찾았습니다(주로 다른 언어나 유틸리티를 사용하여 텍스트를 인쇄하는 래퍼). 기본 bash 솔루션이 있습니까? 어떤 기록도 없다printf
형식 문자열도움이 될 것 같습니다. 이 경우 locale
UTF-32와 같은 고정 너비 문자 인코딩을 사용하는 등의 설정이 관련되어 있습니까?
답변1
문자 수를 세는 대신 터미널에 원하는 위치로 커서를 이동하도록 지시하여 이 문제를 해결할 수 있습니다 printf
.
$ printf "%s\033[10G-\n" "abc" "├─cd" "└──ef"
abc -
├─cd -
└──ef -
글쎄요, 터미널에 인쇄하고 싶다고 가정하면...
제어 시퀀스가 있습니다<ESC>[nnG
nn
이동할 열로, 10진수로 표시됩니다.
물론 첫 번째 열이 할당된 공간보다 길면 결과가 좋지 않습니다.
$ printf "%s\033[10G-\n" "abcdefghijkl"
abcdefghi-kl
이 문제를 해결하려면 <ESC>[K
다음 열을 인쇄하기 전에 나머지 줄( )을 명시적으로 지울 수 있습니다.
$ printf "%s\033[10G\033[K-\n" "abcdefghijkl"
abcdefghi-
또 다른 방법은 문자열의 문자 길이를 결정할 수 있는 것이 있다고 가정하고 수동으로 패딩을 수행하는 것입니다. 이는 Bash에서 간단한 문자에 대해 작동하는 것처럼 보이지만 물론 약간 보기 흉합니다. 너비가 0이고 너비가 2개인 문자는 이를 깨뜨릴 수 있으며 문자 결합도 테스트하지 않았습니다.
#!/bin/bash
pad() {
# parameters:
# 1: name of variable to pad
# 2: length to pad to
local string=${!1}
local len=${#string}
printf -v "$1" "%s%$(($2 - len))s" "$string" ""
}
echo "1234567890"
for x in "abc" "├─cd" "└──ef" ; do
pad x 9
printf "%s-\n" "$x"
done
출력은 다음과 같습니다
1234567890
abc -
├─cd -
└──ef -
답변2
여기에 사용된 솔루션이 있습니다 wc -L
.
for i in "${a[@]}" "${b[@]}"
do printf "%s%*s|\n" "$i" "$[15 - $(wc -L <<< "$i")]" ""
done
0 |
00 |
000 |
0000 |
00000 |
0 |
├─00 |
├─000 |
├─0000 |
└─00000 |
wc -L
입력의 표시 너비를 인쇄하므로 이중 너비 문자 등에서도 작동합니다.
답변3
웹 검색을 좀 했지만 순수 Bash에서는 문제에 대한 해결책을 찾을 수 없었습니다. 아마도 해결책이 없을 수도 있을 것 같습니다. 다음 StackOverflow 게시물을 찾았습니다.
이것가장 많이 투표된 답변거기(사용자가 게시함)그리스도)에는 다음이 포함됩니다.
printf
예, 이는 제가 알고 있는 모든 버전의 문제입니다. 이 문제에 대해 간략하게 논의했습니다.이 답변그리고 또한이것.
또한 Unix StackExchange에서 다음 게시물을 찾았습니다.
이것허용되는 솔루션여기에는 다음 설명이 포함됩니다.
POSIX필요
printf
%-20s
20개를 세어 보자바이트아니요수치printf
인쇄와는 관련이 없지만텍스트, 서식 지정(토론 참조오스틴 그룹에서(POSIX) 및bash
메일링 리스트).
자신이 원하는 일이 불가능할 수도 있고 printf
자신만의 솔루션을 출시해야 할 것 같습니다.
Python 스크립트를 사용하여 원하는 출력을 생성할 수 있습니다. 아마도 유용할 것입니다:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""script.py"""
# Set the default character encoding to UTF-8
import sys
reload(sys)
sys.setdefaultencoding("utf-8")
# Array of ASCII characters
a=[("0")]
a+=[("00")]
a+=[("000")]
a+=[("0000")]
a+=[("00000")]
# Array of UTF-8 Characters
b=[("0")]
b+=[("├─00")]
b+=[("├─000")]
b+=[("├─0000")]
b+=[("└─00000")]
# Print the elements from both arrays
for x in a + b:
print (u"%-15s|" % x).encode('utf-8')
스크립트를 실행하면 다음과 같은 결과가 나옵니다.
user@host:~$ python script.py
0 |
00 |
000 |
0000 |
00000 |
0 |
├─00 |
├─000 |
├─0000 |
└─00000 |
답변4
printf가 분음 부호를 "축소"하는 이유는 무엇입니까?bash
기능이 내부적으로 누락되었기 때문에 적절한 도구를 호출하거나 다른 셸로 전환하여 이를 수행하는 몇 가지 적절한 솔루션이 있지만 실제로 bash
내장 명령만 사용하여 이를 수행하려는 경우 이를 수행하는 방법이 있습니다. -width(여러 너비 바이트 가능) 문자.
$string
모든 POSIX 셸에서와 마찬가지로 bash에서는 with 의 문자 너비를 얻을 수 있지만 ${#string}
C ${#string}
로캘에서는 너비를 바이트 단위로 얻습니다.
따라서 차이점을 다음과 같이 설명할 수 있습니다.
clength() { clength=${#1}; }
blength() { local LC_ALL=C; blength=${#1}; }
align() {
local format="$1" width="$2" arg blength clength
shift 2
for arg do
clength "$arg"; blength "$arg"
printf "$format" "$((width + blength - clength))" "$arg"
done
}
a=(0 00 000 0000 00000)
b=(0 ├─00 ├─000 ├─0000 └─00000)
align '%-*s|\n' 12 "${a[@]}" "${b[@]}"
너비가 0(태그 결합과 같은) 또는 이중 너비 문자를 고려하려면 bash
스크립트에서 이러한 문자 목록을 하드코딩할 준비가 되어 있지 않은 경우(또는 터미널 이스케이프 시퀀스를 사용하여 터미널에 텍스트를 정렬하도록 지시하는 경우(마지막 예)거기, 또는거기) 및 지원되는 모든 터미널에 대한 하드코드 이스케이프 시퀀스( bash
terminfo/termcap에 대한 내장 인터페이스가 없기 때문). 내가 아는 한, zsh와 ksh93은 가변 디스플레이 너비 문자 정렬을 기본적으로 지원하는 유일한 셸입니다(예:연결된 Q&A).