Printf "सिकुड़" umlaut क्यों है?


54

अगर मैं निम्नलिखित सरल स्क्रिप्ट निष्पादित करता हूं:

#!/bin/bash
printf "%-20s %s\n" "Früchte und Gemüse"   "foo"
printf "%-20s %s\n" "Milchprodukte"        "bar"
printf "%-20s %s\n" "12345678901234567890" "baz"

यह प्रिंट करता है:

Früchte und Gemüse foo
Milchprodukte        bar
12345678901234567890 baz

यह है, umlauts के साथ पाठ (जैसे कि ü) प्रति umlaut एक चरित्र द्वारा "सिकुड़ा" है।

निश्चित रूप से, मेरे पास कुछ गलत सेटिंग है, लेकिन मैं यह पता लगाने में सक्षम नहीं हूं कि कौन सा हो सकता है।

यह तब होता है यदि फ़ाइल की एन्कोडिंग UTF-8 है।

यदि मैं इसकी एन्कोडिंग को लैटिन -1 में बदलता हूं, तो संरेखण सही है, लेकिन umlauts को गलत तरीके से प्रस्तुत किया गया है:

Frchte und Gemse   foo
Milchprodukte        bar
12345678901234567890 baz

14
आप उम्मीद करते हैं कि प्रिंटफ को यूटीएफ -8 और अन्य मल्टीबीट चार्ट के बारे में पता होना चाहिए?
४z बजे फ्रॉस्टचुटज़

16
ऐसा लगता है कि यह वर्णों के बजाय बाइट्स गिन रहा है; echo Früchte und Gemüse | wc -c -mअंतर के लिए देखें ।
स्टीफन किट

7
@frostschutz Zsh's printfहै।
स्टीफन किट

10
हां, मुझे उम्मीद है कि प्रिंटफ को कम से कम यूटीएफ -8 के बारे में पता होना चाहिए।
रेने Nyffenegger

12
खैर, यह नहीं है। कठिन भाग्य। ;-)
फ्रॉस्ट्सचुट्ज़

जवाबों:


87

POSIX की आवश्यकता printf है %-20sके मामले में उन 20 गिनती करने के लिए बाइट नहीं वर्ण है कि भले ही थोड़ा समझ में आता है के रूप में printfमुद्रित करने के लिए है पाठ , प्रारूपित (चर्चा देखने के ऑस्टिन समूह में (इसे POSIX) और bashमेलिंग सूची)।

printfकी निर्मित bashऔर अधिकांश अन्य POSIX गोले कि सम्मान करते हैं।

zshउस मूर्खतापूर्ण आवश्यकता को अनदेखा करता है (यहां तक ​​कि shअनुकरण में भी ) तो वह printfकाम करता है जैसा कि आप वहां चाहते हैं। के printfअंतर्निहित के लिए एक ही fish(एक POSIX की तरह नहीं खोल)।

üचरित्र (U + 00FC), जब UTF-8 में एन्कोड दो बाइट्स (0xc3 और 0xbc) है, जो विसंगति बताते हैं से बना है।

$ printf %s 'Früchte und Gemüse' | wc -mcL
    18      20      18

वह तार 18 अक्षरों से बना है, 18 कॉलम चौड़ा है ( इनपुट में सबसे चौड़ी लाइन की डिस्प्ले चौड़ाई की रिपोर्ट करने के लिए -LGNU wcएक्सटेंशन है) लेकिन 20 बाइट्स पर एन्कोडेड है।

में zshया fish, पाठ ठीक से संरेखित किया जाएगा।

अब, ऐसे अक्षर भी हैं, जिनमें 0-चौड़ाई है (जैसे U + 0308 जैसे वर्णों को मिलाना, तिर्यकदृष्टि को जोड़ना) या कई एशियाई लिपियों की तरह डबल-चौड़ाई है (टैब जैसे नियंत्रण वर्णों का उल्लेख नहीं करना) और यहां तक zshकि संरेखित नहीं करेंगे वे ठीक से।

