Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

Nouvelle section Wiki pour poster des messages au sujet de la documentation, voir si besoin comment l'utiliser.

À propos de l'équipe du forum.

#1 Le 03/01/2012, à 01:10

nordinatueur

[script] ToDoList en bash...

Suite à un de mes vieux billets sur un topic très sympa ( ./viewtopic.php?pid=3758609#p3758609 ) j'ai amélioré la script de todoList...

Voici la version avec YAD (bien plus puissant que zenity) et Zenity :

#!/bin/bash 
# Créé par Nordinatueur
# 
rouge='\e[31m'; vert='\e[32m'; jaune='\e[33m'; bleu='\e[34m'; rose='\e[35m'; cyan='\e[36m'; gris='\e[37m'; NC='\e[0m';

todoDir="${HOME}";
todoList="${todoDir}/.todo.list";
cd "${todoDir}";
#cp "${todoList}" "${todoList}.$(date '+%y.+%j')";

Erreur ()
{
    echo "${1}" >&2;
}

Init ()
{
    if [[ ! -e "${todoList}" ]]; then
        echo "Ligne 1" > "${todoList}";
    else
        Erreur "${todoList} existe...";
    fi;
}
Init;


Usage ()
{
    sed -nr 's/[[:space:]]+(-[^\)]*)\) #/\t\1\t/pg' ${0};
    #sed -rn 's/[[:space:]]+(-[^\)]*)\)#/\t\t/pg' ${0};
    exit;
}

SupprimerLigne ()
{
    i=${1:-$};
    sed -i "${1}d" "${todoList}";
}

AjouterLigne ()
{
    i=$((${1}-1));
    ligne="${2:-Ligne ${1}}";
    if [ ${i} -eq 0 ]; then
        sed -i "1i${2}" "${todoList}";
    else
        sed -i "${i}a${2}" "${todoList}";
    fi;
}

DeplacerLigne ()
{
    ligne=$(sed -n "${1}p" "${todoList}" );
    if [ ${1} -lt ${2} ]; then
        sed -i "${1}d;${2}a${ligne}" "${todoList}";
    elif [ ${1} -gt ${2} ]; then
        sed -i "${1}d;${2}i${ligne}" "${todoList}";
    fi;
}

ModifierLigne ()
{
    i=${1:-1};
    ligne=${2:-Ligne ${i}};
    sed -i "${i}c${ligne}" "${todoList}";
}

EchangerLigne ()
{
    i=${1:-1}; j=${2:-2};
    ligne1=$(sed -n "${i}p" "${todoList}" );
    ligne2=$(sed -n "${j}p" "${todoList}" );
    Erreur "lignes ${i} et ${j}";
    Erreur "lignes ${ligne1} et ${ligne2}";
    ModifierLigne "${i}" "${ligne1}";
    ModifierLigne "${j}" "${ligne2}";
}

if which yad; then
    GUI ()
    {
        while true; do
            choix="ajouter une tâche"\!"supprimer une tâche"\!"deplacer une tâche"\!"modifier une tâche"\!"echanger des tâches"\!"afficher/éditer les tâches";
            action=$( yad --on-top --center --title="Gui ToDoList" --text="" --form --field="":CB "${choix}" ) || break;
            continuer=true;

            maxLigne=$(grep -v '^ *$' "${todoList}" | wc -l );
            listeNombresYad=1;
            for i in $(seq 2 ${maxLigne} ); do
                listeNombresYad=${listeNombresYad}\!${i};
            done;
            listeLignesYad="$( cat -n "${todoList}" | sed -e 's/^[[:space:]]*//g;:boucle;N;$!b boucle;s/\n/\!/g;s/[[:space:]]\+/ /g;s/! /!/g' )";

            case ${action,,} in
            ajouter*)
                ((maxLigne++)); listeNombresYad=${listeNombresYad}\!${maxLigne};
                retourYad=$( yad --on-top --center --title="Ajout" --text="Ajout" --form --field=Ligne:CB "${listeNombresYad}" --field="Tâche :" "" ) || continue;
                ligne=${retourYad%%|*};
                contenu="${retourYad%|}"; contenu="${contenu#*|}";
                echo "${ligne} - ${contenu}" > error.log
                AjouterLigne ${ligne} "${contenu}";
                ;;
            supprimer*)
                retourYad=$( yad --on-top --center --title="Suppression" --text="Suppression" --form --field=Ligne:CB "${listeLignesYad}" ) || continue;
                ligne=${retourYad%%[^0-9]*};
                SupprimerLigne ${ligne};
                ;;
            deplacer*)
                retourYad=$( yad --on-top --center --title="Déplacement" --text="Déplacement" --form --field=de:CB "${listeLignesYad}" --field="à la ligne ":CB "${listeNombresYad}" ) || continue;
                Erreur "retour:${retourYad}";
                debut=${retourYad%%|*}; Ndebut=${debut%%[^0-9]*};
                fin=${retourYad%|}; fin=${fin#*|}; Nfin=${fin%%[^0-9]*};
                Erreur "${Ndebut} et ${Nfin}";
                DeplacerLigne ${Ndebut} ${Nfin};
                ;;
            modifier*)
                retourYad=$( yad --on-top --center --title="Modification" --text="Modification" --form --field=Ligne:CB "${listeLignesYad}" --field="Tâche :" "..." ) || continue;
                ligne=${retourYad%%[^0-9]*};
                contenu="${retourYad%|}"; contenu="${contenu#*|}";
                ModifierLigne ${ligne} "${contenu}";
                ;;
            echanger*)
                notify-send "${0##*/}" "L'action ${action} n'est pas valide.";
                continue;
                retourYad=$( yad --on-top --center --title="Échange" --text="Échange" --form --field=de:CB "${listeLignesYad}" --field=à:CB "${listeLignesYad}" ) || continue;
                debut=${retourYad%%|*}; Ndebut=${debut%%[^0-9]*};
                fin=${retourYad%|}; fin=${fin#*|}; Nfin=${fin%%[^0-9]*};
                Erreur "${Ndebut} et ${Nfin}";
                EchangerLigne ${Ndebut} ${Nfin};
                ;;
            afficher*)
                tmpFile="/tmp/.${0##*/}.tmp";
                >${tmpFile} yad --on-top --center --title="Afficher" --text-info --editable --show-uri --filename="${todoList}" --height="350" --width="300" && cp -f "${tmpFile}" "${todoList}" || continue;
                echo >> "${todoList}";
                ;;
            *)
                notify-send "${0##*/}" "L'action ${action} n'est pas valide.";
                ;;
            esac;
            ${continuer} || break;
        done;
    }
