Une place pour une véritable innovation. Partagez vos propres utilitaires créés avec la communauté Manjaro.
Questions et discussions sur la programmation et le codage.
Répondre

[python] réécrire un simple ls

#1Messageil y a 1 mois

:bjr:

Je viens de déterrer et mettre à jour un de mes tout premier script python : un clone de la commande ls.
Bien sûr , ici il ne s'agit que d'une version très inférieure mais qui était surtout un très bon challenge pour apprendre python.

Le cahier des charges:
Fonctionnalités "particulières":
  • Afficher les branches si répertoires Git
  • Afficher le nombre d'entrées des répertoires
  • Afficher des icônes (police Enojy)
  • Avec un peu de couleurs, mais un minimum (répertoires et exécutables)
en paramètres optionnels:
  • -a : fichier cachés
    -m : afficher le type-mime (si le paquet optionnel python-magic est installé)
    -s : trier par taille (size)
    -t : trier par date
    -o : afficher le propriétaire
    -d : afficher uniquement la date (et pas l'heure)

./lls / -ot
:: list: / 
700  root                        2019-01-29 12:33 📁 lost+found/  
755  root                      2 2019-01-29 12:57 📁 srv/  
750  root                        2019-01-29 16:02 📁 root/  
755  root                      2 2019-01-29 17:20 📁 home/  
755  root                      3 2019-03-11 09:49 📁 opt/  
755  root                      1 2019-05-08 17:05 📁 mnt/  
755  root                      8 2019-07-27 00:07 📁 boot/  
555  root                    241 2019-07-28 06:47 📁 proc/  
555  root                     11 2019-07-28 06:47 📁 sys/  
755  root                     14 2019-07-28 06:47 📁 var/  
755  root                    187 2019-07-28 06:47 📁 dev/  
755  root                     30 2019-07-28 06:49 📁 run/  
755  root                   3655 2019-07-28 06:49 📁 bin/ -> /usr/bin  
755  root                   3655 2019-07-28 06:49 📁 sbin/ -> /usr/bin  
755  root                   4621 2019-07-28 06:49 📁 lib64/ -> /usr/lib  
755  root                   4621 2019-07-28 06:49 📁 lib/ -> /usr/lib  
755  root                    211 2019-07-28 06:49 📁 etc/  
755  root                     10 2019-07-28 06:49 📁 usr/  
1777 root                     24 2019-07-28 09:35 📁 tmp/

./lls ~/workspace/python/manjaro-hello -as
:: list: /home/patrick/workspace/python/manjaro-hello (application-utility*, master)
755            1 2018-09-14 21:21 📁 .tx/  
755           46 2018-09-14 21:21 📁 po/  
755            1 2019-02-14 02:38 📁 ui/  
755            3 2018-09-14 21:21 📁 data/  
755            5 2018-09-14 21:21 📁 .idea/  
755            2 2018-09-14 21:21 📁 utils/  
755            2 2018-09-14 22:09 📁 src/  
755           11 2019-03-05 20:29 📁 .git/  
755           45 2018-09-14 22:11 📁 locale/  
644         30 b 2018-09-14 21:21 📄 .gitignore                                                         
600         48 b 2019-02-07 20:22 🐬 .directory                                                         
755        506 b 2018-09-14 22:19 ✨ launch.sh                                    
644        993 b 2018-09-14 21:21 📄 README.md                                                          
755        1.2 K 2018-09-14 21:21 ✨ manjaro-hello.desktop                        
644        5.0 K 2018-09-14 21:21 📄 CHANGELOG.md                                                       
644       34.5 K 2018-09-14 21:21 📄 LICENSE
je vous donne 8 heures pour écrire le script lls ...
Mais NON, je ne suis pas si méchant :twisted:
Script très personnel, mais facilement modifiable pour avoir son petit ls perso

#!/usr/bin/python
"""
as bash "ls"

Params:
    -a : hidden files
    -m : display type-mime (if python-magic installed)
    -s : sort by size (less -> big)
    -t : sort by date (old -> recent)
    -o : display owner
    -d : display only date
"""

import os
import sys
import stat
from pathlib import Path
import datetime
import operator
try:
    import magic    # pacman -S python-magic --asdeps
except ModuleNotFoundError:
    pass


def rgb_fg(r: int, g: int, b: int) -> str:
    return '\x1b[38;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm'

def rgb_bg(r: int, g: int, b: int) -> str:
    return '\x1b[48;2;' + str(r) + ';' + str(g) + ';' + str(b) + 'm'

def html_color(html, txt=True):
    if txt:
        return rgb_fg(int('0x'+html[0:2], 0), int('0x'+html[2:4], 0), int('0x'+html[4:6], 0))
    return rgb_bg(int('0x'+html[0:2], 0), int('0x'+html[2:4], 0), int('0x'+html[4:6], 0))


COLOR_NONE = '\33[0m'
COLOR_LINK = html_color('666666')
COLOR_DIR = html_color('33ccee')
COLOR_EXE = html_color('33eecc')
COLOR_MIME = html_color('888888')
COLOR_GIT = html_color('999999')



class Node:

    def __init__(self, node, file_stats: None, use_mime=False, use_owner=False, short_date=True):
        self.node = node        # Path
        self.stat = file_stats or node.stat() # FileNotFoundError = bad link
        self.use_mime = use_mime and 'magic' in sys.modules
        self.use_owner = use_owner
        self.short_date = short_date

    def get_size(self):
        if self.node.is_dir():
            try:
                return len(set(self.node.iterdir()))
            except PermissionError:
                return ''
        else:
            return self.convert_bytes(self.stat.st_size)

    def get_owner(self):
        ret = ''
        try:
            one = self.node.owner()
        except KeyError:
            one = '?'       # user on other linux
        two = ''
        try:
            two = f"/{self.node.group()}"
        except KeyError:
            two = f"/{self.stat.st_gid}"       # group on other linux
        if two[1:] == one:
            two = ''
        if self.stat.st_uid > 999:
            #ret = f"{one}({self.stat.st_uid}){two}"
            ret = f"{one}{two}"
        else:
            ret = f"{one}"
        return f"{ret:16}"

    def get_time(self):
        long = 10 if self.short_date else 16
        return str(datetime.datetime.fromtimestamp(self.stat.st_mtime))[:long]

    def get_name(self):
        name = f"{COLOR_DIR}{self.node.name}/{COLOR_NONE}" if self.node.is_dir() else self.node.name
        name = f"{name} {COLOR_LINK}-> {self.node.resolve()}{COLOR_NONE}" if self.node.is_symlink() else name
        if self.is_exe:
            name = f"{COLOR_EXE}{name}{COLOR_NONE}"
        if self.node.is_dir():
            gits = self.get_git()
            name = f"{name} {COLOR_GIT}{gits}{COLOR_NONE}"
        return f"{name}"

    @property
    def is_exe(self):
        return self.stat.st_mode & (stat.S_IXGRP | stat.S_IXUSR)

    def is_git(self):
        git_dir = Path(self.node / ".git/refs/heads")
        try:
            branches = [x.name for x in git_dir.iterdir()]
            if len(branches) > 1:
                with open(self.node / ".git/HEAD", 'rt') as f_read:
                    current = f_read.readline().split('/').pop().rstrip()
                    branches[branches.index(current)] = f"{current}*"
            return tuple(branches)
        except (PermissionError, FileNotFoundError):
            pass
        return tuple()

    def get_git(self):
        gits = self.is_git()
        if gits:
            return str(gits).replace("'", '')
        return ''

    def is_dir(self):
        """ for sort """
        return self.node.is_dir()

    def name_lower(self):
        """ for sort """
        return self.node.name.lower()

    def size(self):
        """ for sort """
        return self.stat.st_size

    def time(self):
        """ for sort """
        return self.stat.st_mtime

    def __str__(self):
        """ display file with infos """
        mime = ""
        try:
            if self.use_mime and not self.node.is_dir():
                mime = magic.detect_from_filename(self.node).mime_type
        except ValueError:
            pass

        owner = ''
        if self.use_owner:
            owner = self.get_owner()

        datas = (
            f"{oct(stat.S_IMODE(self.stat.st_mode))[2:]:4}",
            owner,
            f"{self.get_size():>10}",
            f"{self.get_time()}",
            f"{self.get_icon(mime)} {self.get_name():66}",
            f"{COLOR_MIME}{mime}{COLOR_NONE}",
        )
        return ' '.join(datas)

    def get_icon(self, mime=None):
        """
          🚫 🌀
         🔴 🔵 🔶 🔷 🔸 🔹
        """
        filenames = {
            '.directory': '🐬',
            'lost+found/' : '🧻', # 🚾
            '.ssh' : '🔐', #'🔑', 🔒
            '.config' : '🔧',
        }
        extensions = {
            '.php': '🐘',
            '.py': '🐍',
            '.png': '🌌',
            '.jpg': '🌌',
            '.txt': '📝',
            '.log': '📄',
            '.conf': '🔧',
            '.css': '📑',
            '.html': '🌐',
            '.service': '⚙',
            '.pacsave': '🚸',
            '.fr': '🇫🇷',
            '.en': '🇬🇧',
        }
        mimes = {
            'inode/directory': '📁',
            'image/png': '🌌',
            'image/gif': '🌌',
            'image/vnd.microsoft.icon': '🌌',
            'inode/symlink': '🔗',
            'text/plain': '📄',
            'text/x-php': '🐘',
            'text/x-python': '🐍',
            'application/json': '📑',
            'text/html': '🌐',
            'text/x-shellscript': '🌀',
            'text/x-objective-c': '🌀',
            'application/octet-stream': '  ',
        }
        ico = filenames.get(self.node.name, None)
        #print(self.node.name, ico)
        if ico:
            return ico
        if self.node.is_dir():
            return '📁' # 🗂
        if self.node.is_symlink():
            return '🔗'
        if self.is_exe:
            return '✨'

        ico = extensions.get(self.node.suffix, None)
        if ico:
            return ico

        if 'magic' in sys.modules:
            try:
                if not mime:
                    mime = magic.detect_from_filename(self.node).mime_type
                ico = mimes.get(mime, None)
                if ico:
                    return ico
            except ValueError:
                return '  '

        return '  '

    @staticmethod
    def convert_bytes(num):
        """ this function will convert bytes to MB.... GB... etc  """
        if num == 0:
            return ''
        step_unit = 1000.0 #1024 bad size
        for x in 'b', 'K', 'M', 'G', 'T':
            if num < step_unit:
                if x == 'b':
                    return f"{num} {x}"
                return "%3.1f %s" % (num, x)
            num /= step_unit


def parse_user_parameters():
    """ parse sys.argv and set var options """
    options = {
        'hidden': False,
        'mime': False,
        'owner': False,
        'short_date': False,
        'help': False,
        'sort': 'name_lower',
    }
    for param in sys.argv:
        if not param.startswith('-'):
            continue
        param = param.rstrip('-').strip()
        for char in param:
            if char == 'h':
                options['help'] = True
            if char == 'a':
                options['hidden'] = True
            if char == 'm':
                options['mime'] = True
            if char == 'o':
                options['owner'] = True
            if char == 'd':
                options['short_date'] = True
            if char == 't':
                options['sort'] = 'time'
            if char == 's':
                options['sort'] = 'size'
    '''
    # first version
    options['hidden'] = '-a' in sys.argv
    options['mime'] = '-m' in sys.argv
    options['owner'] = '-o' in sys.argv
    options['short_date'] = '-d' in sys.argv
    if '-t' in sys.argv:
        options['sort'] = 'time'
    if '-s' in sys.argv:
        options['sort'] = 'size'
    '''
    return options

options = parse_user_parameters()
if options['help']:
    print("""Params:
    -a : hidden files
    -m : display type-mime (if python-magic installed)
    -s : sort by size (less -> big)
    -t : sort by date (old -> recent)
    -o : display owner
    -d : display only date""")
    exit(0)

def iterdir(path, options):
    """Iterate over the files in this directory.  Does not yield any result for the special paths '.' and '..'. """
    for entry in os.scandir(path):
        if entry.name in {'.', '..'}:
            continue
        try:
            yield Node(
                Path(entry.path),
                use_mime=options['mime'],
                use_owner=options['owner'],
                short_date=options['short_date'],
                file_stats=entry.stat()
                )
        except FileNotFoundError:
            print(f"🤕🔗 ERROR: \"{entry.name}\" a break link !")


cur_dir = Path.cwd()
if len(sys.argv) > 1:
    # find first attr not a parameter, is path to list
    for p in sys.argv[1:]:
        if p.startswith('-'):
            continue
        if p.startswith('/'):
            cur_dir = Path(p)
        elif not p.startswith('-'):
            cur_dir = cur_dir / p

if cur_dir.is_dir():
    node = Node(cur_dir, use_mime=False, file_stats=False)
    print(":: list:", cur_dir, f"{COLOR_MIME}{node.get_git()}{COLOR_NONE}")
    count = 0

    for node in sorted(sorted(iterdir(cur_dir, options), key=operator.methodcaller(options['sort'])), key=operator.methodcaller('is_dir'), reverse=True):
        count += 1
        if not options['hidden']:
            if not node.node.name.startswith('.'):
                print(node)
        else:
            print(node)
    print(f":: ({count}) entrées")            
else:
    try:
        fnode = Node(cur_dir, use_mime=options['mime'], file_stats=False)
        print(fnode)
    except FileNotFoundError:
        pass

[python] réécrire un simple ls

#2Messageil y a 1 mois

Merci beaucoup.
Peut tu me dire comment tu a changer les icônes pour avoir celle du screen ?
Merci

[python] réécrire un simple ls

#3Messageil y a 1 mois

salut

je ne comprends pas ta questions :oops: perso, j'ai les mêmes icônes avec ma copie écran et les copies texte : c'est normal ce sont des emoji : c'est donc affiché par la police de mon bureau (kde) emojiOne ; il est possible que tu n'utilises pas la même police donc un rendu différent ?
Dans mon centre de config de kde, apparemment je n'ai aucune police d'emoji installée manuellement et uniquement noto-fonts-emoji comme paquet

[python] réécrire un simple ls

#4Messageil y a 1 mois

J'aimerai avoir les même icônes que les tienne pour l'affichage dans le terminal.
Voilà le rendu chez moi.

https://postimg.cc/BPxCpTgt

[python] réécrire un simple ls

#5Messageil y a 1 mois

Tu as déjà des icônes :mrgreen:

Il faudrait dans un premier temps savoir
* si tu as des couleurs dans ton navigateur web en regardant le premier message ... Image
* si tu as des couleurs dans l'éditeur de texte gui du code source ,pour moi ok avec kate et vscode (et même nano)

si oui, ton bureau supporte bien cette police mais cela vient de ton terminal : support des emoji couleur est bon depuis "peu" pour le terminal de kde (Déc. 2018) et gnome, les autres ... aucune idée et je suppose que non.

[python] réécrire un simple ls

#6Messageil y a 1 mois

Bon je viens de voir que j'ai les icones en couleur dans les notifications dunst sans avoir rien toucher.
Par contre niveau terminal rien du tout ( Urxvt Uxterm St Konsole etc )
J'ai voulu changer de police mais ça n'a rien changer même avec le pack nerd.

[python] réécrire un simple ls

#7Messageil y a 1 mois

justement (avec konsole), tu dois avoir les icones de la police originale (nerd), c'est le role du fichier *emoji.conf de notre home de préférer une police "autre" pour les afficher quel que soit notre police (voir lien + haut - mon .conf est encore plus simple mais dans même idée)

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>

  <match target="pattern">
    <test qual="any" name="family"><string>Segoe UI Emoji</string></test>
    <edit name="family" mode="assign" binding="same"><string>Noto Color Emoji</string></edit>
  </match>

</fontconfig>

<!-- 
file: ~/.config/fontconfig/conf.d/01-emoji.conf 
voir aussi: 
https://gist.github.com/IgnoredAmbience/7c99b6cf9a8b73c9312a71d1209d9bbb
-->

[python] réécrire un simple ls

#8Messageil y a 1 mois

Merci pour ta réponse.

J'ai tester avec le poste de magiblot et ça marche bien pour konsole par contre pour st urxvt pas moyen de les faire marcher.

Bon pas grave j'ai quand même appris quelque chose.

Merci

[python] réécrire un simple ls

#9Messageil y a 1 mois

Ne ne m'étonne pas pour d'autres terminaux : les emoji (couleurs) ne sont supportés par konsole que depuis la toute fin de l'année dernière - plus vieux d'un an pour vte et ces dérivés : tilix / gnome-terminal
les autres affiches (uniquement) les caractères Unicode de la police courante - pas véritablement la même chose


----
pour urxvt, peut-être possible ?

[python] réécrire un simple ls

#10Messageil y a 1 mois

J'ai tester ce que tu ma donner mais j'ai que des petits carrer sans aucune emoji.
Ce qui me dérange surtout dans konsole c'est que je la trouve lente et que les image et vidéos au niveau des aperçu font des lignes surtout avec ranger.
Je peut utiliser uberzug mais bon pas envie de refaire toute ma config

[python] réécrire un simple ls

#11Messageil y a 1 mois

Salut,

Je pense qu'il est plus facile d'utiliser les Nerd Fonts, mais je n'ai vu que deux remplaçants à ls, colorls et lsd. colorls est complet et configurable, mais lourd, de plus, il m'affichait les liens symboliques comme des fichiers normaux. :saispas:
lsd est plus limité, bien qu'à l'avenir, les couleurs personnalisées devraient arriver, mais il est vraiment léger, même plus qu'exa que j'avais essayé aussi.

[python] réécrire un simple ls

#12Messageil y a 1 mois

Pour les icônes, c'est selon les goûts et sa config, c'est juste un copier/coller à faire dans le code source.
Ici rien n'est configurable car c'est un script personnel fait pour moi et mes goûts et donc bien limité (pas question de reprendre les options de ls, elles sont trop compliqués pour mon usage)
Je ne pense pas en faire un jour une version dans aur donc "configurable"
Smurf a écrit :
il y a 1 mois
mais il est vraiment léger
Pour un simple ls, la légèreté ou rapidité n'est pas un critère ? normalement c'est l'affichage qui est le plus lent et le processus ne tourne que quelques secondes.

------

Pour la rapidité (mais pas la légèreté), j'ai aussi écrit un script perso "d’apprentissage" en go (donc bien loin de python) : un find|grep - il doit-être très rapide avec les goroutines car j'ai du le castrer (trop de fichiers ouvert en même temps pour linux) mais l'executable pèse près d'1Mo après compression avec upx

ps: "doit-être très rapide" : je ne suis vraiment pas convaincu !!! sur DD après suppression du cache disque, j'obtiens 2 minutes pour un scan de 26 000 fichiers texte avec mon script et même temps avec un find|grep, donc j'ai juste un affichage beaucoup plus exploitable avec mon script... (sans vider le cache, après je suis dans les 3..5 secondes ! et 4 secondes sur ssd ; ici la vitesse n'a plus grand intérêt !)

ggrep "regex" [repertoire] [extension]
Il cherche par défaut dans tous les fichiers de type texte

/*
	recursive grep "pattern" in a directory
	scan all text files by default

	env parameter: GGREPLIMIT : number of goroutines that are allowed to run concurrently
*/
package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	//https://misc.flogisoft.com/bash/tip_colors_and_formatting
	COLOR_NONE  = "\033[0m"
	COLOR_DIR   = "\033[0;34m"
	COLOR_EXE   = "\033[0;36m"
	COLOR_ERROR = "\033[38;5;124m"
	COLOR_DIM   = "\033[38;5;243m"

	_VERSION = "1.0.0 rc2"
)

var GitBranch string
var Version string
var BuildDate string
var GitID string

// input values
type Options struct {
	mime, help, binError, timer, usecase bool
	sort, directory, pattern, ext        string
	limit                                uint64
}

// parse console input values
func argsParser() (Options, error) {
	ret := Options{
		sort:      "none",
		directory: "",
		limit:     32,
	}

	// channel: number of goroutines that are allowed to run concurrently
	// if too many: linux error: too many files opened
	limit, err := strconv.ParseUint(os.Getenv("GGREPLIMIT"), 10, 8)
	if err != nil || limit < 1 {
		ret.limit = 32
	} else {
		ret.limit = limit
	}

	for _, arg := range os.Args[1:] {
		if len(arg) < 1 {
			// user enter empty values : program.go "" "" "" !
			continue
		}
		if !strings.HasPrefix(arg, "-") {
			// first is regex
			if ret.pattern == "" {
				arg := strings.ReplaceAll(arg, "(", "\\(")
				ret.pattern = strings.ReplaceAll(arg, ")", "\\)")
				continue
			} else if ret.directory == "" {
				// path ?
				if !strings.HasPrefix(arg, "/") {
					ret.directory, _ = os.Getwd()
					ret.directory += "/" + arg
					ret.directory = filepath.Dir(ret.directory)
				} else {
					ret.directory = arg
				}
				_, err := os.Stat(ret.directory)
				if os.IsNotExist(err) {
					ret.directory, _ = os.Getwd()
					return ret, err
				}
				continue
			} else if ret.directory != "" {
				// one optional extension filter
				if arg[:1] == "." {
					ret.ext = arg
				} else {
					ret.ext = "." + arg
				}
				continue
			}
		}
		// test options, not used here
		for _, ch := range arg[1:] {
			switch ch {
			case 'h':
				ret.help = true
			case 'b':
				ret.binError = true
			case 'm':
				ret.mime = true
			case 't':
				ret.timer = true
			case 'c':
				ret.usecase = true
			case 's':
				ret.sort = "size"
			}
		}
	}
	if len(ret.pattern) < 3 && !ret.help {
		return ret, errors.New("console value: regex pattern too short")
	}
	if !ret.usecase {
		// by default, case-insensitive
		ret.pattern = "(?i)" + ret.pattern
	}
	if ret.directory == "" {
		ret.directory, _ = os.Getwd()
	}
	_, err = os.Stat(ret.directory)
	if err != nil {
		return ret, err
	}
	return ret, nil
}

func main() {

	options, err := argsParser()
	if err != nil || options.help {
		usage(err, &options)
	}

	if options.timer {
		start := time.Now()
		fmt.Println(":: Time mesure")
		defer func() {
			elapsed := time.Since(start)
			//elapsed.Seconds
			fmt.Printf(":: Time duration: %.2f sec. = %s ", elapsed.Seconds(), elapsed)
		}()
		//defer f()
	}

	dir, _ := filepath.Abs(options.directory)
	fmt.Println("::", dir)

	pattern, err := regexp.Compile(options.pattern) // ignore Case
	if err != nil {
		usage(err, &options)
	}

	var mutex = sync.Mutex{}
	var waitg sync.WaitGroup
	sem := make(chan bool, options.limit)
	var total uint64 = 0
	var count uint64 = 0
	var nblines uint64 = 0

	// FIXME Walk not view symlinks !! is in doc https://golang.org/pkg/path/filepath/#Walk
	// TODO write my recursive function
	err_d := filepath.Walk(options.directory,
		func(path string, info os.FileInfo, err error) error {
			if err != nil {
				// bad permission for one file
				fmt.Println(COLOR_ERROR, err, COLOR_NONE)
				return nil
			}
			if strings.HasPrefix(info.Name(), ".") || info.IsDir() || info.Size() < 3 {
				return nil
			}
			if strings.Contains(path, "__pycache__") || strings.Contains(path, "/.git/") {
				return nil
			}
			if options.ext != "" && (options.ext != filepath.Ext(path)) {
				return nil
			}

			waitg.Add(1)
			go func(filename string, pattern *regexp.Regexp) {

				sem <- true
				defer func() { <-sem }()
				defer waitg.Done()

				freader, err := os.OpenFile(filename, os.O_RDONLY, 0444)
				if err != nil {
					//fmt.Println(err.Error)
					return
				}
				defer freader.Close()

				// test if is a text file ? only if scan all files
				if options.ext == "" && !isTxtSign(freader) {
					//println("binary:", long, filename)
					return
				}

				matchs := grep_stream(bufio.NewReader(freader), pattern)

				mutex.Lock()
				total += 1
				mutex.Unlock()
				// display results for one file
				if len(matchs) > 0 {
					var out = fmt.Sprintln(len(matchs), "in", COLOR_DIR, filename, COLOR_NONE)
					for _, match := range matchs {
						out += match.String()
					}
					mutex.Lock()
					count++
					nblines = nblines + uint64(len(matchs))
					fmt.Println(out)
					mutex.Unlock()
				}
			}(path, pattern)

			return nil
		})
	if err_d != nil {
		panic(err_d)
	}
	waitg.Wait()
	if count > 0 {
		fmt.Printf("\nscan %v fichiers - trouvé %v - %v lignes \n", total, count, nblines)
	}
}

// fields returned by grep_ function
type line struct {
	id  uint64
	txt string
}

func (l *line) String() string {
	return fmt.Sprintf("%v%5d%v %v\n", COLOR_DIM, l.id, COLOR_NONE, l.txt)
}

// grep stream
// return array of lines
func grep_stream(reader *bufio.Reader, pattern *regexp.Regexp) []line {

	returns := []line{}
	scanner := bufio.NewScanner(reader)
	var line_count uint64 = 1
	for scanner.Scan() {
		data := scanner.Text()
		if found := pattern.FindString(data); found != "" {
			data = strings.ReplaceAll(data, found, COLOR_EXE+found+COLOR_NONE)
			if strings.HasPrefix(data, "    ") {
				data = data[4:]
			}
			returns = append(returns, line{id: line_count, txt: data})
		}
		line_count++
	}

	return returns
}

// read file for test if is not a binary
// from https://mimesniff.spec.whatwg.org/#binary-data-byte
func isTxtSign(reader *os.File) bool {
	buff := make([]byte, 1024)
	_, err := reader.Read(buff)
	if err != nil {
		return false
	}
	reader.Seek(0, 0)
	for _, b := range buff {
		if b == 0x00 {
			continue
		}
		if b <= 0x08 ||
			b == 0x0B ||
			0x0E <= b && b <= 0x1A ||
			0x1C <= b && b <= 0x1F {
			return false
		}
	}
	return true
}

func usage(err error, options *Options) {
	if Version != "" {
		fmt.Printf("\n%s Version: %v %v %v %v\n", filepath.Base(os.Args[0]), Version, GitID, GitBranch, BuildDate)
	}
	fmt.Printf("Usage: %v \"regex PATTERN\" [directory] [extension]\n", filepath.Base(os.Args[0]))
	fmt.Println("\t-c : Case sensitive")
	fmt.Printf("\nenv GGREPLIMIT=%v (allowed to run concurrently)\n", options.limit)
	if err != nil {
		fmt.Println("\n", err)
		os.Exit(1)
	}
	os.Exit(0)
}
Si le fichier se nomme ggrep.go,
exécuter go run ggrep.go "mot_a_rechercher" / compilation gobuild -ldflags "-s -w" ggrep.go

-------
Sujet dans la rubrique Programmation et non "contribution" :wink:

[python] réécrire un simple ls

#13Messageil y a 1 mois

papajoke a écrit :
il y a 1 mois
Smurf a écrit :
il y a 1 mois
mais il est vraiment léger
Pour un simple ls, la légèreté ou rapidité n'est pas un critère ? normalement c'est l'affichage qui est le plus lent et le processus ne tourne que quelques secondes.
Il est léger au niveau de la place prise et des dépendances, et il est plus réactif d'après ce test.

[python] réécrire un simple ls

#14Messageil y a 1 mois

J'ai trouvé plus simple, un fork de ls qui ajoute seulement des icônes monochromes, ls-icons. On le trouve dans AUR, il faut aussi installer le paquet icons-in-terminal, je trouve que le rendu est pas mal.

Image

[python] réécrire un simple ls

#15Messageil y a 4 semaines

:salut: je viens de trouver (pas dispo dans aur) ls-go

[python] réécrire un simple ls

#16Messageil y a 4 semaines

C'est pas mal aussi, quelques options sont sympas. S'il y a une chose que je regrette avec tous ces projets, c'est qu'une bonne partie a les couleurs codées en dur, obligeant à modifier les sources avant compilation.

Répondre