उदाहरण में zsh:

$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
  u|
  ü|
 ü|
  ᄀ|

इन bash:

$ printf '%3s|\n' u ü $'u\u308' $'\u1100'
  u|
 ü|
ü|
ᄀ|

ksh93प्रदर्शन चौड़ाई के %Lsसंदर्भ में चौड़ाई की गणना करने के लिए एक प्रारूप विनिर्देश है ।

$ printf '%3Ls|\n' u ü $'u\u308' $'\u1100'
  u|
  ü|
  ü|
 ᄀ|

यह अभी भी काम नहीं करता है अगर पाठ में TAB जैसे नियंत्रण वर्ण हैं (यह कैसे हो सकता है? यह printfजानना होगा कि आउटपुट डिवाइस में टैब स्टॉप के अलावा कितनी दूर हैं और यह किस स्थिति में मुद्रण करना शुरू करता है)। यह बैकस्पेस वर्णों के साथ दुर्घटना के द्वारा काम करता है (जैसे roffआउटपुट में जहां X(बोल्ड X) के रूप में लिखा गया है X\bX) हालांकि ksh93सभी नियंत्रण वर्णों की चौड़ाई मानता है -1

अन्य विकल्पों के रूप में, आप कोशिश कर सकते हैं:

printf '%s\t|\n' u ü $'u\u308' $'\u1100' | expand -t3

यह कुछ expandकार्यान्वयन के साथ काम करता है (जीएनयू के हालांकि नहीं)।

GNU सिस्टम पर, आप GNU का उपयोग कर सकते हैं awkजिनकी printfगिनती वर्णों में होती है (बाइट्स नहीं, प्रदर्शन-चौड़ाई नहीं, इसलिए अभी भी 0-चौड़ाई या 2-चौड़ाई वाले वर्णों के लिए ठीक नहीं है, लेकिन आपके नमूने के लिए ठीक है):

gawk 'BEGIN {for (i = 1; i < ARGC; i++) printf "%-3s|\n", ARGV[i]}
     ' u ü $'u\u308' $'\u1100'

यदि आउटपुट किसी टर्मिनल पर जाता है, तो आप कर्सर पोजिशनिंग एस्केप सीक्वेंस का भी उपयोग कर सकते हैं। पसंद:

forward21=$(tput cuf 21)
printf '%s\r%s%s\n' \
  "Früchte und Gemüse"    "$forward21" "foo" \
  "Milchprodukte"         "$forward21" "bar" \
  "12345678901234567890"  "$forward21" "baz"

2
यह गलत है। üCaracter के रूप में हो सकती हैं u+ ¨, जो 3 बाइट है। प्रश्न के मामले में, इसे 2 वर्णों के रूप में एन्कोड किया गया है, लेकिन सभी üसमान रूप से नहीं बनाए गए हैं।
इस्माईल मिगुएल

6
@IsmaelMiguel, एक ग्लिफ़ / ग्रैफेम / ग्रैफ़ेम-क्लस्टर के u\u308लिए दो अक्षर ( wc -mकम से कम यूनिक्स / अर्थ में) है और पहले से ही इस उत्तर में उल्लिखित और शामिल है।
स्टीफन चेज़लस

"जो प्रिंटफ़ के रूप में बहुत कम समझ में आता है कि टेक्स्ट प्रिंट करना है" ठीक है, कोई यह तर्क दे सकता है कि प्रिंटफ़ सी चार्ट्स (बाइट्स) से संबंधित है; इसे पाठ स्थानों से नहीं निपटना चाहिए, और इसमें (संभवतः मल्टीबाइट) चारसेट एन्कोडिंग को समझने का भार नहीं होना चाहिए। लेकिन रक्षा की यह रेखा (आईएसओ C99) आवश्यकताओं के साथ टकराव करती है कि "% s" बाइट ट्रंकेशन को "अमान्य" टेक्स्ट (काटे गए वर्ण) में परिणाम नहीं करना चाहिए। Glibc उस मामले में भी विफल रहता है (यह कुछ भी नहीं छापता है)। एक असली गड़बड़। postgresql.org/message-id/…
leonbloy