elif which zenity; then
    GUI ()
    {
        while true; do
            choix="ajouter une tâche
supprimer une tâche
deplacer une tâche
modifier une tâche
echanger des tâches
afficher/éditer les tâches
";
            action=$( zenity --list --title="Gui ToDoList" --column="Action" <<<"${choix}" ) || break;
            continuer=true;

            maxLigne=$(grep -v '^ *$' "${todoList}" | wc -l );

            case ${action,,} in
            ajouter*)
                ((maxLigne++));
                ligne=$( { cat -n "${todoList}"; printf "\t${maxLigne} Fin"; } | zenity --title="Ajout" --text="Quelle position ?" --list --column="Ligne ?" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                contenu=$( zenity --title="Ajout" --text="Ajout" --entry --entry-text="Tâche n°${ligne}" ) || continue;
                echo "${ligne} - ${contenu}" > error.log
                AjouterLigne ${ligne} "${contenu}";
                ;;
            supprimer*)
                ligne=$( cat -n "${todoList}" | zenity --title="Supprimer" --text="Quelle ligne ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue ;

                SupprimerLigne ${ligne};
                ;;
            deplacer*)
                debut=$( cat -n "${todoList}" | zenity --title="Déplacer" --text="Quelle ligne ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                fin=$( cat -n "${todoList}" | zenity --title="Déplacer" --text="Vers quelle position ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                Ndebut=${debut%%[^0-9]*};
                Nfin=${fin%%[^0-9]*};
                DeplacerLigne ${Ndebut} ${Nfin};
                ;;
            modifier*)
                ligne=$( cat -n "${todoList}" | zenity --title="Déplacer" --text="Quelle ligne ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                contenu=$(sed -n "${ligne}p" ${todoList} );
                contenu=$( zenity --title="Ajout" --text="Ajout" --entry --entry-text="${contenu}" ) || continue;
                ModifierLigne ${ligne} "${contenu}";
                ;;
            echanger*)
                notify-send "${0##*/}" "L'action ${action} n'est pas valide.";
                continue;
                debut=$( cat -n "${todoList}" | zenity --title="Déplacer" --text="Quelle ligne ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                fin=$( cat -n "${todoList}" | zenity --title="Déplacer" --text="Vers quelle position ?" --list --column="Lignes" | sed -n 's/^\(\t*\| *\)\([0-9]\+\).*$/\2/gp' ) || continue;
                Erreur "${Ndebut} et ${Nfin}";
                EchangerLigne ${Ndebut} ${Nfin};
                ;;
            afficher*)
                tmpFile="/tmp/.${0##*/}.tmp";
                >${tmpFile} zenity --title="Afficher" --text-info --editable --filename="${todoList}" --height="350" --width="300" && cp -f "${tmpFile}" "${todoList}" || continue;
                echo >> "${todoList}";
                ;;
            *)
                notify-send "${0##*/}" "L'action ${action} n'est pas valide.";
                ;;
            esac;
            ${continuer} || break;
        done;
    }
