मैं बैश में तालिकाओं के कॉलम को कैसे संरेखित कर सकता हूं?


86

मैं एक तालिका प्रारूप पाठ का उत्पादन करना चाहूंगा। मैंने जो करने की कोशिश की, वह एक सरणी के तत्वों को 't' के साथ प्रतिध्वनित करता था, लेकिन यह गलत था।

मेरा कोड

for((i=0;i<array_size;i++));
do
   echo stringarray[$i] $'\t' numberarray[$i] $'\t' anotherfieldarray[$i]
done;

मेरा आउटपुट

a very long string..........     112232432      anotherfield
a smaller string         123124343     anotherfield

वांछित उत्पादन

a very long string..........     112232432      anotherfield
a smaller string                 123124343      anotherfield

जवाबों:


96

printf महान है, लेकिन लोग इसके बारे में भूल जाते हैं।

$ for num in 1 10 100 1000 10000 100000 1000000; do printf "%10s %s\n" $num "foobar"; done
         1 foobar
        10 foobar
       100 foobar
      1000 foobar
     10000 foobar
    100000 foobar
   1000000 foobar

$ for((i=0;i<array_size;i++));
do
    printf "%10s %10d %10s" stringarray[$i] numberarray[$i] anotherfieldarray[%i]
done

सूचना मैं %10sतार के लिए इस्तेमाल किया । %sमहत्वपूर्ण हिस्सा है। यह इसे एक स्ट्रिंग का उपयोग करने के लिए कहता है। 10बीच में कहते हैं कि कितने कॉलम यह हो रहा है। %dसंख्या विज्ञान (अंक) के लिए है।

man 1 printf अधिक जानकारी के लिए।


36
सिर्फ एक सलाह जो प्रिंटिंग टेबल के लिए उपयोगी होती है: %-10swiil 10 के बाएं-संरेखित तारों को उत्पन्न करती है
स्टीफन

@UtahJarhead चर स्ट्रिंगर्रे [$ i] का संदर्भ $ {stringarray [i]} द्वारा प्रतिस्थापित किया जाना चाहिए और मुट्ठी स्ट्रिंग रिक्त स्थान होने के कारण इसे "$ {stringarray [i]}" के रूप में उद्धृत किया जा सकता है ताकि अंतरिक्ष वर्ण की व्याख्या की जा सके। परिसीमन करनेवाला।
डैनियल पेरेज़

150

कॉलम कमांड का उपयोग करें:

column -t -s' ' filename

यह प्रश्न में दिए गए उदाहरण के लिए काम नहीं करेगा क्योंकि डेटा के पहले कॉलम में स्थान हैं।
बुरहान अली

1
@BurhanAli क्या मुझे अपनी पिछली टिप्पणी दोहरानी है? सभी उत्तर कुछ परिसीमन मान लेते हैं। ओपी ने परिसीमन के बारे में नहीं कहा है। तो उसी सीमांकक का उपयोग कॉलम में भी किया जा सकता है। जैसे कि डेटा के पहले कॉलम में स्पेस होते हैं तो आप इसे पहले कॉलम कैसे कहते हैं ?
पीपी

1
दोहराने की जरूरत नहीं। मैं उन्हें पढ़ता हूं। मेरी टिप्पणी प्रश्न में वांछित आउटपुट पर आधारित है। दिए गए इनपुट पर इस उत्तर का उपयोग वांछित उत्पादन नहीं करता है।
बुरहान अली

@BurhanAli अगर आप प्रिंटफ का उपयोग करते हैं, तो भी आपको सीमांकक जानना आवश्यक है। %sप्रारूप विनिर्देशक व्हॉट्सएप को सीमांकक के रूप में लेता है। उस स्थिति में, यहां कोई भी उत्तर काम नहीं करेगा। मुझे आश्चर्य है कि आप बार-बार वांछित आउटपुट के बारे में बात करते हैं जब पार्सिंग ( किसी उपकरण का उपयोग करके ) इनपुट के सीमांकक पर निर्भर करता है।
पीपी

2
सीमांकक तैयार करने के लिए उदाहरण:cat /etc/fstab | sed -r 's/\s+/ /g' | column -t -s' '
अप्रशिक्षित

16

आपकी आवश्यकता के समान सटीक आउटपुट के लिए, आपको फ़ाइल को इस तरह प्रारूपित करने की आवश्यकता है:

a very long string..........\t     112232432\t     anotherfield\n
a smaller string\t      123124343\t     anotherfield\n

और फिर उपयोग:

$ column -t -s $'\t' FILE
a very long string..........  112232432  anotherfield
a smaller string              123124343  anotherfield

