Giochi e divertimento

Gioco della vita di Conway


I parametri x e y sono la dimensione del “campo di gioco”; la geometria utilizzata è quella toroidale.
Le regole impostate sono:
– una casalle “morta” diventa viva se ha esattamente 3 caselle vive a fianco;
– una casella “viva” continua a vivere se ha 2 o 3 caselle vive a fianco;
– negli altri casi (meno di 2 caselle o più 3 caselle vive a fianco) una casella viva muore.

awk 'BEGIN{
x=64;y=32;
srand();
printf "\x1B[2J";
for(i=0;i<y;i++)for(j=0;j<x;j++)b[j,i]=int(rand()+.5);
while(1){
printf "\x1B[1;1H";
for(i=0;i<y;i++){for(j=0;j<x;j++){printf "%s",b[j,i]?".":" ";a[j,i]=b[j,i]}print;}
for(i=0;i<y;i++){for(j=0;j<x;j++) {
L=a[(j-1)%x,(i-1)%y]+a[(j-1)%x,i]+a[(j-1)%x,(i+1)%y]+a[j,(i-1)%y]+a[j,(i+1)%y]+a[(j+1)%x,(i-1)%y]+a[(j+1)%x,i]+a[(j+1)%x,(i+1)%y];
b[j,i]=((a[j,i]==0 && L==3)||(a[j,i]==1 &&(L==2||L==3)))?1:0;
}}}}'

Labirinto infinito

L’output di questo codice a molti potrebbe risultare familiare:

awk 'BEGIN{while(1)printf "%s",int(rand()+.5)?"/":"\\";}'

La nostra inutilità di sistema preferita

$ cowsay moo
 _____
< moo >
 -----
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

Utility

Cambia il valore in un file di configurazione

L’assegnazione di una variabile in un file di configurazione viene modificato al volo:

cat test.txt
VAR1=TEST
VAR2=1
VAR3=VALUE

VAR=VAR2
VAL=1
sed  -i "s/^${VAR}=.*/${VAR}=${VAL}/" test.txt

cat test.txt
VAR1=TEST
VAR2=1
VAR3=VALUE

Converte una elenco in una lista con separatore

È possibile trasformare una lista di elementi in una serie di elementi separati da “,” (o altro separatore), racchiudendo eventualmente gli elementi tra apici

$ cat lista_testo.txt
pippo
pluto
paperino

$ awk '{if(NR>1) printf ",";printf "%c%s%c",39,$0,39;}END{print "";}' \
lista_testo.txt
'pippo','pluto','paperino'

Formattazione automatica di una tabella

Incolonnamento di una tabella

$ cat unformatted.txt
pippo pluto paperino
paperino pippo pluto
pippo topolino paperino
paperino topolino pippo
pippo topolino pluto paperino

Formatta automaticamente

$ awk '{for(i=1;i<=NF;i++){M[i]=(length($i)>M[i])?length($i):M[i];}}
  END{printf "awk :{printf \"";for(i=1;M[i]>0;i++){printf "%%-%ss ",M[i]+1} 
      printf "\\n\"";
      for(i=1;M[i]>0;i++){printf ",$%s",i}printf ";}: 
unformatted.txt
\n"
}' 
unformatted.txt
|tr -s ":" "'" | bash
pippo     pluto     paperino
paperino  pippo     pluto
pippo     topolino  paperino
paperino  topolino  pippo
pippo     topolino  pluto     paperino

Valorizzo più variabili dall’output di un comando

$ read A B C < <(echo 1 2 3)
$ echo $A - $B - $C
1 - 2 - 3

Codice ascii di un carattere

Ricava il codice ascii di un carattere

$ X=A
$ ASC=`printf "%d\n" "'$X"`
$ echo $ASC
65

Carattere corrispondente ad un codice ascii

Visualizza il corrispondente carattere

$C=65
$awk -v c=$C 'BEGIN{printf "%c\n",c;}'

Caratteri speciali nei nomi di file

Cancellare un file che si chiama \303\271
Il reale nome del file è dato dai caratteri con codici ascii 303 e 271 in ottale che corrispondono a 195 e 185 in decimale: questa sequenza corrisponde al carattere ù in UTF8

A=`echo "x" | awk '{ printf "%c\n",195;}'`
B=`echo "x" | awk '{ printf "%c\n",185;}'`
rm "$A$B"