else
    GUI()
    {
        notify-send "${0##*/}" "Erreut : ni Yad ni Zenity n'est installé.";
    }
fi;


if [ ! -e "${todoList}" ]; then
    exit ;
fi;

while (( $# )); do
    case ${1} in
    -h) # Affiche cette aide
        Usage ;;
    -e) # Éditer ce script
        vim ${0}; exit;;
    -v) # Édite e script avec vim
        vim "${todoList}"; exit;;
    -a) # <ligne> <contenu> : Ajouter une ligne avec du contenu 
        shift;
        AjouterLigne "${1}" "${2}"; shift;
        ;;
    -s) # <ligne> : Supprimer une ligne 
        shift;
        SupprimerLigne "${1}";
        ;;
    -m) # <ligne> <contenu> : Modifier une ligne 
        shift;
        ModifierLigne "${1}" "${2}"; shift;
        ;;
    -d) # <ligne 1> <ligne 2> : Déplacer les lignes 
        shift;
        DeplacerLigne "${1}" "${2}"; shift;
        ;;
    #-x) # <ligne1> <ligne2> : Échanger les lignes en argument
        #shift;
        #EchangerLigne "${1}" "${2}"; shift;
        #;;
    -g) # Interface graphique avec YAD ou Zenity
        shift;
        Erreur ${1};
        GUI ${1};
        ;;
    -*) Erreur "«${1}» option inconnue" >&2;;
    *)
        echo ;;
    esac;
    shift;
done;

sed -i '/^ *$/d' "${todoList}";
#grep -v '^ *$' "${todoList}" | cat -n
cat -b "${todoList}";

Si vous préférez la ligne de commande, le script gère ça aussi !

En plus, il y a ce script pipe pour OpenBox qui va bien avec :

#!/bin/bash
#Par Nordinatueur
#Liste les fichiers du dossier ~/.todo

dossierTodo="${HOME}/.todo";
todoList="${HOME}/.todo.list";
scriptTodo="${HOME}/Public/scripts/todolist.cli.sh ";

Init ()
{
cat <<EOF
    <item label=".    Gui ToDo List    .">
        <action name="Execute">
            <execute> ${HOME}/Public/scripts/todolist.cli.sh -g </execute>
        </action>
    </item>
EOF
}

#LigneExecute <nom> <commande>
LigneExecute ()
{
cat <<EOF
    <item label="${1}">
        <action name="Execute">
            <execute>
                ${2}
            </execute>
        </action>
    </item>
EOF
}

#Crée un pipemenu
#LigneMenu <nom> <commande>
LigneMenu ()
{
cat <<EOF
    <menu execute="${2}" id="todo-${1// /-}-${2##* }" label="${1}" />
EOF
}

