Sjov lille sommeropgave for Linux-geeks

Jeg faldt over en lille opgave den anden dag, som jeg ikke lige havde et af de mange Linux-tools til at løse.
Opgaven er at vise indholdet af et antal tekst-filer ved siden af hinanden - linie for linie - og pænt.

Filen a.txt:

Jeg
læser
v2.dk
nu

Filen b.txt:

holder
du
sommerferie
allerede

Filen c.txt:

fri
blogs
spas
færdig?

Ideen er at få nappet en linie fra hver fil (som her bare har et ord per linie) og printe det efter hinanden på samme linie, herefter tages den næste line fra hver fil og printes på næste linie.
Dette gøres indtil alle linie er processeret. Er en fil blevet løbet til ende må denne blot springes over.

Jeg håber en af læserne har koden til at løse det på en Linux-maskine, og det er underordnet om det er skrevet i Perl, Java, Python, Ruby eller andre relevante sprog.
Lad mig kalde det færdige program "pløf" (for at ære "Bruno" fra DR Ramasjang og Cirkus Summarum - han kan kun tælle til "pløf"):

$ pløf a.txt b.txt c.txt
Jeg   holder      fri
læser du          blogs
v2.dk sommerferie spas
nu    allerede    færdig?

eller

$ pløf a.txt c.txt
Jeg   fri
læser blogs
v2.dk spas
nu    færdig?

Det bemærkes, at jeg forventer at ordene i hver kolonne er venstrestillet, så de ikke rækker ind over nogle af ordene i forrige kolonne (ellers er det for nemt)

Naturligvis gives der ekstra point for den mest ekstreme one-liner, der løser dette :-)

Fat knappeturet og paste koden ind her (evt. via http://http://pastebin.com/)

/pto

P.S. Jeg løste selv opgaven med Emacs og cut-rectangle/yank-rectangle :)

Kommentarer (73)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Mark Gjøl

Problemet med pr er vel egentlig at man skal tage højde for bredden af hver linje fra starten. Altså en two-pass metode. pr -mt bruger en tabulator til at separere, så hvis der er mere end en tabulatorlængde imellem linjerne, så bliver output forkert.

I øvrigt så står linjerne heller ikke tilpas tæt (der er et tab imellem), så det er også noget knald.

  • 0
  • 0
Søren Hansen

Der er nok en, der skal mokke CSV filer i dag. Det skal jeg i hvert fald efter at have fået tilføjet paste(1) til mit aktive kommandoforråd. Mon ikke ikke kommandoen join(1) kommer ret hurtigt i spil også, når nu de to CSV filer ikke er helt sorteret som man gerne ville...

  • 0
  • 0
Baldur Norddahl

Jeg holder på at min version indtil nu er den eneste der faktisk løser opgaven som specificeret.

Output fra mit program (med defekt c.txt som burde være en del af test-suiten):

Jeg   holder      fri      
læser du          du       
v2.dk sommerferie blogs    
nu    allerede    spas     
                  færdig?

Output fra paste:

Jeg  holder  fri  
læser   du  du  
v2.dk   sommerferie blogs  
nu  allerede    spas  
        færdig?

Output fra perl programmet er det samme: simpel brug af tab uden hensyn til kolonnebredde.

Output fra python program:

Jeg holder fri  
læser du du  
v2.dk sommerferie blogs  
nu allerede spas

Haskell er ikke specielt egnet til lige denne type opgave, så jeg er sikker på at i kan slå den i et bedre sprog. Jeg havde bare lyst til at kode noget haskell :-). Men den er ikke slået før i kommer med løsninger der faktisk gør det samme.

  • 2
  • 0
Lars Jeppesen

Denne version i ruby skulle gerne tage højde for den længste linie i hver fil og håndtere manglende linier i filer.
Hvis man har lyst kan man skrive den på en linie ;)

files = ARGV.map { |f| File.open(f) }  
longest = ARGV.map { |f| File.open(f) }.map { |file| file.readlines.inject(0) {|m,s| [m,s.chomp.length].max } }   
begin  
  line = [files, longest].transpose.inject("") { |l,f| l + sprintf("%*s", -(f[1]+1), f[0].eof? ? "" : f[0].readline.chomp) }.rstrip  
  puts line  