Check sul filesystem

Controlla lo spazio libero e in uso nel filesystem corrente (in kbyte)

$ df -k .

In particolare visualizza lo spazio occupato da una cartella

$ du -ks nomecartella

Testi colorati

Settate le variabili d’ambiente:

export ESC="\033"
export F0="${ESC}[0;30m"
export F1="${ESC}[0;31m"
export F2="${ESC}[0;32m"
export F3="${ESC}[0;33m"
export F4="${ESC}[0;34m"
export F5="${ESC}[0;35m"
export F6="${ESC}[0;36m"
export F7="${ESC}[0;37m"

E utilizzare nel seguente modo

printf "${F0}Nero${RESET}\n"
printf "${F1}Rosso${RESET}\n"
printf "${F2}Verde${RESET}\n"
printf "${F3}Giallo${RESET}\n"
printf "${F4}Blu${RESET}\n"
printf "${F5}Magenta${RESET}\n"
printf "${F6}Ciano${RESET}\n"
printf "${F7}Bianco${RESET}\n"

Premi un tasto

Premi un tasto per fare quello che vuoi

function press_a_key
{
  STTY=$(stty -g)
  stty -echo -icanon time 0 min 0
  k=""
  while test ! -n "$k"
  do
    read k
  done
  echo $k
  stty $STTY
}

r=""
while test "$r" != "x"
do
  echo "Premi il tasto 'x' per uscire"
  r=$(press_a_key)
  echo "Hai premuto '$r'"
done

 

Tabelle codici ASCII

Lista caratteri ASCII

$ ascii -d
    0 NUL    16 DLE    32      48 0    64 @    80 P    96 `   112 p
    1 SOH    17 DC1    33 !    49 1    65 A    81 Q    97 a   113 q
    2 STX    18 DC2    34 "    50 2    66 B    82 R    98 b   114 r
    3 ETX    19 DC3    35 #    51 3    67 C    83 S    99 c   115 s
    4 EOT    20 DC4    36 $    52 4    68 D    84 T   100 d   116 t
    5 ENQ    21 NAK    37 %    53 5    69 E    85 U   101 e   117 u
    6 ACK    22 SYN    38 &    54 6    70 F    86 V   102 f   118 v
    7 BEL    23 ETB    39 '    55 7    71 G    87 W   103 g   119 w
    8 BS     24 CAN    40 (    56 8    72 H    88 X   104 h   120 x
    9 HT     25 EM     41 )    57 9    73 I    89 Y   105 i   121 y
   10 LF     26 SUB    42 *    58 :    74 J    90 Z   106 j   122 z
   11 VT     27 ESC    43 +    59 ;    75 K    91 [   107 k   123 {
   12 FF     28 FS     44 ,    60 <    76 L    92 \   108 l   124 |
   13 CR     29 GS     45 -    61 =    77 M    93 ]   109 m   125 }
   14 SO     30 RS     46 .    62 >    78 N    94 ^   110 n   126 ~
   15 SI     31 US     47 /    63 ?    79 O    95 _   111 o   127 DEL

Generatore di caratteri

$ awk 'BEGIN{for(i=0;i<16;i++){for(j=2;j<8;j++){ 
printf "%d %c\t",j*16+i,j*16+i;} printf "\n";}}'
32      48 0    64 @    80 P    96 `    112 p
33 !    49 1    65 A    81 Q    97 a    113 q
34 "    50 2    66 B    82 R    98 b    114 r
35 #    51 3    67 C    83 S    99 c    115 s
36 $    52 4    68 D    84 T    100 d   116 t
37 %    53 5    69 E    85 U    101 e   117 u
38 &    54 6    70 F    86 V    102 f   118 v
39 '    55 7    71 G    87 W    103 g   119 w
40 (    56 8    72 H    88 X    104 h   120 x
41 )    57 9    73 I    89 Y    105 i   121 y
42 *    58 :    74 J    90 Z    106 j   122 z
43 +    59 ;    75 K    91 [    107 k   123 {
44 ,    60 <    76 L    92 \    108 l   124 |
45 -    61 =    77 M    93 ]    109 m   125 }
46 .    62 >    78 N    94 ^    110 n   126 ~
47 /    63 ?    79 O    95 _    111 o   127 

Tabella caratteri speciali