# scripTodo -a <ligne> <contenu>

echo '<openbox_pipe_menu>';

case ${1} in
[0-9])
    LigneExecute "Ajouter à cet endroit (${1})" "${0} ajouter ${1}";
    LigneExecute "Supprimer cette tâche" "${scriptTodo} -s ${1}";
    LigneMenu "Déplacer cette tâche vers ..." "${0} deplacer ${1}";
    LigneExecute "Modifier cette tâche" "${0} modifier ${1} '${2}'";
    #LigneMenu "Échanger cette tâche avec ..." "${0} echanger ${1}";
;;
ajouter)
    shift;
    texte="$( yad --center --on-top --entry )" &&\
    ${scriptTodo} -a ${1} "${texte}";
;;
modifier)
    shift;
    texte="$( yad --center --on-top --entry --entry-text="${2}" )" &&\
    ${scriptTodo} -m ${1} "${texte}";
;;
deplacer)
    shift;
    i=1;
    while read ligne ; do
        LigneExecute "${i} - ${ligne}" "${scriptTodo} -d ${1} ${i}";
        ((i++));
    done < "${todoList}";
;;
echanger)
    shift;
    i=1;
    while read ligne ; do
        LigneExecute "${i} - ${ligne}" "${scriptTodo} -x ${1} ${i}";
        ((i++));
    done < "${todoList}";
;;
*)
    Init;
    i=1;
    while read ligne ; do
        #LigneMenu "${ligne}" "${0} ${i} '${ligne}'";
        LigneMenu "${ligne}" "${0} ${i}";
        ((i++));
    done < "${todoList}";
    LigneExecute "Ajouter à cet endroit (${i})" "${0} ajouter ${i}";
esac;

echo "</openbox_pipe_menu>";

N'hésitez pas à me faire part de vos avis.

Edit : Autre chose !
Le conky devient simplement :

${if_existing /home/nordine/.todo.list }${font size=14}ToDo List   (${execpi 5 wc -l < /home/nordine/.todo.list })${font}
${offset 1}${font size=10}${color0}${exec cat -b /home/nordine/.todo.list }${color}${font}${endif}

où vous devez remplacer « /home/nordine » par votre dossier utilisateur.

Dernière modification par nordinatueur (Le 03/01/2012, à 02:02)


Linux User #508094
Pour une meilleure coopération, utilisez des liens relatifs sur le forum !

Hors ligne

#2 Le 03/01/2012, à 01:43

Bousky

Re : [script] ToDoList en bash...

J'ai pas lu ton script (il est tard…), mais pour ma part, j'utilise ce script maison (en ligne de commande uniquement) :

#!/bin/bash

file=~/.todo
default_prio=D
display_color=1
colors=13264571 # A rouge, B jaune, C vert, D cyan, E bleu, F violet, G blanc, date rouge
numbers=1
date_alerte=3

