Petit serveur web vite fait avec bash

Vous est-il déjà arrivé de vouloir partager quelques fichiers avec quelqu’un ou avec un autre appareil (un smartphone par exemple…) et d’être ennuyé parce que les solutions simples (clé USB, partage réseau…) ne sont à ce moment-là pas disponibles ? N’aurait-il pas été bienvenu d’avoir accès à un serveur web instantané permettant de partager le répertoire courant ?

Si ces fichiers sont sur une machine de type Unix ou Linux, disposant d’un shell bash et de la commande socat, alors un simple script de quelques lignes peut faire l’affaire !

This article has been translated to English.

Dans sa forme la plus simple, si vous êtes prêts à taper l’URL à télécharger à la main, le script minimal suivant peut suffire:

#!/bin/bash
dec() { eval printf %s \$\'$(sed 's#'\''#%27#g;s#\\#\\\\#g;s#%#\\x#g')\'; }

if [ "$1" != '-' ]; then
  self="$(cd "$(dirname "$0")" &>/dev/null && pwd)/$(basename "$0")"
  cd "${1:-.}" && exec socat TCP-LISTEN:${2:-8080},reuseaddr,fork exec:"$self -"
fi

request=".$(sed -n '/^GET /{s/^GET \(.*\) HTTP.*/\1/p;q}' | dec)"
printf 'HTTP/1.0 200 OK\r\nContent-Type: application/octet-stream\r\nContent-Length: %d\r\n\r\n' "$(stat -c %s "$request")"
cat "$request"

Ce script se lance en donnant optionnellement en paramètre le chemin à partager (chemin courant par défaut) et le port (8080 par défaut).

Par exemple, vous pourriez lancer ce script sans aucun paramètre depuis votre répertoire personnel, puis taper l’adresse suivante dans le navigateur : http://votre_pc:8080/.bash_profile, pour récupérer le fichier ~/.bash_profile depuis une autre machine.

Personnellement, puisque je ne vais que rarement saisir ce script de mémoire, mais plutôt l’avoir avec moi pour quand j’en ai besoin, je préfère la version plus complète, qui me permet de naviguer dans l’arborescence plutôt que de saisir les adresses à la main :

#!/bin/bash
dec() { eval printf %s \$\'$(sed 's#'\''#%27#g;s#\\#\\\\#g;s#%#\\x#g')\'; }
enc() { sed 's#&#\&amp;#g;s#<#\&lt;#g;s#>#\&gt;#g;s#\"#\&quot;#g' <<<"$1"; }

if [ "$1" != '-' ]; then
  self="$(cd "$(dirname "$0")" &>/dev/null && pwd)/$(basename "$0")"
  cd "${1:-.}" && exec socat TCP-LISTEN:${2:-8080},reuseaddr,fork exec:"$self -" || exit 1
fi

request=".$(sed -n '/^GET /{s/^GET \(.*\) HTTP.*/\1/p;q}' | dec)"
order=$(grep -o '\?[0-9][nd]$' <<<"$request")
order=${order:1}
request="${request%\?[0-9]?}"
if [ ! -e "$request" ]; then
  status='404 Not Found'
elif [ ! -r "$request" ]; then
  status='403 Forbidden'
elif [ -f "$request" ]; then
  status='200 OK'
  type=application/octet-stream
  size=$(stat -c %s "$request")
elif [ -d "$request" ]; then
  status='200 OK'
  type=text/html
  output=$(cat <<-END
    <!doctype html>
    <html><head>
     <meta charset="utf-8">
     <title>$(enc "${request:1}")</title>
    </head><body><table><thead>
     <th scope=col><a href="?1d">Type</a></th><th scope=col><a href="?2d">Mode</a></th>
     <th scope=col><a href="?3d">User</a></th><th scope=col><a href="?4d">Group</a></th>
     <th scope=col><a href="?5n">Bytes</a></th><th scope=col><a href="?6d">Last modification</a></th>
     <th scope=col><a href="?7d">Name</a></th>
    </thead><tbody>
    $(
      find "$PWD/${request:2}" -maxdepth 1 -printf '%Y\t%M\t%u\t%g\t%s\t%T+\t%P\n' \
      | sort -t$'\t' -k${order:-7d} | while IFS=$'\t' read t m u g s d n; do
        [ -n "$n" ] && l="${request%/}/$n" || { l="${request%/?*}/"; m=; u=; g=; s=; d=; }
        printf ' <tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td><a href="%s">%s</a></td></tr>\n' \
          "$t" "$m" "$(enc "$u")" "$(enc "$g")" "$s" "$d" "$(enc "${l:1}${order:+?$order}")" "$(enc "${n:-..}")"
      done
    )
    </tbody></table></body></html>
    END
  )
else
  status='403 Forbidden'
fi
type=${type:-text/plain}
[ -z "$size" ] && output=${output:-$status ${request:1}}
size=${size:-${#output}}
printf 'HTTP/1.0 %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s' "$status" "$type" "$size" "$output"
[ -z "$output" ] && cat "$request"

Ce script s’exécute de la même manière que le précédent. Il permet en revanche de voir le contenu d’un répertoire et de se déplacer dans l’arborescence, et surtout de télécharger des fichiers simplement en cliquant dessus. Ce script permet en outre de trier l’affichage selon le critère de son choix (nom, taille…). Enfin, il gère les cas d’erreur (accès interdit, fichier non trouvé).

Le style d’écriture est un peu moche (effets de bord, pas d’explications…). J’ai voulu faire (très) court. Si vous avez des questions sur le fonctionnement, écrivez-les dans les commentaires !

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs

URL de rétrolien : http://yalis.fr/cms/index.php/trackback/56

Fil des commentaires de ce billet