Archivi Categorie: awk

Elimina le righe duplicate, ma tieni le vuote

È fattibile uno script che dato un file di testo faccia come da titolo? Uh! vediamo 😯

Un compito semplice e conosciutissimo –anche da me– esegue la prima parte, eccolo:

#!/usr/bin/awk -f

!a[$0]++

Funziona perfettamente, esempio:

Proprio OK, eliminate tutte le doppie.
Ma a me servirebbe un’altra cosa: le righe vuote voglio tenerle tutte. Questo mi serve per le raccolte di Visto nel Web, AI e innovazioni e cit. e loll. dove le righe vuote separano le descrizioni dei link. OK, dovrei riscrivere tutto ma adesso va di moda –funzionalmente– essere lazy… 😉

AWK è uno dei miei linguaggi preferiti da sempre; appena misi le mani su un ‘puter Unix feci le fotocopie dei manuali di sh, vi, sed e AWK. Per Fortran e C avevo già tutto, cioè no, qualcosa: il K&R troppo sintetico, il TAB da aggiustare per il Fortran, le opzioni di compilazione, mica c’era Stak Overflow allora 😐 E neanche il Web. E anche i BBS sarebbero venuti dopo (con l’accoppiatore acustico (modem) a 2400 BPS). OK, sono OT 😐

Non so se la soluzione cui sono giunto sia bella, ma mi sembra funzioni e per adesso è questa (ndtv0):

#!/usr/bin/awk -f