while : ; do
    case "$1" in
    -h|-help|--help|-\?)
        cat <<-EOF
        todo.sh [options] commande ...

        OPTIONS

        -f fichier
                indique le fichier à utiliser au lieu de $(sed "s|^$HOME|~|" <<< "$file")

        -c      désactive la mise en forme
        -C      active la mise en forme
        -# couleurs
                indique les couleurs à utiliser, $colors par défaut

        -n      ne pas numéroter les tâches
        -N      numéroter les tâches

        -j jours
                délais pour la mise en évidence des dates

        -d      débugage

        COMMANDES

        add [@date] [%priorité] texte ...
                ajoute une tâche
        
        edit indice [@[date]] [%priorité] [texte ...]
                modifie une tâche. Sans arguments, l'édition est interactive.
                @ sans date permet de supprimer l'échéance.
        
        del indice ...
                supprime une(des) tâche(s)

        list    liste les tâches

        test    retourne 0 si il y a des tâches, 1 sinon

        Il est possible d'abréger une commande en ne mettant que son initiale.

        DATES, PRIORITÉS & TAGS

        Les dates doivent êtres comprises par « date --date ». Elles sont
        arondies au jour près.
        Les priorités sont des lettres de A à G ou des chiffres de 1 à 7
        (D (4) par défaut).
        Tout mot du texte commençant par un seul « + » est considéré comme
        un tag. C'est une fonctionnalité purement cosmétique (souligné gras).
        EOF
        exit
    ;;
    -v|-version|--version)
        cat <<-EOF
        todo.sh
                Version 2.1.1
                Créé par Nicolas « Bousky » Bouscarle
                Placé sous license WTFPL/LPRAB http://sam.zoy.org/lprab/
        EOF
        exit
    ;;
    -f)
        file="$2"
        shift
    ;;
    -#)
        if [[ ! "$2" =~ ^[-0-79][-0-79][-0-79][-0-79][-0-79][-0-79][-0-79][-0-79]$ ]] ; then
            echo "couleurs \"$2\" non valides" >&2
            exit 1
        fi
        for i in {0..7} ; do
            if [ "${2:$i:1}" = - ] ; then
                colors_new=${colors_new}${colors:$i:1}
            else
                colors_new=${colors_new}${2:$i:1}
            fi
        done
        colors=$colors_new
        shift
    ;;
    -j)
        if ! [ "$2" -ge 0 ] &> /dev/null ; then
            echo "nombre de jours invalide $2" >&2
            exit 1
        fi
        date_alerte="$2"
        shift
    ;;
    -c) display_color=0 ;;
    -C) display_color=1 ;;
    -n) numbers=0 ;;
    -N) numbers=1 ;;
    -d) debug=1 ;;
    -*)
        echo "Option \"$1\" invalide" >&2
        exit 1
    ;;
    *)  break
    esac
    shift
done

if [ "$debug" ] ; then
    echo "----DEBUG----"
    echo "file         : $file"
    echo "default_prio : $default_prio"
    echo "colors       : $colors"
fi

touch "$file" || exit 2

function read_input {
    while [ "$1" ] ; do
        case "$1" in
            @@*|%%*) task_text="$task_text ${1:1}" ;;
            @*     ) task_date="${1:1}" ;;
            %*     ) task_prio="${1:1}" ;;
            *      ) task_text="$task_text $1"
        esac
        shift
    done
    task_text=${task_text# }
}

case "$1" in
add|a)
    task_date=xxxx-xx-xx
    task_prio=$default_prio
    shift
    read_input "$@"
    [ "$task_date" = xxxx-xx-xx ] || task_date=$(date -d "$task_date" +%F) || exit 2
    task_prio_unif=$(tr 1234567 ABCDEFG <<< "${task_prio^^}")
    if [[ ! "$task_prio_unif" =~ ^[ABCDEFG]$ ]] ; then
        echo "priorité non valide \"$task_prio\"" >&2
        exit 2
    fi
    if ! [ "$task_text" ] ; then
        echo "toute tâche doit avoir un texte" >&2
        exit 2
    fi
    if [ "$debug" ] ; then
        echo "task_date      : $task_date"
        echo "task_prio      : $task_prio"
        echo "task_prio_unif : $task_prio_unif"
        echo "task_text      :$task_text"
    fi
    echo "$task_prio_unif $task_date $task_text" >> "$file"
;;
del|d)
    while [ "$2" ] ; do
        if ! [ "$2" -gt 0 ] &> /dev/null && ! [ "$2" -le $(cat "$file" | wc -l) ] &> /dev/null ; then
            echo "numéro de lige \"$2\" non valide" >&2
            exit 2
        fi
        sed -i "$2"d "$file"
        shift
    done