end while !line.empty?
  • 0
  • 1
Baldur Norddahl

Version2 skal vist lige overveje lange linjer i kode blokke.

Jeg får følgende resultat med ruby programmet:

Jeg    holder      fri  
læser du          du  
v2.dk  sommerferie blogs  
nu     allerede    spas  
                   færdig?

Den lille fejl ligner noget UTF-8 vs ascii problem.

Det nye python program giver:

Jeg   holder      fri      
læser du          du       
v2.dk sommerferie blogs    
nu    allerede    spas     
Traceback (most recent call last):  
  File "/tmp/p4", line 6, in <module>  
    print ("%%-%ss %%-%ss %%-%ss" % tuple(lens)) % tuple([l[i].strip() for l in lines])  
IndexError: list index out of range
  • 0
  • 0
Søren Lund

Baldur er vist den eneste, der læser specifikationen helt igennem :-) Så, ja, paste alene er ikke nok. Men kodning er der stadig ikke brug for. Vha. column kan vi få rykket ordene på plads:

paste a.txt b.txt c.txt | column -tc3

næsten... nu er der faktisk for meget luft imellem, så sed må også på banen:

paste a.txt b.txt c.txt | column -tc3 | sed &#039;s/ \( *\)/\1/g&#039;

Den "defekte c.txt" giver et andet output end Baldurs løsning, der er kønnere, men Peters specifikation er ikke helt tydeligt på det punkt ;-)

  • 2
  • 0
Lars Jeppesen

Jeg syntes ikke opgaveformuleringen var helt klar mht. encoding af filerne ?
Men nu skulle det virke med UTF-8 filer.

files = ARGV.map { |f| File.open(f) }  
longest = ARGV.map { |f| File.open(f) }.map { |file| file.readlines.inject(0) {|m,s| [m,s.chomp.force_encoding('UTF-8').length].max } }   
begin  
  line = [files, longest].transpose.inject("") { |l,f| l + sprintf("%*s", -(f[1]+1), f[0].eof? ? "" : f[0].readline.chomp.force_encoding('UTF-8')) }.rstrip  
  puts line  
end while !line.empty?
  • 0
  • 1
Finn Aarup Nielsen

Sikken en tåbelig opgave. Nu spilder alle Danmarks nørder deres tid.

Mit første forsøg var dette 6-liniers Python program:

https://gist.github.com/1075844

To-liniers for-løkken kan ændres til en join. Programmet virker ikke med filer med forskellig antal linier (måske skal man læse hvad opgaven går ud på før man løser den?). Et andet program med 'join' i stedet for almindelig 'for' og med håndtering af forskellig linieantal er her:

https://gist.github.com/1075931

på 5-linier med Python3.

  • 0
  • 0
Finn Aarup Nielsen

Det er vel meningen at programmet skal kunne håndtere flere input? Hvis man laver b.txt længere kan man også se at der er nogen varianter præsenteret oven over der placerer ordene i den forkerte kolonne. Her har jeg lavet b.txt længere, slettet et ord fra c.txt, inkluderet en d.txt og kørt den igennem mit Python program:

$ ./pløf a.txt b.txt c.txt b.txt d.txt  
Jeg   holder         fri   holder         er        
læser du             blogs du             der       
v2.dk sommerferierne spas  sommerferierne nogen     
nu    allerede             allerede       hjemme?   
      i                    i                        
      Danmark              Danmark                
  • 0
  • 0
Baldur Norddahl

@Tommi

Det vil være god stil at læse både artiklen ordentligt samt det der allerede er skrevet. Peter skriver:

Det bemærkes, at jeg forventer at ordene i hver kolonne er venstrestillet, så de ikke rækker ind over nogle af ordene i forrige kolonne (ellers er det for nemt)

Din løsning ryger direkte ned i kategorien "ellers er det for nemt".

Søren Lund har en løsning der kombinerer paste med column. En meget men og elegant løsning, der dog ikke håndterer filer af forskellig længde korrekt. Jeg har forbedret den en smule:

paste a.txt b.txt c.txt | column -ntc3

Det ser ud til at gøre det korrekte.