124 192 À 123 { 125 } 96 ` 183 · 128
130 200 È 147 148 136 ˆ 174 ® 162 ¢
132 201 É 139 155 170 ª 177 ± 167 §
133 204 Ì 171 « 187 » 180 ´ 149 169 ©
188 ¼ 210 Ò 145 146 186 º 150 137
189 ½ 217 Ù 185 ¹ 126 ~
190 ¾ 178 ² 215 ×
179 ³ 247 ÷
176 °
152 ˜

La tabella ASCII completa



0 NUL 16 DLE 32 48 0 64 @ 80 P 96 ` 112 p 128 144  160 176 ° 192 À 208 Ð 224 à 240 ð
1 SOH 17 DC1 33 ! 49 1 65 A 81 Q 97 a 113 q 129  145 161 ¡ 177 ± 193 Á 209 Ñ 225 á 241 ñ
2 STX 18 DC2 34 50 2 66 B 82 R 98 b 114 r 130 146 162 ¢ 178 ² 194 Â 210 Ò 226 â 242 ò
3 ETX 19 DC3 35 # 51 3 67 C 83 S 99 c 115 s 131 ƒ 147 163 £ 179 ³ 195 Ã 211 Ó 227 ã 243 ó
4 EOT 20 DC4 36 $ 52 4 68 D 84 T 100 d 116 t 132 148 164 ¤ 180 ´ 196 Ä 212 Ô 228 ä 244 ô
5 ENQ 21 NAK 37 % 53 5 69 E 85 U 101 e 117 u 133 149 165 ¥ 181 µ 197 Å 213 Õ 229 å 245 õ
6 ACK 22 SYN 38 & 54 6 70 F 86 V 102 f 118 v 134 150 166 ¦ 182 198 Æ 214 Ö 230 æ 246 ö
7 BEL 23 ETB 39 55 7 71 G 87 W 103 g 119 w 135 151 167 § 183 · 199 Ç 215 × 231 ç 247 ÷
8 BS 24 CAN 40 ( 56 8 72 H 88 X 104 h 120 x 136 ˆ 152 ˜ 168 ¨ 184 ¸ 200 È 216 Ø 232 è 248 ø
9 HT 25 EM 41 ) 57 9 73 I 89 Y 105 i 121 y 137 153 169 © 185 ¹ 201 É 217 Ù 233 é 249 ù
10 LF 26 SUB 42 * 58 : 74 J 90 Z 106 j 122 z 138 Š 154 š 170 ª 186 º 202 Ê 218 Ú 234 ê 250 ú
11 VT 27 ESC 43 + 59 ; 75 K 91 [ 107 k 123 { 139 155 171 « 187 » 203 Ë 219 Û 235 ë 251 û
12 FF 28 FS 44 , 60 < 76 L 92 \ 108 l 124 140 Œ 156 œ 172 ¬ 188 ¼ 204 Ì 220 Ü 236 ì 252 ü
13 CR 29 GS 45 61 = 77 M 93 ] 109 m 125 } 141  157  173 ­ 189 ½ 205 Í 221 Ý 237 í 253 ý
14 SO 30 RS 46 . 62 > 78 N 94 ^ 110 n 126 ~ 142 Ž 158 ž 174 ® 190 ¾ 206 Î 222 Þ 238 î 254 þ
15 SI 31 US 47 / 63 ? 79 O 95 _ 111 o 127 DEL 143  159 Ÿ 175 ¯ 191 ¿ 207 Ï 223 ß 239 ï 255 ÿ

Schedulazione di un comando

Schedulazione con crontab

Il comando crontab permette di schedulare l’esecuzione di un comando, lanciando:

crontab -l

viene visualizzato l’elenco dei comandi schedulati. Per editare le schedulazioni assicurarsi in primo luogo che la variabile d’ambiente EDITOR sia settata con l’editor preferito:

export EDITOR=vi

e quindi eseguire:

crontab -e

Il formato della stringa da editare è:

.---------------- minuto (0 - 59) 
|  .------------- ora (0 - 23)
|  |  .---------- giorno del mese (1 - 31)
|  |  |  .------- mese (1 - 12)  
|  |  |  |  .---- giorno della settimana (0 - 6) (domenica=0 o 7)
|  |  |  |  |     

*  *  *  *  *  comando da eseguire


