[>a2448.html>] [<a2446.html<] [^a2.html^]


Capitolo 351.   Script per la shell

351.1   Script shell apparentemente inefficaci

Come, mai se faccio uno script stupidissimo come questo:

#!/bin/sh
cd /home/aur

e lo avvio, non mi va alla directory /home/aur?

---------

Perché lo script viene eseguito da una shell secondaria (/bin/sh), che termina quando finisce lo script e pertanto perde tutte le impostazioni (compresa PWD, cioè la directory corrente). Per farlo eseguire dalla shell principale (cioè quella che accetta i tuoi comandi) dovresti creare un alias, una funzione oppure fare:

$ source nomescript[Invio]

o:

$ . nomescript[Invio]

in questo caso infatti sarà la shell corrente ad eseguire i comandi e non un processo figlio.

351.2   È possibile eseguire uno script shell nel web?

---------

Certo, dagli l'estensione .cgi, forniscigli i permessi con:

# chmod 755 file.cgi[Invio]

e richiamalo mediante link o fornendo l'indirizzo esatto. Come cgi puoi usare qualunque linguaggio interpretato o compilato che sia in grado di leggere lo standard input, scrivere su standard output e leggere il contenuto delle variabili d'ambiente.

351.3   Contare i caratteri

Esiste un comando o un sistema per contare i caratteri presenti in un file di testo?

---------

$ cat nomefile | wc -l[Invio]

conta le linee;

$ cat nomefile | wc -c[Invio]

conta i caratteri;

$ man wc[Invio]

per dettagli.

351.4   Differenze tra $@ e $# negli script shell

Non ho capito la differenza tra il parametro posizionale $@ e $#. Chi me li spiega?

---------

Scrivi questo file: prova.sh

  #!/bin/bash

  echo $@;
  echo $#;

Ora, dopo avergli dato i permessi di esecuzione, prova a lanciarlo in questo modo:

$ ./prova.sh ciao bla "un esempio"[Invio]

Penso che ti sarà molto chiaro il risultato.

$@ stamperà tutti i parametri passati da linea di comando, mentre $# indicherà la posizione dell'ultimo di essi, nel nostro caso 3.

Aggiungi allo script anche la riga:

echo $3

Come puoi vedere, verrà restituito un esempio.

351.5   Aggiungere numeri di riga ad un file di testo

Come si aggiungono i numeri di riga ad un file di testo? Mi serve di numerare progressivamente tutte le righe di un file.

---------

Lo puoi fare con perl. Crea un file pippo.pl e rendilo eseguibile:

#!/usr/bin/perl 

$nr=0;
while (<STDIN>){
 # Cambia eventualmente %04d con quello che vuoi
 print sprintf("%04d: %s",$nr,$_);
 $nr++;
}
exit;

e poi dai il comando:

$ cat pluto.txt | ./pippo.pl > filenumerato[Invio]

351.6   Scelta di un file a caso in una directory

Mi chiedevo se esiste già o se è possibile realizzare un comando che esegua il seguente compito:

comando percorso_directory_scelta

con un output costituito da un file a caso (senza tutto il percorso) contenuto nella directory_scelta.

---------

$ ls -1 | sed -e $(random -e `ls -1 | wc -l` ; echo $(( RAND=$?+1 )))p --quiet[Invio]

Possiamo sopperire alla mancanza di 'random' con l'equivalente in perl:

$ perl -e '@files=`ls -1`;print "@files[int(rand($#files))]\n";'[Invio]

351.7   Ricerca doppioni di file

Ho la necessità di cercare in una directory e nelle relative discendenti, se ci sono o meno dei file con nomi uguali e che il risultato di questa ricerca venga poi inviato ad un file di testo

---------

------
#!/bin/sh
# $1= directory dove cercare i file.

for $i in `find $1 -name *`; do
        $n= basename $i
        if [ `find $1 -name $n |wc -l` -gt 1]; then
                find $1 -name $n
        fi
done
------

Forse un po' lento ma dovrebbe andare. Per mettere l'output in un file di testo basta redirigere l'output con un '>'.

Oppure:

$ for F in `ls -1 -R percorso_dir1`; do find percorso_dir2 -name $F; done > lista_files_doppi[Invio]

dove per percorso_dir1 si intende il percorso completo della prima directory da confrontare, e per percorso_dir2 il percorso della seconda, analogamente.

351.8   Convertire in minuscolo tutti i file di una directory

Come si può creare uno script che mi rinomini in minuscolo tutti i file di una directory? Oppure esiste già un comando apposito?

---------

$ perl -e '@files = `ls -1`; chop @files; foreach $f(@files){!-e lc($f) && rename ($f, lc($f))}'[Invio]

o con la shell:

$ for FILE in `/bin/ls`; do FILELOWER=`echo $FILE | tr A-Z a-z`; mv -i $FILE $FILELOWER; done[Invio]