baldur@pkunk:~$ paste a.txt b.txt c.txt b.txt d.txt | column -ntc5  
Jeg    holder       fri    holder       er  
læser  du           blogs  du           der  
v2.dk  sommerferie  spas   sommerferie  nogen  
nu     allerede            allerede     hjemme?  
       i                   i              
       Danmark             Danmark      
  • 1
  • 0
Peter Toft

Jeg kan faktisk (endnu) ikke se at "paste" er løsningen - tæt på men ikke ok. Bardurs sidste skud med paste *txt | column -nts5 giver to mellemrum mellem kolonnerne, og dermed ikke helt perfekt :)
Jeg skal have kigget på de andre forslag. Min gode ven Finn rammer vist heller ikke plet (men tæt på)

./pløf a.txt b.txt c.txt  
Jeg    holder      fri        
læser du          blogs      
v2.dk  sommerferie spas       
nu     allerede    færdig? 

Er det ikke æøå-helvedet i Python, du rammes af?

  • 0
  • 0
Baldur Norddahl

Jeg vil også være med på Scala vognen. Her er min version: http://pastebin.com/n3PrzLvT

val data = args.map(io.Source.fromFile(_).getLines().toArray)  
data.map(_.padTo(data map(_.size) max, "")).  
  map(c=>c.map(_.padTo (c.map(_.size).max+1,' '))).  
  transpose.  
  map(_.mkString).  
  foreach(println)

Det er mest en mindre bearbejdning af de andre Scala programmer.

Programmet er egentlig en two-liner. Jeg kunne ikke finde en måde at gøre det til en ægte one-liner. Problemet er genbrug af "data" to steder i udtrykket.

Programmet køres således:

baldur@pkunk:/tmp$ ~/scala-2.9.0.1/bin/scala pløf.scala a.txt b.txt c.txt b.txt d.txt  
Jeg   holder      fri   holder      er        
læser du          blogs du          der       
v2.dk sommerferie spas  sommerferie nogen     
nu    allerede          allerede    hjemme?   
      i                 i                     
      Danmark           Danmark             
  • 0
  • 0
Lars Jeppesen

Her kommer en one-liner i ruby.
Bemærk at der ikke er brugt ;

[ARGV.map{|f|File.open(f).readlines.map{|l|l.chomp.force_encoding('UTF-8')}}].map {|fi| fi.map{|a|a.fill("", (a.length)..fi.map{|m|m.length}.max-1).map{|a2|a2.ljust(a.map{|s|s.length}.max+1)} }.transpose.each{|i|puts i.join.rstrip}}

->Peter: Det kunne måske være i idé at lægge en test vektor op, så kunne man også checke om man håndtere UTF-8 encoding korrekt.

  • 0
  • 0
Baldur Norddahl

@Troels Genialt :-).

Scoren er:

Scala(Troels) 153 tegn one-liner
Scala(Baldur) 195 tegn
Ruby(Lars) 228 tegn one-liner
Python(Tue) 260 tegn
Scala(Henrik) 280 tegn
Python(Finn) 284 tegn
Ruby(Lars J) 287 tegn
Scala(Lars B) 301 tegn
Haskell(Baldur) 360 tegn

Det er uden whitespace efter bedste evne.

Scala styrer så jeg er meget tilfreds med resultatet...

  • 0
  • 0
Baldur Norddahl

Tak version2 for at smadre indlægget. Jeg prøver igen:

  • Scala(Troels) 153 tegn one-liner
  • Scala(Baldur) 195 tegn
  • Ruby(Lars) 228 tegn one-liner
  • Python(Tue) 260 tegn
  • Scala(Henrik) 280 tegn
  • Python(Finn) 284 tegn
  • Ruby(Lars J) 287 tegn
  • Scala(Lars B) 301 tegn
  • Haskell(Baldur) 360 tegn
  • 0
  • 0
Palle Simonsen

Troels har naturligvis ret. Den rigtige unix (linux) løsning involverer ikke kodning.
Hvis man nøjes med column -t kan sed undværes :)