2
क्या करने $में है $'\t'?
rjmunro

यदि 2 कॉलम आकार में भिन्न 5 वर्णों से अधिक हैं, तो टैबस्टॉप का उपयोग पूरी तरह से अनुपयोगी हो जाता है।
यूटाजारह


16
function printTable()
{
    local -r delimiter="${1}"
    local -r data="$(removeEmptyLines "${2}")"

    if [[ "${delimiter}" != '' && "$(isEmptyString "${data}")" = 'false' ]]
    then
        local -r numberOfLines="$(wc -l <<< "${data}")"

        if [[ "${numberOfLines}" -gt '0' ]]
        then
            local table=''
            local i=1

            for ((i = 1; i <= "${numberOfLines}"; i = i + 1))
            do
                local line=''
                line="$(sed "${i}q;d" <<< "${data}")"

                local numberOfColumns='0'
                numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<< "${line}")"

                # Add Line Delimiter

                if [[ "${i}" -eq '1' ]]
                then
                    table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
                fi

                # Add Header Or Body

                table="${table}\n"

                local j=1

                for ((j = 1; j <= "${numberOfColumns}"; j = j + 1))
                do
                    table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<< "${line}")")"
                done

                table="${table}#|\n"

                # Add Line Delimiter

                if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]]
                then
                    table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
                fi
            done

            if [[ "$(isEmptyString "${table}")" = 'false' ]]
            then
                echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1'
            fi
        fi
    fi
}

function removeEmptyLines()
{
    local -r content="${1}"

    echo -e "${content}" | sed '/^\s*$/d'
}

function repeatString()
{
    local -r string="${1}"
    local -r numberToRepeat="${2}"

    if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
    then
        local -r result="$(printf "%${numberToRepeat}s")"
        echo -e "${result// /${string}}"
    fi
}

function isEmptyString()
{
    local -r string="${1}"

    if [[ "$(trimString "${string}")" = '' ]]
    then
        echo 'true' && return 0
    fi

    echo 'false' && return 1
}

function trimString()
{
    local -r string="${1}"

    sed 's,^[[:blank:]]*,,' <<< "${string}" | sed 's,[[:blank:]]*$,,'
}

नमूना दौड़

$ cat data-1.txt
HEADER 1,HEADER 2,HEADER 3

$ printTable ',' "$(cat data-1.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+

$ cat data-2.txt
HEADER 1,HEADER 2,HEADER 3
data 1,data 2,data 3

$ printTable ',' "$(cat data-2.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+
| data 1    | data 2    | data 3    |
+-----------+-----------+-----------+

$ cat data-3.txt
HEADER 1,HEADER 2,HEADER 3
data 1,data 2,data 3
data 4,data 5,data 6

$ printTable ',' "$(cat data-3.txt)"
+-----------+-----------+-----------+
| HEADER 1  | HEADER 2  | HEADER 3  |
+-----------+-----------+-----------+
| data 1    | data 2    | data 3    |
| data 4    | data 5    | data 6    |
+-----------+-----------+-----------+

$ cat data-4.txt
HEADER
data

$ printTable ',' "$(cat data-4.txt)"
+---------+
| HEADER  |
+---------+
| data    |
+---------+

$ cat data-5.txt
HEADER

data 1

data 2

$ printTable ',' "$(cat data-5.txt)"
+---------+
| HEADER  |
+---------+
| data 1  |
| data 2  |
+---------+

REF LIB यहां: https://github.com/gdbtek/linux-cookbooks/blob/master/lbooks/util.bash


धन्यवाद! यही वह है जिसकी तलाश में मैं हूं। मैक उपयोगकर्ताओं के लिए: आपको -eडैश को सही ढंग से प्रिंट करने के लिए इको कमांड में पैरामीटर को निकालना होगा ।
ओड्म

बहुत बढ़िया और बढ़िया नमूना! धन्यवाद!
जुनेगॉन्ग ओह

बदलते रंग वास्तव में संरेखण के साथ खिलवाड़ करते हैं। यकीन नहीं होता क्यों ...
हम्म्म

@mattdevio क्या आपने रंगों के लिए एक फिक्स पाया है?
कार्लोस फ्लोरोस्कोयो

2
सचेत! रेफरी लिंक में एक अधिक अद्यतन फ़ंक्शन है।
insign

4

यह आपके आश्चर्य से आसान है।

यदि आप अर्धविराम फ़ाइल और हेडर द्वारा एक अलग से काम कर रहे हैं:

$ (head -n1 file.csv && sort file.csv | grep -v <header>) | column -s";" -t