;;
list|l)
    sort -d "$file" -o "$file"
    grep -v "^. xxxx-xx-xx" "$file" &>/dev/null || no_date=1
    echap=$(echo -e "\E")
    if [ "$numbers" = 1 ] ; then
        nb_len=$(cat "$file" | wc -l)
        nb_len=${#nb_len}
        num=1
    fi
    while task_line=$(line) ; do
        task_date=$(cut -d" " -f2  <<< "$task_line")
        task_prio=$(cut -d" " -f1  <<< "$task_line")
        task_text=$(cut -d" " -f3- <<< "$task_line")
        if [ "$display_color" = 1 ] ; then
            task_prio="$echap[1;3"$(tr ABCDEFG "$colors" <<< $task_prio)"m($task_prio)$echap[0m"
        else
            task_prio="($task_prio)"
        fi
        if [ "$no_date" ] ; then
            task_date=""
        else
            if [ "$task_date" = xxxx-xx-xx ] ; then
                task_date="                "
            else
                [ "$display_color" = 1 ] && if (($(date -d "$task_date" +%s)<$(date -d "$date_alerte day" +%s))); then
                    red_date=1
                else
                    red_date=0
                fi
                task_date=$(date -d "$task_date" "+%a %e %b %Y")
                task_date="${task_date//.} "
                [ "$red_date" = 1 ] && task_date="$echap[1;3${colors:7:1}m$task_date$echap[0m"
            fi
        fi
        if [ "$numbers" = 1 ] ; then
            printf "%${nb_len}d) " $num
            ((num ++))
        fi
        if [ "$display_color" = 1 ] ; then
            task_text=$(sed -r 's/(^|[[:space:]])\+([^+[:space:]][^[:space:]]*)/\1+'"$echap"'[1;4m\2'"$echap"'[24;22m/g' <<< "$task_text")
            echo -n "$task_date$task_prio $task_text"
            echo -e  "\E[0m"
        else
            echo "$task_date$task_prio $task_text"
        fi
    done < "$file"
;;
test|t)
    [ -s "$file" ]
;;
edit|e)
    if ! [ "$2" -gt 0 ] &> /dev/null && ! [ "$2" -le $(cat "$file" | wc -l) ] &> /dev/null ; then
        echo "numéro de lige \"$2\" non valide" >&2
        exit 2
    fi
    index="$2"
    task_line=$(sed "$index"'!d' "$file")
    task_date=$(cut -d" " -f2  <<< "$task_line")
    task_prio=$(cut -d" " -f1  <<< "$task_line")
    task_text_old=$(cut -d" " -f3- <<< "$task_line")
    if [ "$3" ] ; then
        shift 2
        read_input "$@"
    else
        [ "$task_date" = xxxx-xx-xx ] && task_date=
        echo -n "$index) "
        [ "$task_date" ] && echo -n "@$task_date "
        echo "%$task_prio $task_text_old"
        read -re -i "$task_date" -p @ task_date
        read -re -i "$task_prio" -p % task_prio
        IFS_old="$IFS"
        IFS=
        read -re -i "$task_text_old" -p ">" task_text
        IFS=$IFS_old
    fi
    [ "$task_text" ] || task_text="$task_text_old"
    [ "$task_date" ] || task_date="xxxx-xx-xx"
    [ "$task_date" = xxxx-xx-xx ] || task_date=$(date -d "$task_date" +%F) || exit 2
    task_prio_unif=$(tr 1234567 ABCDEFG <<< "${task_prio^^}")
    if [[ ! "$task_prio_unif" =~ ^[ABCDEFG]$ ]] ; then
        echo "priorité non valide \"$task_prio\"" >&2
        exit 2
    fi
    sed -i "$index"d "$file"
    echo "$task_prio_unif $task_date ${task_text# }" >> "$file"
;;
*)
    echo "Commande \"$1\" invalide" >&2
    exit 1
esac

Linux qui plante complètement ? Plus rien ne répond ? On peut toujours le redémarrer proprement :
Alt + SysRq + REISUB (Retourne En Islande Sur Un Bateau !)

Hors ligne

#3 Le 03/01/2012, à 02:06

nordinatueur

Re : [script] ToDoList en bash...

Très intéressant !
J'étudierai ça dès demain !
D'abord dodo !


Linux User #508094
Pour une meilleure coopération, utilisez des liens relatifs sur le forum !

Hors ligne

#4 Le 05/02/2012, à 23:04

n3o51

Re : [script] ToDoList en bash...

Salut marche pas chez moi le -h

n3o:~ $ ./todo.sh -h
/home/n3o-chacha/.todo.list existe...
/usr/bin/yad
sed: impossible de lire ./todo.sh: Aucun fichier ou dossier de ce type
n3o:~ $

Welcome to the real world
________________________________

Hors ligne