351.9   Ordinare le righe di un file di testo al contrario

Ho ricevuto dei file di dati (.txt) in cui gli stessi sono ordinati (con la data ad inizio riga) cronologicamente dal più recente al più vecchio. Mi servono al contrario. In pratica la prima riga deve diventare l'ultima; la seconda la penultima, ecc..

---------

$ cat -n nome_file | sort -r | awk '{$1="";print}' | sed -e s/\ // > nuovo_file[Invio]

oppure:

$ cat nome_file | perl -e '@a=< STDIN >; while(@a){print pop(@a)};' > nuovo_file[Invio]

Non basta il semplice:

$ cat nome_file | sort -r > nome_file.rev[Invio]

che funzionerebbe se il primo campo di ogni riga fosse la data in formato aa/mm/gg. Ma se è invece in formato gg/mm/aa le righe verrebbero ordinate in base al giorno e non in base all'anno.

351.10   Come utilizzare sed per sostituire testo

Vorrei sostituire da una frase una parola con un'altra. Come si fa?

---------

Con il comando sed, eccoti un esempio:

$ echo 'Il miglior sistema operativo: Windows' | sed /Windows/s//Linux/g[Invio]

351.11   Occorrenza di una parola in un file

Come si fa a sapere quante volte compare una parola in un file?

---------

$ grep parola file|wc -l[Invio]

351.12   opzione -f (if) negli script shell

Molto spesso negli script che contengono if compare l'opzione -f. Cosa sta ad indicare?

---------

Credo che tu intenda questo:

#!/bin/bash

if [ -f "$1" ]; then 
        echo "file regolare";
fi

Se il percorso passato come argomento a questo script rappresenta un file regolare, lo script te lo segnala. Per file regolare si intende un qualsiasi file normale, che non sia quindi un dispositivo, un socket, una directory, ecc.

351.13   date: utilizzarlo negli script

In alcuni script shell mi interessa utilizzare la data. Digitando però:

$ date[Invio]

mi vengono restituite informazioni inutili e fastidiose per il mio script. Come posso eliminarle?

---------

Il comando:

$ echo `date +%x`[Invio]

ti restituisce il formato: mm/gg/aa. Utilizzare invece:

$ echo `date +%x` | sed "/\//s///g"[Invio]

ti potrebbe essere più utile in quanto restituisce il formato: mmggaa.

Le virgolette nell'ultimo comando sono necessarie a causa della '\'. Lo stesso risultato lo ottieni con:

$ echo `date +%x | sed 's/\///g'`[Invio]

351.14   Sostituire una stringa in un file di testo

Vorrei che nel file pippo.txt tutte le stringa fossero sostituite da stringa1. Quale è il comando?

---------

Eccolo:

$ sed s/'stringa'/'stringa1'/g pippo.txt > filemodificato.txt[Invio]

351.15   Cerca, trova e copia

Come faccio a cercare tutti i file di un certo tipo (*.png) e a copiarli in una directory?

---------

Se la directory di destinazione è /root/pngfile:

# find / -name "*.png" -exec cp -p {} /root/pngfile \;[Invio]

351.16   Cambiare le estensioni dei file

Come si può creare uno script che mi permetta di cambiare solo le estensioni di gruppi di file?

---------

#!/usr/bin/perl

$old_ext = @ARGV[0] || usage();
$new_ext = @ARGV[1] || usage();

print "$old_ext --> $new_ext\n\n";

@files = `ls -1`;
chop @files;

foreach $f(@files) {
        $f =~ /(^.*)\.$old_ext/ && rename ($f, "$1.$new_ext");
}

sub usage {
        print <<"END";

        Usage: ./script.pl old_ext new_ext

        Example: ./script.pl tar.gz zip

END
        exit(1);
}

Dai i permessi di esecuzione e invocalo nella directory in cui devi rinominare i files.

Siccome lo script per semplicità non effettua alcun controllo sulla nuova estensione richiesta dall'utente, è meglio usarlo con prudenza.

351.17   Modificare l'output di pppstats

Dando il comando:

$ pppstats -r -v ppp0[Invio]

ottengo qualcosa tipo:

      IN   PACK VJCOMP VJTOSS NON-VJ  RATIO  UBYTE  |      OUT   PACK VJCOMP VJSRCH VJMISS  RATIO  UBYTE
   48089    366     16      0     99   1.00      0  |   308840    385     54    212      8   1.00      0

vorrei togliere la prima riga e avere la seconda tale e quale, ma tra un numero e l'altro non ci devono essere gli spazi ma dei caratteri di tabulazione.

---------

Ecco i comandi necessari:

# pppstats -r -v ppp0 | grep -v VJCOMP | perl -e '$i=<STDIN>;$i=~s/\s+/\t/g;print $i;' | cut -f 2-[Invio]

