bash, grep, sed, awk…: dinosauri o classici ?
Se siete degli utenti Windows questi comandi per voi risulteranno sconosciuti, ma sono il pane quotidiano degli amministratori UNIX/Linux.
“Bash” è in realtà la “linea di comando”, attraverso la quale è possibile digitare manualmente dei comandi ( in nome tecnico la “shell” ), proprio come il “Dos Prompt” di Windows. Per analogia, uno script bash è analogo ad un “batch file” di Windows.
“Grep” è invece un’utility che non esiste in Windows, ed è in sostanza un tool di ricerca all’interno di file di testo. Per esempio ‘grep chopin compositori.txt’ ritornerà le righe del file compositori.txt che contengono il nome ‘chopin’.
“Sed” ( stream editor ) è un tool che prendendo in entrata un file effettua una serie di sostituzioni. Per esempio il comando sed s/chopin/mgutman/ compositori.txt sostituisce dentro il file compositori.txt il nome chopin con il nome mgutman.
Infine, “Awk” è lo strumento più complesso della famiglia, è anch’esso uno strumento di trasformazione come sed ma gestisce anche le colonne oltre alla righe ( per farla semplice )
Ho deciso di mettere alla prova questi strumenti sul mio Mac ( che è Unix ) creando uno script per la il download delle playlist youtube di liszt73 ( che ringrazio ancora )
Ho impiegato diverse ore per arrivare ad un qualcosa di funzionante, anche perchè alle mie prime armi. Ecco lo script:
#!/bin/bash function GetVideo { videoUrl="http://it.youtube.com/watch?v=$1" echo "url is:" $videoUrl # scarica il sorgente della pagina, rimuovendo i caratteri newline \n HTML=".source$1.html" wget -q -o"$1".wget.log -O - $videoUrl | tr -d "\n" >$HTML # estrai da un div il titolo del video title=$(egrep -o '<div id="watch-vid-title" class="title">.<h1\ >[^<]*</h1>'<$HTML | sed 's/<div id="watch-vid-title" class="title">.<h1\ >\([^<]*\)<\/h1>/\1/' \ | tr ":" "-" | tr "/" "-") echo "title is" $title mv "$1".wget.log "$title".source.log # estrai dal codice javascript l'hash ( variabile "t") hash=$(egrep -o '\"t\": \"[-_a-zA-Z0-9]*\"' $HTML | egrep -o '([-_a-zA-Z0-9]{2,})') echo "hash is:" $hash # dall'url estrai l'ID del video video_id=$(echo $videoUrl | egrep -o 'v=[^\&]+' | egrep -o '[^=]{2,}') echo "video_id is:" $video_id # calcola l'URL per scaricare il video in formato MP4 mp4url="http://www.youtube.com/get_video?fmt=18&video_id=$video_id&t=$hash" echo "mp4url is:" $mp4url # ... e in formato Flash flvurl="http://www.youtube.com/get_video?video_id=$video_id&t=$hash" echo "flvurl is:" $flvurl # scaricalo in background wget -o"$title".log -O"$title".mp4 $mp4url || wget -o"$title".flv.log -O"$title".flv $flvurl & # pulizia rm $HTML } # invocare con yt_pl.sh playlistID( e.g. 6E6D77B78DCE77CA ) pagenr out=".pl$1-$2.html" plurl="http://it.youtube.com/view_play_list?p=$1&page=$2" dList=".dl$1-$2" info="$1page$2info.txt" # salva il sorgente della pagina playlist wget -O$out $plurl # cerca nel sorgente i link ai video cat $out | grep -o "<a href=\"/watch?v=[-_0-9a-zA-Z]*&feature=PlayList&p=[-_0-9a-zA-Z]*&index=[0-9]" | sort -u \ | egrep -o 'v=[-_0-9a-zA-Z]+&?' \ | sed -E 's/v=([-_0-9a-zA-Z]+)&?/\1/' >$dList echo "Elenco ID" >> $info cat $dList >> $info echo "Sorgente HTML" >>$info cat $out >>$info # per ogni ID invoca la funzione GetVideoMP4 for i in $(cat $dList) do GetVideo $i done #pulizia rm $out rm $dList
Una volta salvato come ytpl.sh, per utilizzarlo dovete lanciare un comando del tipo:
./ytpl.sh <playlistID> <numero totale di pagine>
Esempio
./ytpl.sh CA9F80F64D7C2400 2
In pratica lo script usa wget per scaricare i sorgenti delle pagine di youtube, ricavare alcuni parametri e generare gli URL di scaricamento, affidandoli ancora a wget. Alcune cose divertenti che si possono fare sono:
- mettere in “piping” i comandi, ovvero l’output di un comando è reindirizzato all’altro tramite il carattere “|” . Questa possibilità da luogo ad un codice molto compatto.
- mettere in una variabile i risultati di un comando tramite variabile=$(comando)
- redirezionare l’output di un comando su un file o sullo schermo, tramite il carattere “>”
- prevedere un comando di fallback nel caso che un comando fallisca. Nel mio caso, se fallisce il download dell’mp4 ( non sempre i video sono disponibili in mp4 ) , parte un download del flash. La sintassi è “comando1 || comando2″
Gli ostacoli più grandi che ho trovato sono stati:
- Generare le corrette espressioni regolari ( per esempio lo standard grep non accetta “+” come quantificatore per 1 o più caratteri. E’ necessario usare egrep o l’opzione “-E” )
- Capire come funzionano le sostituzioni della shell, quando usare le virgolette o meno, etc. etc.
- Capire che senza togliere le newline ‘\n’ nei sorgenti HTML non avrei mai potuto usare grep o sed perchè sono utility che gestiscono la singola linea ( molti tag HTML sono su diverse linee ). Per non dovere imparare Awk
, ho deciso di togliere le newline con il comando tr.
Chiaramente senza Google non avrei potuto fare niente, e in particolare questo tutorial è davvero eccezionale.
In conclusione direi che per questo tipo di problemi i tool si sono rivelati un’ottimo strumento, non credo che sarei mai riuscito a fare qualcosa del genere in C o VB, etc… D’altro canto è chiaro che non mi metterei a fare un gestionale con questi tool…