psi@linux-ets5:~/v2spas> paste a.txt b.txt c.txt | column -t  
Jeg    holder       fri  
læser  du           blogs  
v2.dk  sommerferie  spas  
nu     allerede     færdig?  
psi@linux-ets5:~/v2spas> 

... og så er der sommerferiefisketur i det gode (fiske)vejr! ...

  • 3
  • 1
Finn Aarup Nielsen

@Peter Toft,

Mit program fejler ved Python2, mens det kører udmærket med Python3 (vistnok!? Mine filer og terminal er UTF-8). I Python2 skal man benytte izip_longest i stedet for zip_longest og man skal håndtere Unicode/UTF-8 Python2-problemet. Så det er muligvis æøå-helvedet i Python du er ramt af? :-(

  • 0
  • 0
Finn Aarup Nielsen

Med ikke videre kønt snyderi (fjernelse af hashbang og whitespace samt forkortelse af variabler) kan jeg komme ned på 227 karaktere:

https://gist.github.com/1080175

$ wc pløf3   
  3  19 227 pløf3  
$ python3 pløf3 a.txt b.txt c.txt b.txt d.txt

Lars' Ruby oneliner-version kan vel også blive et par karaktere kortere og kører ok på med ruby1.9.1 (ikke ok med ruby1.8). Jeg kender ikke noget til Ruby, men jeg kunne umiddelbart få Lars' version ned på 223 karaktere.

Der er et stykke vej ned til scala-versionerne. Min ældre default Ubuntu scala 2.7.7 kan forøvrigt ikke klare visse elementer i scala-programmerne. De paste-versioner jeg har testet giver ikke det korrekte resultat hvis min b.txt er længere end a.txt: Anden kolonne bliver rykket for langt mod venstre.

  • 0
  • 0
Eyðun Nielsen

@Morten

Ja, det må da være den version der er kortest. 48 tegn. Ruby, Scala... pffffff.... :-D

user@host:~/work/version2.dk$ paste *.txt | column -t | sed -E 's/ ( +)/\1/g'  
Jeg   holder      fri  
læser du          blogs  
v2.dk sommerferie spas  
nu    allerede    færdig?

@Palle Nej, sed kan vist ikke undværes... Der er et ekstra whitespace i dit output.

God sommer :-)

  • 0
  • 0
Lars Jeppesen

Det lykkes mig at presse ruby versionen ned på 152 tegn, ved at bruge Troels' reduce tricks. Men Ruby har desværre ikke noget lign. zipAll, ellers kunne det blive endnu mindre.

Det kræver dog ruby 1.9x og -Ku option før at UTF-8 virker korrekt.

puts ARGV.map{|f|IO.read(f).split}.reduce(){|a,b|a.fill("",a.size..b.map{|m|m.size}.max-1).zip(b).map{|c|c[0].ljust(a.map{|s|s.size}.max+1)+c[1].to_s}}
  • 0
  • 0
Finn Aarup Nielsen

Nu har jeg fået debugget, forkortet og forlænget Python programmet så det er 197 linier.

import itertools,sys  
t=[open(f).read().split("\n")[:-1]for f in sys.argv[1:]]  
for l in itertools.zip_longest(*t,fillvalue=""):print(" ".join([l[n].ljust(max(map(len,t[n])))for n in range(len(l))]))

Mit tidligere program virkede ikke når der var mellemrum i data-filerne. Til at teste bruger jeg to filer. a.txt som er:

Jeg  
har  
ferie  
   
i  
dag  
!!!

og b.txt som er:

Min  
store  
søde  
blåstripede zebra

Jeg vil så kunne skrive

$ python3 pløf a.txt b.txt a.txt b.txt  
Jeg   Min               Jeg   Min                
har   store             har   store              
ferie søde              ferie søde               
      blåstripede zebra       blåstripede zebra  
i                       i                        
dag                     dag                      
!!!                     !!!     

Paste giver forkert justering med følgende kommandoer: paste a.txt b.txt a.txt b.txt |column -t|sed -E 's/ ( +)/\1/g'

Lars Jeppesen's Ruby program giver forkert resultat når der er mellemrum i linierne.

  • 0
  • 0
Baldur Norddahl

Haskell ser ud til at mangle både zipAll og padTo. Så jeg må selv implementere dem og det koster. Også selvom Haskell lader mig lave en polymorf funktion der klarer begge dele.