È possibile usare anche la wildcard “*” o indicare un intervallo o una lista. Il simbolo “*” sta ad indicare un qualunque valore, il simbolo “-” viene usato per definire un intervallo, mentre “,” permette di indicare una lista.

Esempi:

Il comando viene eseguito tutti i giorni dal lunedì al venerdì dalle 8 alle 17.30 ogni 30 minuti

0,30 8-17 * * 1-5 /bin/ls

Il comando viene eseguito il sabato e la domenica allo scoccare di ogni ora

0 * * * 0,6 /bin/ls

Il comando viene eseguito il primo di ogni mese all’una di notte

0 1 1 * * /bin/ls

Schedulazione a settimane alterne

Lancio alle 6 del lunedì mattina a settimane alterne

00 06 * * 1 if test -f /path/di/appoggio/cron.stop; \
then rm /path/di/appoggio/cron.stop; \
else modulo_da_lanciare; \
touch /path/di/appoggio/cron.stop; \
fi

o anche

00 06 * * 1 test -f /path/di/appoggio/cron.stop && \
(rm /path/di/appoggio/cron.stop) || \
(modulo_da_lanciare; touch /path/di/appoggio/cron.stop)

Schedulazione 2 volte al mese

Lancio alle 6 del lunedì mattina il 1° e il 15 di ogni mese

00 06 1,15 * * modulo_da_lanciare

Ricerca nel filesystem

Find non ricorsivo

Cerca nella directory corrente, ma non nelle sottodirectory

find . ! -name . -prune

Ricerca i file in base al contenuto

Ricerca i file che contengono una particolare stringa, la ricerca in questo caso è anche ricorsiva:

find . -exec grep -l ${STRINGA} {} \;

o anche

find . | sed 's/.*/"&"/' | xargs grep -l ${STRINGA}

oppure

find . -print0 | xargs -0 grep -l ${STRINGA}

Ricerca in un intervallo di date

Cerca i file modificati da più di 365 gg e da meno di 730

find . -mtime -730 -mtime +365 -exec ls -lrt {} \;

Cerca i file troppo grandi

Utile per cercare i file più grandi di 300kb, ordinati per dimensione:

find . -type f -size +300k -print0 | xargs -r0 du -a| sort -n

Ricerca e estrazione da file

Una riga specifica in un file

linenumber=8
sed -n "${linenumber}p" filename

Tutte le righe in un range

sed -n '100,200 p' filename

Dall’ n-esima riga in avanti

Visualizza tutte le righe dalla terza in poi

awk '{if(NR>=3) print;}' file.dat

Tutte le righe tra due tag

Visualizza tutte le righe tra i tag INIZIO e FINE

sed -n '/INIZIO/,/FINE/p' file.dat

Cerca una stringa in un file e visualizza anche la riga precedente e successiva

Sostituire il parametro nome_del_file con il nome del file in entrambe le occorrenze e la stringa_cercata

awk '/stringa_cercata/{
printf "awk %cNR>=%d && NR<=%d%c nome_del_file\n",39,NR-1,NR+1,39; 
}' nome_del_file | sh

Occorrenze di un carattere in un file

Conta le occorrenze del carattere $CHAR nel file $FILE

cat $FILE | sed "s/[^${CHR}]//g"| tr -d "\n" | wc -c

Visualizza informazioni su un file

stat text.txt

Output:

  File: "text.txt"
  Dim.: 35              Blocchi: 1          Blocco di IO: 65536  file regolare
Device: c879786ch/3363403884d   Inode: 17451448556069705  Coll.: 1
Accesso: (0755/-rwxr-xr-x)  Uid: ( 1008/xxx)   Gid: (  513/    None)
Accesso  : 2012-05-03 13:09:12.137612800 +0200
Modifica : 2012-03-22 14:58:25.663665600 +0100
Cambio   : 2012-03-22 14:58:25.904011200 +0100
Creazione: 2012-03-22 14:58:25.423320000 +0100

Redirezione dell’output

Output di un comando verso un file

Il risultato di un comando viene inviato verso un file: se il file già esiste viene sovrascritto, il suo contenuto originale è perso per sempre

ls *.txt > lista_file_testo.txt

Posso accodare al file: al file originale vengono aggiunte nuove righe

ls *.txt >> lista_file_testo.txt

Messaggi di errore di un comando su un file

I messaggi di errore sono diretti su un canale diverso dallo standard, per cui:

echo "Elenco dei file di testo" > elenco.log
ls *.txt 2>> elenco.log

cat elenco.log
Elenco dei file di testo
ls: impossibile accedere a *.txt: No such file or directory

Tutto l’output sul file di log

Per redirigere sia il canale di standard output che di error verso uno stesso file di log:

echo "Elenco dei file di testo" > elenco.log
ls *.txt >> elenco.log 2>&1

Output di un comando verso un nuovo processo

Per esempio filtro dall’output del comando ls solo ciò che contiene la stringa “lista”

ls *.txt | grep lista

Può essere utile per implementare un ciclo su ciascun file

ls *.txt | while read FILE
do
   echo "$FILE è un file di testo"
done

Chiudere il canale di output

In uno script, l’esecuzione della seguente riga produce la ridirezione dell’output standard di tutti i successivi comandi, allo stesso modo si può redirigere lo standard error:

exec 1>/dev/null

Lo standard output e error possono essere chiusi con

exec 1>&-
exec 2>&-

Operazioni numeriche

Semplici operazioni aritmetiche

Per la somma, differenza e rapporto:

a=`expr $a + 1`

per il prodotto è meglio usare la seguente sintassi

a=`expr $a \* 1`

In ksh e bash è possibile inoltre scrivere:

a=$(($a + 1))

Calcoli più complessi

Il comando bc permette di eseguire dei calcoli più complessi, definire funzioni e logiche:

a=`echo "$a + 3" | bc`

In questo modo si possono ottenere dei risultati con decimali:

a=`echo "scale = 2; 781/3" | bc`

Controllo se una variabile è numerica

if [[ $N =~ ^[0-9]+$ ]] 
then echo "$N numerico"
else echo "$N non numerico"
fi

Gestione date

Creazione di un timestamp

È utile l’utilizzo del timestamp, ovvero la formattazione della data in una modalità particolarmente sintetica e ordinata per cifre più significative, cioè YYYYMMDDHHMMSS

TIMESTAMP=`date '+%Y%m%d%H%M%S'`

Epoch della data attuale

Ovvero il numero di secondi trascorsi dalla mezzanotte del 1° gennaio 1970, può essere generato in diversi modi, a seconda della versione del comando date

date +%s

date -u '+%S+%M*60+%H*3600+(%j-1)*86400+(%Y-1970)*31536000+\
((%Y-1969)/4)*86400'|bc

Oppure in perl:

/usr/bin/perl -e 'printf "%d\n", time;'

E anche in millisecondi

/usr/bin/perl -e 'use Time::HiRes qw(time);my $t = time;printf "%d%03d\n",$t,($t-int($t))*1000;'

Calcolo dell’epoch di una data

Esempio di calcolo dell’epoch del “01/01/2012 12.41.12”:

HOUR=12
MIN=41
SEC=11
DAY=01
MONTH=01
YEAR=2012
set -A MONTHS 0 0 31 59 90 120 151 181 212 243 273 304 334 365
echo "b=0;if(${MONTH}>2) if (${YEAR}%4==0) b=1; \
${SEC}+${MIN}*60+${HOUR}*3600+(${MONTHS[${MONTH}]} + ${DAY} + b-1)*86400+\
(${YEAR}-1970)*31536000+((${YEAR}-1969)/4)*86400" | bc

Conversione dell’epoch in data

Esempio di calcolo della data corrispondente all’epoch 1325421671:

echo 1325421671 |\
awk '{
        e=$1;
        split("0,365,730,1096,1461", ydq,",");
        split("0,31,59,90,120,151,181,212,243,273,304,334,365",mml,",");
        tot_m=int(e/60);sec=e-tot_m*60;
        tot_h=int(tot_m/60);min=tot_m-tot_h*60;
        tot_d=int(tot_h/24);hours=tot_h-tot_d*24;
        tot_q=int(tot_d/1461);dqua=tot_d-tot_q*1461;
        base_y=1970 + tot_q * 4;
        for(i=0;i<4;i++){
                if (dqua<ydq[i+1])break;
        }
        year=base_y + i - 1;
        dyear=dqua - ydq[i] + 1;
        leap=(year % 4)?0:1;
        mmlp=0;
        for(i=1;i<=12;i++)
        {
                mmll=(i>=2)?mml[i+1]+leap:mml[i+1];
                if(mmll>=dyear){
                        month=i;
                        day=dyear-mmlp;
                        break;
                }
                mmlp=mmll;
        }
        printf "%02d/%02d/%04d %02d:%02d:%02d\n",day,month,year,hours,min,sec;
}'

