BASH: ls commande inconnue

Il s’agit (avant de lancer une commande plus utile) d’afficher, selon les options la liste des fichiers.
Ce script me paraît simple, mais — quelles que soient le nombre d’ options (0, 1 ou 2) — j’obtiens le message d’erreur suivant:
/usr/local/bin/renommerNomDuCompte.sh: ligne 35: ls : commande introuvable

Le script (sans certaines lignes de commentaire):

#!/bin/bash --
# Renommer les comptes en nom plus humains
# Sans option = tous les fichiers du répertoire courant
# 1 option = le(s) nom(s) de fichier(s) dans le répertoire courant
# 2 options= autre répertoire
# Dans la chaine du nom, 
# remplacer:   X  par: Y
NOM="$1" 
DIR="$2" 
if  [[ -z "$DIR" ]] 
then
  DIR=$(pwd)
fi
if [[ -z "$NOM" ]] 
then
  PATH="${PWD}/*"
else
  PATH=${DIR}/${NOM}
fi
# Commande:
ls "$PATH"

echo « $PATH » donne toujours un nom de chemin valide, ,d’ailleurs, la commande « ls » seule ou avec un nom explicite, donne la même erreur

La commande stat produit le même message, ce n’est donc pas lié directement à ls spécifiquement

Un script Perl personnel renvoie le même message «not found» pour 2 commandes:
sed et grep (les seules commandes du shell utilisée dans «system»
J’utilise ce script depuis des années, quotidiennement, sans jamais de problème.

Un script simple comme

#!/bin/bash --
ls "$PWD"

ne produit pas cette erreur.

Quelle est donc la ligne de mon code qui péche?
Il ne contient que des variables et des tests dans des conditions if très simples.

Pas une bonne idée d’utiliser ls tel quel , utilise plutôt une boucle for comme ça :

for dir in */; do
  echo "$dir"
done

Voir essayer avec ça :

for i in `ls`
do
   echo `ls -l $i`
done

Merci Clochette,

cela semble vrai pour toutes les commandes du shell sur les fichiers, les chaines de caractère etc. ? (stat, sed, grep …)

Bonjour

Les variables d’environnement du shell utilisent des noms entièrement en lettres majuscules.

Ce qui permet de les différencier rapidement des variables qui seront créées dans un script
puisqu’on utilise alors, pour nommer ces dernières, des lettres minuscules.


Dans ton script, en créant ta variable nommée PATH tu as ré-assigné une valeur à cette variable qui est une variable d’environnement importante puisqu’elle est censée contenir la liste des chemins permettant d’accéder à toutes les commandes.

Mais en ré-assignant une autre valeur à la variable d’environnement PATH, le chemin d’accès à la commande ls ne pouvait plus y être trouvé, ce qui fait que la commande externe ls (comme toutes les autres commandes externes) ne pouvait plus être accessible dans la suite du script autrement qu’en spécifiant son chemin absolu :
/usr/bin/ls

Voir :

man --pager='less -p "PATH   "' bash

Ce n’est pas une bonne idée d’utiliser la commande ls dans un script
car son comportement peut changer d’une version à l’autre et il existe des commandes et des méthodes bien plus efficaces, simples et plus fiables pour récupérer les informations que la commande ls pourrait retourner.


Il existe une méthode plus simple pour assigner une valeur par défaut à une variable,
voir le chapitre

man --pager='less -p "Remplacement des paramètres$"' bash

Ou bien, dans la version originale du manuel (non traduit)

LANG=C man --pager='less -p "Parameter Expansion$"' bash

Il y a aussi cette page web : Parameter expansion [Bash Hackers Wiki]

@josephtux
Vouloir réaffecter la variable d’environnement système PATH dans ton script est une très mauvaise idée, bien que non gravissime puisqu’elle ne sera pas exportée hors du script.
Pour qu’une commande soit trouvée indifféremment de la variable système PATH, et sans ambiguïté (alias par exemple), il suffit de l’appeler directement, soit:
/usr/bin/ls

Merci à tous,
en effet, j’ai compris qu’il ne fallait pas créer des variables en majuscule pour éviter ces problèmes. Merci aussi pour vos liens documentaires.

Méfie-toi aussi, dans tes scripts, de ne pas nommer tes variables ou/et tes fonctions ou/et tes scripts
avec un nom qui est déjà utilisé pour nommer une commande de ton système,
comme par exemple les commandes :
dir
ou bien :
test

Tous vos bons conseils existent-ils (un répertoire des bonnes pratiques pour programmer en Bash?)

Sinon, pourquoi ne pas ouvrir une discussion pour en réaliser un, par exemple, exemple évidemment ouvert aux critiques les plus sévères et aux corrections sans brutalité :wink: :

Bonnes pratiques pour programmer en Bash
Factoriser (variables, fonctions)

  • pas de programmation spaghetti!

Bien nommer variables et fonctions

  • du sens
    • singulier, tableaux=pluriels, verbes, etc.
    • informe sans ambiguïté
  • forme sans ambiguité
    • pas de variable tout en capitales, style réservé aux variables du système
    • pas de fonction et de variables au nom identique
    • pas de fonction en minuscule (style à réserver aux commandes du système)

Commentaires intelligents (à différents niveaux)

  • ce que doit produire la fonction, ce qu’elle reçoit, ce qu’elle renvoie
  • pourquoi choisir tel algorithme (pour les pros!), rappels de la syntaxe d’une commande (pour les nuls, comme moi).

Utiliser shellcheck (et sous vim, le plugin syntastic)

etc.

J’ai trouvé ceci, qui semble bien correspondre à ce que je cherche (dans sa forme rapide à explorer):

https://perso.univ-lyon1.fr/jean-patrick.gelas/UE_Systeme/conseils_script_shell/

Bonjour

Quelques liens et documents au sujet de bash :

grymoire Sh

The Art of Unix Programming (by Eric Steven Raymond)

The GNU Bourne-Again SHell (case.edubashtop)

Bash Guide for Beginners (Machtelt Garrels)

Bash Reference Manual

Advanced Bash-Scripting Guide
An in-depth exploration of the art of shell scripting

BashGuide

Linux Documentation Project Guides

Introduction à la programmation en Bash (Eric Sanchis - aral.iut-rodez)

Bash Hackers Wiki Frontpage

Salut MicP,

J’ai été sur ton lien, ça sent la page perso.
Je me permet juste de te dire que les liens sont bleu foncé sur fond foncé et ça pique les yeux.

Un tout grand merci pour ton regroupement d’infos, c’est très utiles.

Pour répondre à ta question josephtux,

le nommage est quelque chose de très important, mais qui malheureusement n’a d’autre manuel que ton imagination et ta logique.

Il y a cependant des conventions de nommage en fonction des languages de programmations, comme en Javascript, BASH, …
Par exemple en bash j’utilise des nom de variable ma_variable , en Javascript maVariable…
Et je m’y tiens tient tout simplement.

En BASH malheureusement on a également tous les programmes systemes dans le PATH, qui peuvent être appelé , j’avais passer des heures à deboger un script de sauvegarde parce que comme un imbécile, j’avais nommer la fonction rsync_backup => qui elle même pointait vers un ALIAS rsync.
Enfin on peut vite se retrouver avec des erreurs étranges, pour cela tu peux également ajouter le -x a ton #!/bin/bash afin de mieux voir ce qu’il se passe.

J’avais également commencé un gros tuto sur bash que tu peux retrouver Mon site

Oui, c’est d’ailleurs pour ça que ça s’appelle les pages perso :slight_smile:
et donc, ces pages web sont adapté à MA vue.

Je n’aurai sans doute pas dû donner ce lien perso,
mais je jais essayer d’arranger ça en essayant de retrouver la source de tous ces liens.

Micp c’était absolument pas une critique mais plutôt une suggestion.
Car à titre personnel, je change ça a la volé dans l’inspecteur.

Rassure-toi, je ne l’ai pas pris négativement, mais comme j’avais eu la flemme de rechercher les liens sur le moment, j’avais simplement donné un lien vers mes pages perso, et donc, c’est logique qu’elles ne puissent pas convenir à tous.

Mais finalement, je viens de prendre le temps de mettre les liens,
et ça a été plus vite fait que je ne le pensais :slight_smile:

Je trouve très étonnant d’extrapoler une règle générale à partir d’un cas extrêmement particulier, à savoir ‹ qu’il ne faudrait jamais utiliser de majuscules pour une nom de variable ›
Dans la même logique, on pourrait dire ‹ qu’il ne faudrait jamais utiliser de minuscules pour un nom de variable ›, puisqu’il existe 1355 risques de confusion avec un nom de commande.

ls /usr/bin |wc -l
1355

echo $(pwd)
echo $PWD

pwd est une commande. PWD est une variable système.

Dans les docs proposées, 10 secondes pour trouver un exemple:
VAR=/usr/lib; export VAR

La seule bonne règle est qu’il ne faut pas ré-utiliser de noms de commandes en minuscules, ou de variables système en majuscules pour éviter toute confusion.

@josephtux
Tu as donc mis 3 jours pour comprendre où était passée la commande ‹ ls ›, il faut donc aller à ton rythme.
Je ne suis pas sûr que tu aies compris que la commande ‹ ls › est en fait explicitement /usr/bin/ls.

Il y a déjà des tonnes de docs bash disponibles, parce-qu’il n’existe pas en fait de doc qui puisse convenir à tout le monde, selon le niveau, et qui soit à jour. Aucune n’est parfaite → c’est pour ça qu’il y en a autant…
Des docs de 2001 ne sont pas à jour, et très ambigües sur certains points, donc perte de temps pour du bash 5.
L’interpréteur de commandes du système Debian de base est dash.

$ readlink /usr/bin/sh
dash

Bash est proposé en plus par défaut car plus évolué (mais plus gourmand en ressources).
zsh en est un autre très puissant qui peut remplacer bash (je suis revenu à bash après des années de zsh).

Bash n’a donc rien de spécifique à Debian, et il y a un fossé entre connaître bash, et l’ensemble des commandes linux, parce-que bash tout seul, sans les commandes linux n’est pas très utile.

La meilleure documentation est celle que tu te feras en fonction de ta progression.
Tu pioches progressivement ce qui te manque dans des docs plus ou moins bien ciblées sur ta recherche, et tu complètes, en pratiquant.
C’est un peu comme le forgeron qui apprend en forgeant.

Pour forger, et revenir au concret, quelques commentaires sur ton script:

1 - dans un script aussi simple, tu aurais pu te passer des variables NOM="$1" DIR="$2", et directement utiliser $1 et $2 dans tout ton script, en précisant seulement en commentaire
# NOM="$1" ; DIR="$2"

2 - à titre d’exercice, tu peux regarder si une solution plus compacte ne parait pas plus conviviale, à savoir:

#!/bin/bash
read -p "NOM ? " NOM
read -p "CHEMIN ? " CHEMIN
ls ${CHEMIN:-$PWD}/$NOM
exit

ou en fonction

fonction1() { read -p "NOM ? " NOM
              read -p "CHEMIN ? " CHEMIN
              ls ${CHEMIN:-$PWD}/$NOM ; }

fonction que tu lances avec
$ fonction1

Il me semble que certaines constructions ne fonctionnent pas avec $1 ,$2 etc. ce qui m’avait fait adopter cette habitude. Mais il y a longtemps, alors que j’étais un débutant un peu plus réactif et j’ai oublié. Peut-être une erreur de ma part déjà à cette époque.
Votre suggestion de commentaire reste intéressante, mais pour un script qui a tendance à s’allonger, je préfère garder un nom plus explicite (au cas ou je modifierai la syntaxe du script, $1 et $2 pourrai changer de signification, comme être décalés ou interverti). Comme vous l’avez constaté, je suis au niveau presque 0 de la programmation, donc au fur et à mesure que j’apprends (et aussi hélas, désapprends!) je peux vouloir modifier beaucoup de choses! C’est aussi vrai dans le cas ou je veux étendre et améliorer le script.

Je ne crois pas avoir écrit «il faut» mais suggéré en attente de critique. La votre reste cependant intéressante. En effet, ce que je cherche c’est à trouver des bonnes pratiques:

  • pour éviter des pièges comme celui de collision/conflit avec des noms existants (variables, fonctions, programmes), en cause dans la question initiale de ce fil,
  • pour faciliter la relecture du script et sa compréhension
  • éventuellement pour adopter des pratiques personnelles de personnes compétentes
  • et pour être aussi plus lisible et compréhensibles par d’autres au cas où il serait utile de partager ce script (je pense à d’autres que moi, lecteurs de cette discussion.)
  • peut être pour d’autres motifs qui m’échappent et qu’il serait pourtant utile de prendre en compte.

Par exemple j’utiliseparfois. certaines notation venant du C, de Java ou autre:

  • je prefixe la variable avec son type (un abrégé du type) afin de me rappeler l’utilité de la variable et son contenu; de fait je suis sur de ne jamais avoir de concomitance avec une variable système ou un executable.
    Par exemple: variable de nom : cNom (variable nom de type char)

c’est parce que je ne connais pas toutes ces commandes ni toutes ces variables, que j’envisage cette règle personnelle.
Est-ce qu’elle présente un inconvénient?
Sinon j’aime nommer les variables (comme mes noms de fichier) avec des majuscules au début de chaque mot composant le nom. Auparavant je mettais comme vous le faites des « _ » pour les séparer, mais ma méthode me suffit pour la lisibilité et raccourcit un peu les noms de fichier plus lisibles également dans les listes (comme ls -l ).