import System  
import List  
s r cs=map(\c->(c++replicate((maximum.(map length))cs-(length c))r))cs  
main=do  
 a<-getArgs  
 c<-foldl(\a b->a>>=(\a1->b>>=(\b1->return((lines b1):a1))))(return [])(map readFile a)  
 putStrLn$unlines.(map unwords).transpose$(map(s ' ')$s "" c)

PS. Alle Haskell og Scala programmerne klarer spaces uden problemer fordi der ikke laves nogen antagelser omkring input andet end linjeskift.

  • 0
  • 0
Palle Simonsen

@Eydun

Peter's krav til løsningen er: "Opgaven er at vise indholdet af et antal tekst-filer ved siden af hinanden - linie for linie - og pænt."
Dette fortolker jeg som at en person (f.eks. Peter) skal synes at output er pænt. Dermed løser paste / column den stillede opgave ;)

Kudo's må dog gå til Søren, som bragte paste på banen.

  • 0
  • 0
Palle Simonsen

Hej Henrik

Nu ved jeg ikke, hvad du har puttet i a,b og c. Men jeg har prøvet på OpenSuse 11 og Mint10. I begge tilfælde er det pænt.

Her er en bash version jeg lige har genereret - du/I får lige det hele:

psi@psi-ThinkPad-T61 ~ $ mkdir v2spas  
psi@psi-ThinkPad-T61 ~ $ cd v2spas/  
psi@psi-ThinkPad-T61 ~/v2spas $ cat > a.txt  
venstre1  
venstre2  
venstre3  
psi@psi-ThinkPad-T61 ~/v2spas $ cat > b.txt  
midter1  
midter2  
midter3  
psi@psi-ThinkPad-T61 ~/v2spas $ cat > c.txt  
højre1  
højre2  
højre3  
psi@psi-ThinkPad-T61 ~/v2spas $ paste *.txt | column -t  
venstre1  midter1  højre1  
venstre2  midter2  højre2  
venstre3  midter3  højre3  
psi@psi-ThinkPad-T61 ~/v2spas $ 

Har også prøvet med sed besværgelsen - det er stadig pænt, dog med et whitespace mindre mellem kolonnerne.

psi@psi-ThinkPad-T61 ~/v2spas $ paste a.txt b.txt c.txt|column -t|sed -E 's/ ( +)/\1/g'  
venstre1 midter1 højre1  
venstre2 midter2 højre2  
venstre3 midter3 højre3  
psi@psi-ThinkPad-T61 ~/v2spas $ 

@Henrik hvordan er din terminal sat op, siden kolonnerne vælter? Har du prøvet med en ren xterm? (xterm&) eller uden Xwin?

Nå - fiskestængerne venter (igen) ;)

./palle

  • 0
  • 0
Palle Simonsen

@henrik

Hmm ... linien i a.txt er større end default tabstop, som er 8 tegn. Så med hjælp af tabs går det:

psi@psi-ThinkPad-T61 ~/v2spas $ tabs 1 10 18  
   
psi@psi-ThinkPad-T61 ~/v2spas $ paste *.txt | column -t -s,  
venstre1 midter1 højre1  
         midter2 højre2  
         midter3 højre3  
                 højre4  
                 højre5  
psi@psi-ThinkPad-T61 ~/v2spas $ 

Opgaven er så, at lave shellscriptet pløf, så kaldet af tabs indrettes, så der netop er tabstops ift. den længste linie i hver fil uanset antal filer ... men det bliver en anden dag ...

./palle

  • 0
  • 0
Jens Katz-Kolberg

Når jeg sådan en regnvejrsdag sidder og læser tråden, og til min overraskelse opdager, at der endnu ikke er nogen, der har vist C-udgaven, føler jeg det som min sure pligt at gøre det. Så her er den, komplet med fejl og uden kommentarer:

/* http://www.version2.dk/blog/sjov-lille-sommeropgave-linux-geeks-29627 */  
   
#include <stdlib.h>  
#include <stdio.h>  
#include <assert.h>  
#include <string.h>  
   