Numeri di giorni dall’Inizio del Tempo Unix

DD=02
MM=05
YY=2012
set -A MONTHS 0 0 31 59 90 120 151 181 212 243 273 304 334 365
echo "b=0;
if(${MM}>2) if (${YY}%4==0) b=1;
(${MONTHS[${MM}]} + ${DD} + b-1)+(${YY}-1970)*365+((${YY}-1969)/4)" | bc

e inversamente, un valore calcolato con il comando precedente si trasforma di formato data con

echo 20000 |\
awk '{
        e=$1;
        split("0,365,730,1096,1461", ydq,",");
        split("0,31,59,90,120,151,181,212,243,273,304,334,365",mml,",");
        tot_d=e
        tot_q=int(tot_d/1461);dqua=tot_d-tot_q*1461;
        base_y=1970 + tot_q * 4;
        for(i=0;i<4;i++){
                if (dqua<ydq[i+1])break;
        }
        year=base_y + i - 1;
        dyear=dqua - ydq[i] + 1;
        leap=(year % 4)?0:1;
        mmlp=0;
        for(i=1;i<=12;i++)
        {
                mmll=(i>=2)?mml[i+1]+leap:mml[i+1];
                if(mmll>=dyear){
                        month=i;
                        day=dyear-mmlp;
                        break;
                }
                mmlp=mmll;
        }
        printf "%02d/%02d/%04d\n",day,month,year;
}'

04/10/2024

Calcolo della Pasqua

Questo script implementa l’algoritmo per il calcolo della Pasqua Cristiana secondo il calendario gregoriano. Le quantità 24 e 5 in grassetto nelle formule valgono per gli anni compresi tra il 1900 e il 2099.

echo 2012 | awk '{
	y=$1;
	a=y%19;
	b=y%4;
	c=y%7;
	d=(19*a + 24)%30;
	e=(2*b + 4*c + 6*d + 5)%7;
	if ((d + e) < 10){
		h=3;
		k=(d + e + 22);
	}
	else{
		h=4;
		k=(d + e - 9);
		if (k==26) k=25;
		if (k==25 && d==28 && e==6 && a>10) k=18;
	}
	printf "%02d/%02d/%04d\n",k,h,y;
}'

Calcolo del giorno precedente

daybefore() 
{
   DD=$( echo $1 | cut -c 1-2 )
   MM=$( echo $1 | cut -c 3-4 )
   YY=$( echo $1 | cut -c 5-8 )

   LEAP=0 
   BIS=$(echo "$YY % 4"| bc) 
   test $BIS -eq 0 && LEAP=1 
   set -A MML 31 31 28 31 30 31 30 31 31 30 31 30 31
   MML[2]=$(( LEAP + 28 ))

   ND=$(( DD - 1 ))
   NM=$MM
   NY=$YY
   test $ND -eq 0 && NM=$(( MM - 1 )) 
   test $ND -eq 0 && ND=${MML[NM]} 
   test $NM -eq 0 && NY=$((NY - 1 )) 
   test $NM -eq 0 && NM=12 
   printf "%02d%02d%04d\n" $ND $NM $NY 
}

La funzione daybefore calcola il giorno precedente alla data passata come parametro

daybefore 01032012

Data di ieri:

daybefore $(date +%d%m%Y)

Calcolo del giorno della settimana

Viene restituito 0 per Domenica, 1 per Lunedì ecc.

Per la data odierna:

date +%u


Per una data qualunque:

DATA='02/05/2012'
read DD MM YY < <( echo $DATA | sed 'sx/x xg' )
echo "(6 - $( cal $MM $YY | awk 'NR==3' |wc -w ) + ( $DD % 7 ) ) %7" | bc

Validazione di una data

Sfruttando il comando cal è possibile verificare se una data è valida.

$ myDATE=22/07/2015
$ echo $myDATE |sed 's/\// /g'| xargs cal >/dev/null 2>/dev/null
$ echo $?
0

Nel caso di data non valida restituisce un codice di ritorno diverso da zero.

$ myDATE=33/07/2015
$ echo $myDATE |sed 's/\// /g'| xargs cal >/dev/null 2>/dev/null
$ echo $?
123