{ if  ($1 ~ /^\#:/ || $0 == "")
  { print }
  else
  { { if (!a[$0]++) print }
  }
}

A dirla tutta c’è una riga vuota di troppo ma questo non da fastidio al mio caso. Questo però è solo il primo passo. Devo salvare l’output in un file e confrontarlo con quello di input. Se i due coincidono lo cancello altrimenti cancello l’originale (no, lo rinomino con estensione .bak) e rimonimo l’output con il nome dell’originale (file ndtv1).

#!/bin/bash

ndtv0 $1 > $1".tmp"
diff -s $1 $1".tmp"
R=$?
if [ $R == 1 ]; then
  echo "aggiorno"
  mv $1 $1".bak"
  mv $1".tmp" $1
else
  rm $1".tmp"
fi

Ripetendo il comando il file dovrebbe già essere OK:

OK, non resta che abilitare ndtv0 e ndtv1 (dopo averlo spostato in ~/bin o in un’altra dir presente in $PATH).

Ma è una cosa tutt’altro che finita. Nello script AWK ho inserito di tenere le righe delle categorie, quelle che iniziano con #: (notare che # è speciale e dev’essere baskslashato).

Il file risultante da questo filtro potrà risultare con righe fuori posto, come le due righe vuote consecutive dell’esempio. Ma qui continuo con un linguaggio normale (Python in questo caso) e il lavoro diventa più normale, noioso. Un collega di una volta (quasi 30 anni fa) dissente dal mio insistere con roba tipo AWK. Sono vecchio (ma anche lui, anagraficamente). Forse è arrivata per me l’ora di smettere, anche perché i blog sono passati di moda 😐

XKCD è la risposta

climagic

Uno dei mystery mysteryousy tuttora senza risposta è il significato di XKCD. Tra le teorie proposte quella che ha più probabilità di risultare un giorno dimostrata è quella che trovate in questo papero cinguettio: The hidden meaning of #XKCD.

È da prendere come dogma (è così, credici!)?
No, nope, come vado a dimostrare [1].

xkcd

Nella sua forma completa lo script è:

printf "%s\n" {A..Z} | nl | grep "[XKCD]" | awk '{sum+=$1} END {print sum}' # The hidden meaning of #XKCD. #AWKtober

Ma si può scomporre; intanto (solo per questo caso, in tutti gli altri servono, sono preziosi) si può togliere il commento finale, il primo # e tutto quello che segue. Però prima ci ricordiamo che #AWKtober (leggetelo come se foste ‘mericani) è un’istituzione, quasi segreta ma si può guardare qui, qui, qui e qui.
Se vi piace Justin Bieber c’è anche questa performance, non assumo responsabilità, nèh,
Twitter poi ha tutta una lista, come si poteva desumere fin da subito dall’hashtag.

OK, vengo allo script, decompongo.
Come primo passo lo riduco così: printf "%s\n" {A..Z} ottenendo

x1

questo è solo l’inizio va fino a Z come richiesto.
Aggiungiamo il secondo pezzo, concatenandolo via pipe: printf "%s\n" {A..Z} | nl e abbiamo

x2

Uh! adesso il terzo blocco, printf "%s\n" {A..Z} | nl | grep "[XKCD]" ed ecco:

x3

Ah! comincia ad essere chiaro basta fare la somma dei records del campo 1, cosa delegata a AWK:printf "%s\n" {A..Z} | nl | grep "[XKCD]" | awk '{sum+=$1} END {print sum}'

x4

OK! possiamo fare anche la verifica, usando uno tra i millantamila linguaggi di programmazione; che ne dite di usare il migliore, così:

x5

OK! adesso fatelo con Windows [2] 😀

Io posso smettere quando voglio. Probabilmente… forse…


[1] con la collaborazione di Sole (yesss! esistono anche ings prestate all’informatica).
[2] Sole si dissocia, netti che teh Boss legge il post…

:mrgreen:

AWK, grep e oltre: ack

rr1

Continuo da qui la serie di post su AWK. Cioè dovrei ma oggi esco leggermente fuori tema e parlo di ack di cui ignoravo colpevolmente e completamente l’esistenza. Poi –OK, vi racconto.

I file di visto nel Web li conservo in una directory (cartella) perché non si sa mai. Ma cercare qualcosa lì dentro è come cercare il famoso ago nel pagliaio (ma se ti va bene ti può capitare di trovare la figlia del contadino, dice J.H.Comroe).

Ecco io ero alla ricerca di qualcosa che ricordo benissimo di aver citato –forse– ma esaminare tutti i file non è facile. Anche grep si lamenta, scrive righe non pertinenti, esempio:

sre-7

Eppure… possibile che non ci sia un’opzione giusta? Per fortuna c’è Google. E quando hai un problema c’è una probabilità altissima che lo stesso problema l’abbia già avuto qualcun altro più smart di me. Così sono finito su ack.
Può sostituire grep nel 99% dei casi dice il suo autore Andy Lester.
La home di ack non mi è nuova vuoi vedere che… (da verificare).

Insomma, a farla breve l’ho installato con sudo apt install ack-grep ed ecco

s-re8

OK, da scriptare prima di subito (~/bin/sivw):

#!/bin/bash

ack -iR "$1" ~/lab/OKp/visto\ nel\ web/

abilitarlo con con chmod +x e

s-re9

ricordavo perfettamente (kwasy), e pensa te che

s-re10

aprile 2012, ero giovane allora.

Intanto sono in ritardo, ci sono tante altre novità, per esempio adesso si sta svolgendo #RacketCon, tante cose, da raccontare, per adesso un’immagine

emina-torlak

:mrgreen:

AWK e grep – qualche precisazione

fermat

La raccolta di script AWK pare sia piaciuta. Forse continua, ho ancora materiale, prossimamente… forse…

Ma subito una risposta veloce per un caso trattato superficialmente, questo:

Scrive solo le righe con una regex specificata
awk '/questo/'

OK, ammetto che sono stato troppo superficiale. Integro ma mi riservo una nota finale.

Userò per gli esempi il file txt, questo

uno
due
questo e quello
questa
questo
quella
quello
(5)
otto
ot to
ottantotto

Ed ecco:

s-re1

In alternativa si può usare grep

s-re2

Simili, come si vede, con grep si omettono gli /, la regex trovata viene colorata per via dell’alias

alias grep='grep --color=auto'

grep ha inoltre l’opzione -i per cercare ignorando la differenza tra maiuscole e minuscole, cosa che con AWK (vedi post linkato) è più macchinosa.

Apici o virgolette possono essere omessi in casi normali

s-re3

ma ci sono eccezioni:

s-re4

In alternativa è possibile escapare le parentesi con \ che trasforma il carattere seguente da speciale a normale (letterale).

s-re5

Tranne casi particolari (mai dire mai) mi sembra meno intuitivo.

Resta un grosso argomento da affrontare: le espressioni regolari, regexs o RE. Su di loro sono stati scritti volumi, versati barili d’inchiostro. E io faccio spesso qualche tentativo prima di ottenere quello che voglio.

Nella pagina di raccolta di manuali su questo blog trovate almeno un link minaccioso (dovrei aggiornarla), qui.

In generale mi sembra che questo caso sia meglio usare grep, ma chissà…
Un esempio vero, mio. Per compilare le telenovelas “Visto nel Web” e “cit. & loll” devo indicare l’URL della pagina linkata. Ogni URL è poi collegato a un sito; per sapere se l’ho già memorizzato uso questo script:

s-re6

Infine, tornando ai posts su AWK: sono un punto di partenza …

Aggiornamento
Ho aggiunto 2 tutorials e un quick reference per le espressioni regolari sulla pagina dei manuali. Altro materiale è disponibile online ma non so quanto regolare (inteso come rispettoso dei diriti degli autori).

:mrgreen:

AWK – raccolta di scripts – 2

nm5

Continuo da qui. Do per scontato (a differenza del post precedente) l’uso di files* e delle pipes.

Converte CR-LF in LF
utile per i file .txt di Windows. Personalmente ho creato lo script w2l e il corrispondente l2w. Gli editors di Linux non hanno in genere problemi a leggere i files di Windows. Cosa che capita invece di là se si usa l’orrendo Blocco note | Notepad. A volte è tragico 👿
awk '{sub(/\r$/,"")};1'
preferisco
sed 's/\r$//'g
oppure ancora meglio
tr -d "\r"

Converte LF in CR-LF
il complemento al precedente (l2w)
awk '{sub(/$/,"\r")};1'
preferisco
sed 's/$/\r/'g

Cancella spazi e tab iniziali (ltrim)
awk '{sub(/^[ \t]+/, "")};1'

Cancella spazi e tab finali (rtrim)
awk '{sub(/[ \t]+$/, "")};1'

Cancella spazi e tab iniziali e finali (trim)
awk '{gsub(/^[ \t]+|[ \t]+$/,"")};1'

Cancella spazi e tab iniziali e finali (trim) e spazi multipli tra i campi
awk '{$1=$1};1'

Inserisce 4 spazi allinizio della riga
awk '{sub(/^/, "    ")};1'
ma meglio
sed 's/^/    /'

Sostituisce “foo” con “bar” per ogni riga solo la prima istanza
awk '{sub(/foo/,"bar")}; 1'
meglio
sed 's/foo/bar/'

Sostituisce “foo” con “bar” per ogni riga solo la quarta istanza
occorre installare gawk
gawk '{$0=gensub(/foo/,"bar",4)}; 1'

Sostituisce “foo” con “bar” per ogni riga, tutte le istanze
awk '{gsub(/foo/,"bar")}; 1'
meglio
sed 's/foo/bar/g'

Sostituisce “foo” con “bar” solo se la riga contiene “baz”
vedi sopra per sub/gsub
awk '/baz/{gsub(/foo/, "bar")}; 1'

Sostituisce “foo” con “bar” solo se la riga non contiene “baz”
awk '!/baz/{gsub(/foo/, "bar")}; 1'

Sostituisce “alpha”, “beta” o “gamma” con “delta”
awk '{gsub(/alpha|beta|gamma/, "delta")}; 1'
meglio
sed -r 's/alpha|beta|gamma/delta/g'

Se la riga finisce con \ gli appende la successiva
fallisce con righe multiple terminanti con \ (ma basta rilanciarlo)
awk '/\\$/ {sub(/\\$/,""); getline t; print $0 t; next}; 1'

Scrive i nomi di tutti gli utenti
awk -F ":" '{print $1 | "sort" }' /etc/passwd

Scambia i campi #1 e 2 (generalizzabile ovviamente)
awk '{print $2, $1}'
oppure, più facile da ricordare
awk '{temp = $1; $1 = $2; $2 = temp}; {print}'

Cancella il campo 2
awk '{ $2 = ""; print }'

Scrive i campi in ordine inverso
awk '{for (i=NF; i>0; i--) printf("%s ",$i);print ""}'

Concatena ogni 3 righe separandole con virgola
awk 'ORS=NR%3?", ":"\n"'

Scrive le righe con il campo 3 uguale a OK
awk '$3 == "OK"'

Scrive le righe con il campo 3 diversi da OK
awk '$3 != "OK"'
o anche
awk '!($3 == "OK")'

Trova le rige che contengono A,  B e C in qualsiasi ordine
awk '/A/ && /B/ && /C/'

Trova le rige che contengono A, B e C in questo ordine
awk '/A.*B.*C/'

Righe più lunghe di 10 caratteri (sì tutti gli Unicode)
awk 'length > 10'

Scrive le righe da 2 a 4
awk 'NR==2,NR==4'

Scrive solo la riga 5
awk 'NR==5 {print;exit}'

Scrive solo le righe con una regexs specificata
awk '/questo/'

versione case-insensitive
awk 'BEGIN {IGNORECASE = 1} /questo/'

a3

Scrive solo le righe che non contengono la regex
awk '!/questa/'

Scrive da una regex alla fine del file
awk '/questa/,0'

Scrive le righe tra 2 regexs
awk '/inizio/,/fine/'

Scrive solo le righe non vuote come grep '.'; la seconda versione tiene buone anche quelle fatte da soli blanks (come grep)
awk NF
awk '/./'

Rimuove le righe duplicate, non consecutive
awk '!a[$0]++'
versione miliore
awk '!($0 in a){a[$0];print}'
attenti ai blanks finali, forse meglio trimmare prima; questo l’ho scriptato e lo uso, ne ho parlato qui.

C’è ancora materiale da esaminare; purtroppo non elaborato dal mio giovane (ex?) collaboratore; dovrò farlo io, prossimamente 😉

:mrgreen:

AWK – raccolta di scripts – 1

Uno progetto partito e poi, per ragioni di lavoro, interrotto e che adesso riparte 😀
Consiste nella personalizzazione di scripts (spesso one-liner) AWK, copiati, adattandoli, da qui: Handy one-line scripts for AWK – Compiled by Eric Pement – eric [at] pement.org.
Alro materiale proviene da qui compilato da qualcuno di Canisius College.
Il lavoro è stato svolto da un giovane collaboratore che –prima di finire– è finito a comporre siti Web, quindi HTML, CSS, JavaScript e sinili in ambiente Microsoft (ecco, l’ecosistema). Se poi vorrà riprendere aggiornerò 😀

6a00

Il mio compito consiste nella sola pubblicazione dell’elenco, previo test 😀 se del caso con note esplicative.

Inserire una riga vuota dopo ogni riga
awk '1;{print ""}'

oppure

awk 'BEGIN{ORS="\n\n"};1'

a0

vale per tutti: scriptarlo

a1

oppure, notare awk nella shebang, la prima riga


#!/usr/bin/awk -f
awk 1;{print ""}  $*

per l’opzione -f vedere l’help; si suppone che il lettore sappia come fare, come pure altre cose normali per esempio reindirizzare l’output con > file-di-output, p.es.: ./ins-v t1.txt t2.txt > tres.

Inserire 2 righe vuote ogni riga
awk '1;{print "\n"}'

oppure

awk 'BEGIN{ORS="\n\n\n"};1'

Numerare le righe
ripartire per ogni file
awk '{print FNR "\t" $0}' files*

numerazione consetutiva, uno dei seguenti
awk '{print NR "\t" $0}' files*
awk '{printf("%5d : %s\n", NR,$0)}' files*
awk 'NF{$0=++a " :" $0};1' files*
awk '{print (NF? ++a " :" :"") $0}' files*

files* è la lista de file da trattare.

Numero di campi di ogni riga
awk '{print $0 " : " NF}' files*

Conta la somma del numero dei campi
awk '{s=s+NF; print $0 " : " s}; END{print s}' files*

come sopra ma scrive solo il totale
awk '{s=s+NF}; END{print s}' files*

come i precedenti ma ridefinendo FS (field separator); in questo modo il secondo è una versione di wc -w
awk 'BEGIN {FS=":"}; {s=s+NF; print $0 " : " s}; END{print s}' files*
awk 'BEGIN {FS=":"}; {s=s+NF}; END{print s}' files*

a2

Per i campi numerici mette il valore assoluto; invariate le stringhe
awk '{for (i=1; i<=NF; i++) if ($i < 0) $i = -$i; print }' files*

a3

Scrive la riga con il primo campo più lungo
awk '$1 > max {max=$1; maxline=$0}; END{ print max, maxline}' files*

Scrive la riga più lunga

awk 'BEGIN {maxlength = 0; longest = 0} \
    {                                   \
        if (length($0) > maxlength) {   \
            maxlength = length($0);     \
            longest = $0                \
        }                               \
    }                                   \
    END {print maxlength, longest}' files*

Siccome il testo è lungo si è usato il carattere \ per annullare l’a-capo.

Scrive l’ultimo campo di ogni riga
awk '{print $NF}' *files

Scrive l’ultimo campo dell’ultima riga
awk 'END{print $NF}' *files

Scrive le righe con più di 4 campi
awk 'NF > 4' *files

scrive le linee in cui il secondo campo è maggiore di 3
awk '$2 > 3' *files

Crea una stringa di 8 “o”
awk 'BEGIN{while (a++<8) s=s "o"; print s}'

Creare una sequenza è possibile ma non semplice; vedi il manuale di GNUawk e StackOverflow (range); conviene usare altri tools.

Pausa ma continua 😀

:mrgreen:

nodupt – un tool che migliora uniq

havetouched

Alle volte servono tools semplici, Linux li ha, ereditati da Unix, ma a volte serve personalizzarli. Ecco un esempio, perso.
Il mio caso è quello di voler eliminare le righe duplicate di uno stream, un file di testo, un .txt.
C’è il comando uniq ma nel mio caso non basta. Adesso vi conto.

Per visualizzare il file di dati (uno finto, creato per questo post, ma simile a quello reale) ricorro a uno script awk altrimenti non riesco a visualizzare i blanks. Lo script è lungo una riga (altre la shebang), questo, show:

#!/usr/bin/awk -f
{printf("*%s*\n", $0)}

Come si vede legge il/i files passatigli sulla riga di comando e li riscrive racchiudendo ogni riga tra due asterischi. Lo abilito ed ecco:

c0

Allora il file dup ha righe ripetute, non consecutive e con dei blanks (spazi e tabs) fuori posto. C’è il comando uniq ma non fa quello che voglio:

c1

Ma con Linux (e una volta Unix) è semplicissimo, ci sono riuscito anch’io, ecco nodupt:

#!/bin/bash
trimall $1 | nodup

che fa esattamente quello che mi serve:

c2

OK 😀 semplice vero?
😳 sì, avete ragione, dimenticavo 😳

nodupt è la concatenazione di due scripts, semplicissimi:

ecco trimall:

#!/usr/bin/awk -f
{$1=$1};1

e nodup:

#!/usr/bin/awk -f
!($0 in a){a[$0];print}

Come suggeriscono i nomi trimall elimina i blanks iniziali, finali e ridondanti tra i campi di ogni riga e passa l’output a nodup che elimina le righe duplicate.

Adesso un paio di confessioni: non è tutta farina del mio sacco.
Su awk prossimamente un post con tante ricette, lo sta preparando un mio giovane collaboratore (anzi, forse no, si farà un blog tutto suo, forse).
L’idea della concatenazione mi ha tormentato tutta la notte (afosa come non mai) per via che ieri ho scoperto questo post: Concatenative language.
Avendo tempo ci sarebbero parecchie idee qui. Chissà… 😉

:mrgreen:

Fattori di un numero intero

bb3OK, sono indaffarato su altre cose ma non posso abbandonare Racket, il mio linguaggio di programmazione preferito (con zilioni di altri, tutti pari-merito).
E come già in passato (è piaciuto) un confronto con altri dei miei linguaggi preferiti. Poi prossimamente continua lo studio di Rust, anche lì si stanno accumulando robe: sono travolto dai ritardi 🙄

Sto copiando da Rosetta code, qui: Factors of an integer.

AWK

Comincio con un’eccezione: AWK non è multipiattaforma ma ci sono affezionato da sempre

#!/usr/bin/awk
BEGIN {
    print("enter a number or C/R to exit")
}
{   if ($0 == "") { exit(0) }
    if ($0 !~ /^[0-9]+$/) {
      printf("invalid: %s\n",$0)
      next
    }
    n = $0
    printf("factors of %s:",n)
    for (i=1; i<=n; i++) {
      if (n % i == 0) {
        printf(" %d",i)
      }
    }
    printf("\n")
}

f0Per passare il numero da fattorizzare sulla riga di comando si può fare:

#!/usr/bin/awk
{
    if ($0 == "") { exit(0) }
    if ($0 !~ /^[0-9]+$/) {
      printf("invalid: %s\n",$0)
      next
    }
    n = $0
    printf("factors of %s:",n)
    for (i=1; i<=n; i++) {
      if (n % i == 0) {
        printf(" %d",i)
      }
    }
    printf("\n")
}

f1E il comando si può –ovviamente semplificare:

#!/bin/bash
echo $1 | awk -f f1.awk

f2

Python

La versione più semplice è semplicissima:

#!/usr/bin/python3

def factors(n):
    return [i for i in range(1, n + 1) if not n%i]

print(42, ":", factors(42))
print(2701, ":", factors(2701))
print(32768, ":", factors(32767))

f3La prima ottimizzazione si ottiene considerando solo i numeri compresi nell’intervallo [1 .. n/2 + 1]. Notare // per la divisione fra interi (dalla versione 3). In questo caso il numero stesso non compare nell’output.

#!/usr/bin/python3

def factors(n):
    return [i for i in range(1, n // 2 + 1) if not n%i]

print(42, ":", factors(42))
print(2701, ":", factors(2701))
print(32768, ":", factors(32767))

f4Meglio ancora, i divisori non possono essere maggiori della radice quadrata del numero; output come il caso precedente (ho modificato il range del for facendolo partire da 2, con 1 il numero testato compare nell’output).

#!/usr/bin/python3

from math import sqrt

def factors(n):
    factors = set()
    for x in range(2, int(sqrt(n)) + 1):
        if n % x == 0:
            factors.add(x)
            factors.add(n // x)
    return sorted(factors)
      
print(42, ":", factors(42))
print(2701, ":", factors(2701))
print(32768, ":", factors(32767))

Quant’è efficace l’ottimizzazione? considero un numero grosso, 1323116819 e provo, modificando gli script che elaborino il primo parametro passato, così:

#!/usr/bin/python3

import sys

def factors(n):
    return [i for i in range(1, n + 1) if not n%i]

N = int(sys.argv[1])
print(N, ":", factors(N))

f5Eh, sì 😀

Racket

La versione elementare:

#lang racket
 
;; a naive version
(define (naive-factors n)
  (for/list ([i (in-range 1 (add1 n))]
             #:when (zero? (modulo n i))) i))

(printf "~a : " 42)
(printf "~a \n" (naive-factors 42))

f6Molto meglio usando factorize per ricavare i primi e costruirne la lista:

#lang racket
 
(require math)
(define (factors n)
  (sort (for/fold ([l '(1)]) ([p (factorize n)])
          (append (for*/list ([e (in-range 1 (add1 (cadr p)))] [x l])
                    (* x (expt (car p) e)))
                  l))
        <))

(printf "~a : " 42)
(printf "~a \n" (factors 42))

 

Quanto è più veloce? Provo con lo stesso numero usato per Python:

#lang racket

;; a naive version
(define (naive-factors n)
  (for/list ([i (in-range 1 (add1 n))]
             #:when (zero? (modulo n i))) i))

(require math)
(define (factors n)
  (sort (for/fold ([l '(1)]) ([p (factorize n)])
          (append (for*/list ([e (in-range 1 (add1 (cadr p)))] [x l])
                    (* x (expt (car p) e)))
                  l))
        <))

(define N 1323116819)
(printf "naive version, ~a : " N)
(time (printf "~a \n" (naive-factors N)))
(printf "better version, ~a : " N)
(time (printf "~a \n" (factors N)))

f7Ma esiste un modo ancora migliore, usare la funzione divisors che fa la stessa cosa di factorize ma è più veloce. Uso il numero enorme di Rosetta.

f8

Anche sulle cose semplici c’è molto da imparare 🙄

:mrgreen:

Python vs. Perl

Programming-republic-of-perlSono in ritardo su tutto; devo ancora finire due cose per domani ma devo proprio dire una cosa. Anche perché riguarda in qualche misura quello che devo fare per domani.
Perl, devo essere stato uno dei primi a affrontarlo, è nato nel 1987, controllato anche sulla Wiki, non è un errore, credevo prima. No, non ho prove, anche i libri non li ho più. OK, nostalgia mode OFF 😯

Oggi ho trovato questo post: The Fall Of Perl, The Web’s Most Promising Language.
L’autore, Conor Myhrvold, è –come dire– eclettico. Ma conosce bene la materia il post è informato e esaustivo.

camelMa il motivo vero di queste note è un altro, anzi più di uno.

Python
Oggi le cose in cui Perl eccelleva si fanno con Python; anche le espressioni regolari. E Python ha un grosso vantaggio: la sintassi è molto più amichevole, sembra il Basic di una volta. E anche la storia dell’indentazione (non è che devo dire rientri, vero?) ho visto che è facile da digerire. E Python è molto usato, ci sono librerie (OK, non si chiamano così ma sono quello che una volta si sarebbero chiamate librerie). Metti in conto che non devi compilare e anzi puoi modificare al volo lo script e Python vince alla grande, ad esclusione dei progetti più impegnativi dove si usa Java o il C++. A proposito non so se è una cosa locale ma il C++ ha fama di essere ostico e impegnativo; io trovo tale anche Java, ma chissà 😳

Octave (Matlab)
Conor dice che Python “even expanding at Matlab’s expense“. Però, per quel che ne so, Matlab è usato parecchio per calcoli di matrici e roba simile; e ha gli stessi vantaggi di scripting di Python. Resta da verificare se serva la versione proprietaria o sia sufficiente Octave, FOSS.

awk
Ci sono però dei casi in cui forse Python ha dei rivali. Per esempio gli script di awk di cui ho parlato nei post precedenti. Funzionano, anzi cresceranno in futuro. Anche se c’è il problema di Windows.

OK, a proposito per domani devo vedere se usare awk (o Perl) o Python. Scelta difficile io sono pythonista convinto ma vengo da sh, sed, awk e Perl.
E una delle cose di cui vado orgoglioso tra quelle recenti è fatta con Gnuplot 🙄

Ancora script

geekUn post piccolo piccolo ma c’è una cosa che devo proprio dire: è una grande soddisfazione quando fai qualcosa che viene usata e apprezzata. Specie se funziona al primo tentativo, anche senza la mia assistenza 😛
Sto parlando degli script che ho raccontato qui e qui.
E la novità è che si vuole continuare su questa strada. Come ogni cosa nuova (sì ho convertito un windowsiano!) ci vuole un po’ di tempo per impratichirsi. Anche per chi vuol fare tutto da solo un paio di suggerimenti.

Con Linux (e Unix prima) ci sono tantissimi tools, fanno quasi tutto, per esempio…

Userò questo file di dati (t-scr) negli esempi che seguiranno:

uno
uno!!due
uno!!due!!quattro
uno!!d u e
1!!2!!3!!4!!5!!6!!7!!8!!9!!10

cat

Il comando cat (catenate) scrive su terminale il/i file elencati. Come sempre si possono usare le pipe, roba già vista.

tac

tac fa la stessa cosa di cat ma invertendo le righe, comincia dall’ultima e risale fino alla prima.

rev

Non so se sia utile ma si può fare la stessa cosa per ogni riga con rev (reverse).

OK, adesso arriviamo a awk, linguaggio molto versatile anche se un po’ –come dire– ecco, specie all’inizio 😯
Nota: nei file che seguono non ho inserito la shebang, la riga che consente l’esecuzione dello script con il solo nome; questo perché i codici riportati non sono completi.

BEGIN {
    FS = "!!"
}
{
    printf "%s: %s\n", NF, $0
}

0

I campi (field) di ogni riga sono separati dalla sequenza specificata nella variabile FS (field separator). Deve essere specificata prima di iniziare a processare il file, quindi nella sezione BEGIN.

La variabile NF sta per number of fields. $0 è la riga intera.

BEGIN {
    FS = "!!"
}
{
    if (NF == 2) { printf "%s: %s\n", NF, $0 }
}

1

Ecco come estrarre i soli record con due campi, quelli che c’interessano.

BEGIN { FS = "!!" }
{
    if (NF == 2) { printf "%d: %s!!%s \n", NF, $2, $1 }
}

2

I valori dei singoli campi sono contenuti nelle variabili $1, $2, … Questo è decisamente meglio che operare con rev 😛

Notare inoltre che la composizione delle righe in awk è simile a quella del C.

BEGIN { FS = "!!" }
{
    if (NF == 2) {
        printf "%d: %s!!%s \n", NF, $2, $1
        trovati++
    }
}
END { printf "trovati %d records di %d\n", trovati, NR }

3

NR è la variabile automatica che contiene il numero di record letti, cioè le righe del file nel nostro caso; ho usato la variabile trovati per memorizzare il numero di record utili; viene creata automaticamente appena viene utilizzata.

Il file dei dati può essere ordinato con sort. Il comando è, al solito, molto versatile, usa man sort per vedere le opzioni che servono.

geek tns

Per oggi basta così. Resta da verificare se awk può essere conveniente anche nel caso di elaborazioni molto più articolate sui campi. Personalmente io lo trovo utilissimo per cose non troppo complesse, poi passo a Python. C’è però chi produce script lunghi (e illeggibili); questione di gusti. Chissà se sono stato utile? 🙄