typedef struct {   
    int nlines;  
    char **lines;  
    size_t maxlinelen;  
} finfo;  
   
finfo *readfile(char *filename){  
    FILE *fd;  
    finfo *curfile;  
    char buffer[100], *line;  
    size_t linelen;  
   
    curfile=(finfo *)malloc((size_t)sizeof(finfo));  
    curfile->nlines=0;  
    curfile->lines=NULL;  
    curfile->maxlinelen=0;  
   
    fd=fopen(filename, "r");  
    assert(fd!=0);  
   
    while((line=fgets(buffer,80,fd))!=NULL){          
        curfile->nlines++;  
        linelen=strlen(line); line[linelen-1]=0;  
   
        if(linelen>curfile->maxlinelen){ curfile->maxlinelen=linelen; };  
   
        curfile->lines=(char **)realloc(curfile->lines,curfile->nlines*sizeof(char*));  
        assert(curfile->lines != NULL);  
   
        curfile->lines[curfile->nlines-1]=(char*)malloc(linelen*sizeof(char));  
        assert(curfile->lines[curfile->nlines-1] != NULL);  
   
        strcpy(curfile->lines[curfile->nlines-1], line);  
    };  
   
    fclose(fd);  
   
    return curfile;  
}  
   
void spacify_cpy(char *tobuf, int tolen, char *frombuf, int fromlen){  
    int i;  
   
    assert(tolen>=fromlen);  
    for(i=0; i<(tolen-1); i++) tobuf[i]=' ';  
    tobuf[tolen-1]=0;  
   
    for(i=0; i<fromlen; i++) tobuf[i]=frombuf[i];  
}  
   
int main (int argc, const char * argv[])  
{  
    int f, i;  
   
    finfo **files;  
    int nfiles, maxstrlen, maxnlines;  
    char *strbuffer;  
   
    nfiles=argc-1;  
   
    files=(finfo **)malloc(nfiles*sizeof(finfo*));  
   
    maxstrlen=0;  
    maxnlines=0;  
    for(f=0; f<nfiles; f++){  
        files[f]=readfile((char *)argv[f+1]);  
   
        if(files[f]->maxlinelen > maxstrlen){ maxstrlen = (int)files[f]->maxlinelen; }  
        if(files[f]->nlines > maxnlines){ maxnlines = files[f]->nlines; }  
    }  
   
    strbuffer=(char *)malloc(maxstrlen+1);  
   
    for(i=0; i<maxnlines; i++){  
        for(f=0; f<nfiles; f++){  
            if(i >= files[f]->nlines){  
                spacify_cpy(strbuffer, (int)files[f]->maxlinelen, NULL, 0);  
            } else {  
                spacify_cpy(strbuffer, (int)files[f]->maxlinelen, files[f]->lines[i], (int)strlen(files[f]->lines[i]));  
            }  
            printf("%s ", strbuffer);  
        }  
        printf("\n");  
    }  
    return 0;  
}
  • 2
  • 0
Palle Simonsen

Nå, så må vi hellere få en simpel singlepass Common lisp udgave også ...