# perl -e '@a=split(/\s+/, `pppstats -r -v ppp0|grep -v VJCOMP`);shift(a);foreach $b(@a) {print "$b\t"};'[Invio]

# pppstats -r -v ppp0 | grep -v OUT | sed -e 's/\ \+/ /g' | cut -f 2-[Invio]

(N.B.: nella parte con il sed il tab è scritto con [Ctrl+v] e [tab])

351.18   Sostituire caratteri in un file di testo

Esiste un modo per rimpiazzare da riga di comando alcuni caratteri contenuti in un file di testo con altri caratteri? In sostanza, mi serve un modo per convertire tutti i «<» contenuti in un file in «:»

---------

$ sed -e s/\</:/g nomefile > nuovofile[Invio]

351.19   Ricerca indirizzi e-mail in un file html

Come faccio a ricavare da una pagina html tutti gli indirizzi e-mail in essa contenuti? Mi servirebbe per automatizzare un lavoro che adesso sto facendo a mano. Ho fatto qualche prova con il grep ma senza molti risultati.

---------

Se hai installato l'interprete perl, puoi risolvere con una riga di comando di questo tipo che ti garantisce una certa precisione:

$ cat file.html | perl -e 'while ($l=<STDIN>) {$l=~/mailto:\s*([\w-_\.]+\@[\w-_\.]+\.[\w-_\.]+)/ && print "$1\n";}'[Invio]

Se gli indirizzi e-mail da estrapolare non compaiono sotto forma di tag HTML, puoi togliere la stringa relativa al mailto e lo spazio nella regexp.

351.20   Rinominazione progressiva file - pt. 1

Quale potrebbe essere lo script che prende tutti i file di una directory e li rinomina progressivamente (1.zip 2.zip ecc.) ?

---------

#!/usr/bin/perl

$my_dir = @ARGV[0] || die "Non è stata specificata la directory\n";
@files = `ls $my_dir`;
chop @files;

foreach $f(@files) {
        $count++;
        $f =~ /\.(.*)/;
        rename("$my_dir/$f", "$my_dir/$count.$1");
}

oppure:

#!/bin/bash
#
_N=1
for _F in 'ls'
do
    mv $_F ${_N}.zip
    _N='expr $_N + 1'
done

se invece vuoi aggiungere solamente il suffisso .zip allora:

#!/bin/bash
#
for _F in 'ls'
do
    mv $_F  ${_F}.zip
done

351.21   Rinominazione progressiva file - pt. 2

Come si fa in modo semplice quello che in Dos si faceva per esempio con:

ren abc*.txt abc*.asc ?

---------

Si può fare così:

$ for i in abc*.txt; do mv $i ${i%.txt}.asc; done[Invio]

Se i file sono tantissimi ed eccedi la lunghezza massima di una riga di comando, puoi sempre fare:

$ find . -name "abc*.txt" | while read i; do mv $i ${i%.txt}; done[Invio]

ma attenzione che find è ricorsivo.

Le forme:

${nomevariabile%%pattern}

${nomevariabile%pattern}

${nomevariabile##pattern}

${nomevariabile#pattern}

vengono espanse dalla shell nel contenuto della variabile tranne la parte all'inizio (`#') o alla fine (`%') che soddisfa il pattern. Il pattern è al solito un pattern di shell (*,?,[a-z]), e non un'espressione regolare.

Con `##' e `#' il pattern viene eventualmente tolto dall'inizio, mentre con `%%' e `%' viene eventualmente tolto dalla fine. La differenza tra `##' e `#' (o `%%' e `%') sta nel fatto che il primo rimuove il pattern più lungo possibile, mentre il secondo rimuove il più corto possibile.

351.22   Aggiungere una stringa alla fine di ogni riga di un file testo

Come si fa ad aggiungere una stessa parola alla fine di ogni riga di testo?

---------

$ cat file-name | sed "s/$/xxx/"[Invio]

dove xxx è la stringa da aggiungere.

351.23   Decomprimere diversi file in directory sequenziali

Ho le seguenti necessità:

  1. creare 2000 directory;

  2. inserire in ognuna di esse un file .zip;

  3. entrare nelle directory una ad una e lanciare unzip nomefile di volta in volta.

---------

La shell è lì apposta per questo genere di lavori basta dirgli cosa fare più o meno nei termini in cui l'hai descritto a parole, ossia:

    #!/bin/sh

    cd /zipdir

    for f in *
    do
        mkdir /destinazione/$f
        unzip $f -d /destinazione/$f
    done

Lo script assume che tutti gli zip siano in /zipdir e che la directory /destinazione esista già.

Per il resto non fa altro che elaborare i file uno per uno, creare una directory con lo stesso nome del file .zip, e decomprimercelo dentro.

LDR --- Copyright © 1999-2000 Gaetano Paolone --  bigpaul @ pluto.linux.it

[>a2448.html>] [<a2446.html<] [^a2.html^]