यदि आप सरणी के साथ काम कर रहे हैं (विभाजक के रूप में टैब का उपयोग कर):

for((i=0;i<array_size;i++));
do

   echo stringarray[$i] $'\t' numberarray[$i] $'\t' anotherfieldarray[$i] >> tmp_file.csv

done;

cat file.csv | column -t

4

awk समाधान जो स्टड से संबंधित है

चूंकि columnPOSIX नहीं है, शायद यह है:

mycolumn() (
  file="${1:--}"
  if [ "$file" = - ]; then
    file="$(mktemp)"
    cat > "${file}"
  fi
  awk '
  FNR == 1 { if (NR == FNR) next }
  NR == FNR {
    for (i = 1; i <= NF; i++) {
      l = length($i)
      if (w[i] < l)
        w[i] = l
    }
    next
  }
  {
    for (i = 1; i <= NF; i++)
      printf "%*s", w[i] + (i > 1 ? 1 : 0), $i
    print ""
  }
  ' "$file" "$file"
  if [ "$1" = - ]; then
    rm "$file"
  fi
)

परीक्षा:

printf '12 1234 1
12345678 1 123
1234 123456 123456
' > file

टेस्ट कमांड:

mycolumn file
mycolumn <file
mycolumn - <file

सभी के लिए आउटपुट:

      12   1234      1
12345678      1    123
    1234 123456 123456

यह सभी देखें:


1
if [ "$file" = - ]; thenअंत में होना चाहिए if [ "$1" = - ]; then। वर्तमान कोड के साथ, आप कभी भी अपनी अस्थायी फ़ाइलों को साफ़ नहीं करते हैं।
कैमुसेन्सी

3

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

इसके बजाय यह प्रयास करें:

stringarray=('test' 'some thing' 'very long long long string' 'blah')
numberarray=(1 22 7777 8888888888)
anotherfieldarray=('other' 'mixed' 456 'data')
array_size=4

for((i=0;i<array_size;i++))
do
    echo ${stringarray[$i]} $'\x1d' ${numberarray[$i]} $'\x1d' ${anotherfieldarray[$i]}
done | column -t -s$'\x1d'

ध्यान दें कि मैं ग्रुप सेपरेटर कैरेक्टर (1d) टैब का उपयोग कर रहा हूं, क्योंकि अगर आपको ये एरेज़ किसी फ़ाइल से मिल रहे हैं, तो उनमें टैब हो सकते हैं।


0

बस अगर कोई ऐसा करना चाहता है तो PHP में मैंने Github पर एक जिस्म पोस्ट किया

https://gist.github.com/redestructa/2a7691e7f3ae69ec5161220c99e2d1b3

बस कॉल करें:

$output = $tablePrinter->printLinesIntoArray($items, ['title', 'chilProp2']);

यदि आप 7.2 से अधिक पुराने php संस्करण का उपयोग कर रहे हैं तो आपको कोड को अनुकूलित करने की आवश्यकता हो सकती है

उसके बाद अपने वातावरण के आधार पर इको या राइटलाइन को कॉल करें।


0

नीचे दिए गए कोड का परीक्षण किया गया है और वही करता है जो मूल प्रश्न में अनुरोध किया गया है।

पैरामीटर्स: 30% 30 कॉलम का कॉलम और टेक्स्ट राइट एलाइन। % 10d पूर्णांक संकेतन,% 10s भी काम करेगा। जोड़ा गया स्पष्टीकरण कोड टिप्पणियों में शामिल है।

stringarray[0]="a very long string.........."
# 28Char (max length for this column)
numberarray[0]=1122324333
# 10digits (max length for this column)
anotherfield[0]="anotherfield"
# 12Char (max length for this column)
stringarray[1]="a smaller string....."
numberarray[1]=123124343
anotherfield[1]="anotherfield"

printf "%30s %10d %13s" "${stringarray[0]}" ${numberarray[0]} "${anotherfield[0]}"
printf "\n"
printf "%30s %10d %13s" "${stringarray[1]}" ${numberarray[1]} "${anotherfield[1]}"
# a var string with spaces has to be quoted
printf "\n Next line will fail \n"      
printf "%30s %10d %13s" ${stringarray[0]} ${numberarray[0]} "${anotherfield[0]}"



  a very long string.......... 1122324333  anotherfield
         a smaller string.....  123124343  anotherfield

जैसा कि @steffen द्वारा इंगित किया गया है, बाएं संरेखित उपयोग के लिए "-" प्रतीक अर्थात प्रिंटफ "% -30 s" "$ {stringarray [0]}"
डैनियल पेरेज़
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.