;;; A simple common lisp version of pløf  
(with-open-file (f1 "a.txt")  
    (with-open-file (f2 "b.txt")  
        (with-open-file (f3 "c.txt")  
            (do ((l1 (read-line f1) (read-line f1 nil 'eof))  
                 (l2 (read-line f2) (read-line f2 nil 'eof))  
                 (l3 (read-line f3) (read-line f3 nil 'eof)))  
                ((and (eq l1 'eof)  
                      (eq l2 'eof)  
                      (eq l3 'eof)) nil)  
        (when (eq l1 'eof) (setf l1 ""))  
        (when (eq l2 'eof) (setf l2 ""))  
        (when (eq l3 'eof) (setf l3 ""))   
        (format t "~8a ~8a ~8a ~%" l1 l2 l3)))))
  • 2
  • 0
Baldur Norddahl

C programmet ser ud til at forudsætte at linjer maksimalt kan være 80 tegn. LISP programmet er hardcoded til tre filer af navn a.txt, b.txt og c.txt.

Kan vi ikke få programmer som er uden sådanne kunstige begrænsninger?

For mine utrænede LISP øjne ser det ikke ud til at programmet beregner kolonnebredde. Men det er måske det der menes med singlepass. Jeg vil bare påpege at mange af de andre programmer, inklusiv de korte Scala programmer, også er singlepass. Man indlæser første fil i en struktur. Dernæst indlæses anden fil i samme struktur samtidig med at man fikser kolonnebredden for første fil, som på det tidspunkt er kendt. Man er naturligvis nødt til at vente med at udskrive indtil det hele er indlæst.

  • 0
  • 0
Baldur Norddahl

@Palle

Peter's krav til løsningen er: "Opgaven er at vise indholdet af et antal tekst-filer ved siden af hinanden - linie for linie - og pænt." Dette fortolker jeg som at en person (f.eks. Peter) skal synes at output er pænt. Dermed løser paste / column den stillede opgave ;)

Peter har allerede dømt paste/column løsningen ude. Han er ikke tilfreds med de ekstra mellemrum. Det stod der egentlig ikke noget om i opgavebeskrivelsen men det er ham som er dommer.

Man kan bruge 'sed' til at fjerne de ekstra mellemrum men ikke uden at få problemer med inputfiler som indeholder dobbelt mellemrum.

  • 0
  • 0
Palle Simonsen

@Baldur

Som man kunne se med tabs ; paste | column udgaven, vil opgaven kunne løses i ren .sh - selv med de nu ændrede regler og tilfældig input data. Jeg vil fastholde, at et shell script er den traditionelle Unix/linux løsning og med lidt yderligere sed eller awk gymnastik og ved at bruge 'wc -L' og evt 'bc' kan man sætte tabs korrekt.

Commonlisp udgaven er ganske rigtig fornærmende primitiv, så den kan let forbedres.

  • 0
  • 0
Baldur Norddahl

@Palle

Tabs er snyd som formodentlig ikke løser Peters problem. Men lad ham være dommer over det. Selvfølgelig kan man løse opgaven med Bash programmering men det er ikke nødvendigvis nemmere eller kortere end de andre løsninger.

Her er et forslag til en Bash løsning:

#!/bin/bash  
   
function maxlen {  
  LEN=0  
  for i in ${!DATA[*]}  
  do  
    CLEN=${#DATA[$i]}  
    if (( $CLEN > $LEN ))  
    then  
      LEN=$CLEN  
    fi  
  done  
}  
   
function padstr {  
  STR=$1  
  while (( ${#STR} < $2 ))  
  do  
    STR="$STR "  
  done  
}  
   
function paddata {  
  maxlen  
  for (( i=0; i<$1; i++ ))  
  do  
    padstr "${DATA[$i]}" $LEN  
    DATA[$i]="$STR"  
  done  
}  
   
for file in "$@"  
do  
  mapfile -t IN < $file  
  paddata ${#IN[*]}  
  for (( i=0; i<${#IN[*]}; i++ ))  
  do  
    DATA[$i]="${DATA[$i]} ${IN[$i]}"  
  done  
done  
   
for (( i=0; i<${#DATA[*]}; i++ ))  
do  
  echo "${DATA[$i]:1}"  
done

Output:

baldur@pkunk:/tmp$ ./pløf.sh a.txt b.txt c.txt d.txt  
Jeg   holder  foo fri   er  
læser du          blogs der  
v2.dk sommerferie spas  nogen  
nu    allerede          hjemme?  
      i  
      Danmark
  • 0
  • 0
Sigmund Vestergaard

Jeg fik lyst til at komme en kommentar. Oprettede derfor en bruger, men var meget overrasket over, at Version2 ikke anvender OpenID... men lad det ligge.

Det var txt filerne i opgaven, jeg ville kommentere på. Enhver Linux-geek ved da, at det er ikke endelsen, det kommer an på... men indholdet. Filerne burde bare kaldes a, b og c - uden noget txt - for at holde sig til Linux-geek-ånden... ;) Jeg vil vende tilbage snart med et bud på en løsning.

PS: De lange kodelinjer ovenfor har fucket siden helt op - det må udviklerne lige se på, når de kommer tilbage fra ferie...

  • 0
  • 0
Log ind eller Opret konto for at kommentere
IT Company Rank
maximize minimize