@leonbloy, जो C की समझ में आ सकता है printf(3)(उस C99 की आवश्यकता के बाद थोड़ा सा अर्थ जो आप उल्लेख कर रहे हैं, उसके लिए धन्यवाद), लेकिन printf(1)पात्रों के साथ हर शेल ऑपरेटर या अन्य टेक्स्ट यूटिलिटी डील के रूप में उपयोगिता नहीं (या पात्रों के साथ सौदा करने के लिए भी संशोधित किया गया था) जैसे कि wcजो एक मिला -m( बाइट-c रहते हुए ) या जिसे बाद में बाइट्स के अलावा कुछ और मिल सकता है)। cut-b-c
स्टीफन चेज़लस

यहां तक ​​कि अगर यह बाइट्स के बजाय वर्णों का उपयोग करता है, तो भी यह कॉलम संरेखित करने के लिए उपयुक्त नहीं होगा। आपको यह जानने की जरूरत है कि प्रत्येक चरित्र में कितने टर्मिनल सेल होते हैं, जो चरित्र (0-2) द्वारा भिन्न होता है।
आर ..

10

यदि मैं इसकी एन्कोडिंग को लैटिन -1 में बदलता हूं, तो संरेखण सही है, लेकिन umlauts को गलत तरीके से प्रस्तुत किया गया है:

Frchte und Gemse   foo
Milchprodukte        bar
12345678901234567890 baz

वास्तव में, नहीं, लेकिन आपका टर्मिनल लैटिन -1 नहीं बोलता है, और इसलिए आपको umlauts के बजाय कबाड़ मिलता है।

Iconv का उपयोग करके आप इसे ठीक कर सकते हैं:

printf foo bar | iconv -f ISO8859-1 -t UTF-8

(या बस संपूर्ण शेल स्क्रिप्ट आइकॉन में चलाएं)


3
यह एक उपयोगी टिप्पणी है लेकिन मूल प्रश्न का उत्तर नहीं देता है।
गेरिट

1
@gerrit तो कैसे? अगर प्रिंट 1 लैटिन में प्रिंट करते समय सही काम करता है, तो क्या यह लैटिन 1 में प्रिंट होता है और बाद में इसे यूटीएफ -8 में बदल देता है? मेरे लिए मुख्य प्रश्न के लिए एक उचित तय की तरह लगता है।
राउटर वेर्लस्ट

1
मूल प्रश्न यह है कि "उमलाट क्यों सिकुड़ रहा है", उत्तर (अन्य उत्तरों की तरह) "क्योंकि यह utf-8 का समर्थन नहीं करता है"। यह नहीं पूछ रहा है कि umlauts को गलत क्यों प्रस्तुत किया गया है या मैं umlaut रेंडरिंग को कैसे ठीक कर सकता हूं । किसी भी तरह से, आपका सुझाव utf-8 के सबसेट के लिए उपयोगी है जिसे iso8859-1 (केवल) के रूप में दर्शाया जा सकता है।
गेरिट

4
@ राउटरविलेस्ट, हाँ, हालांकि यह केवल उस पाठ पर लागू हो सकता है जो एकल-बाइट वर्णसेट में एन्कोड किया जा सकता है।
स्टीफन चेजलस 16

3
मैंने भी प्रश्न को "मैं कैसे सही आउटपुट प्राप्त कर सकता हूं" के बजाय "मैं गलत तरीके से उत्पादन नहीं कर सकता, जब तक कि मुझे पता है" के रूप में प्रश्न नहीं पढ़ा।
मिस्टर लिस्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.