Bonjour.
Mon problème est le suivant, j'aimerai faire une boucle sur le liste des fichiers de mon répertoire.
Mon code ressemble à ça:
#!/bin/sh
VAR=0
ls | while read line
do
VAR=$(($VAR +1))
done
echo $VAR
Mais vu qu'une pipe lance un nouveau processus, la ligne echo $VAR affiche systématiquement 0
J'ai trouvé une solution qui marche pour bash:
#!/bin/bash
VAR=0
while read line
do
VAR=$(($VAR +1))
done < <(ls)
echo $VAR
Mais cette solution ne marche pas avec dash[1]. J'ai cherché sur internet mais cette fois ci google n'était pas mon ami.
Si jamais vous avez une idée pour faire la même chose en dash, ce serait avec grand plaisir.
Merci d'avance.
[1]Debian_Almquist_shell
# J'oubliais...
Posté par Diagonale de Cantor (site web personnel) . Évalué à 1.
[^] # Re: J'oubliais...
Posté par pi6Lohe . Évalué à 1.
#!/bin/dash
VAR=0;
while read line
do
var=$(($var + 1))
done <<EOF
$(ls)
EOF
echo $var
# Reprend les bases
Posté par benoar . Évalué à 4.
Boucluer sur des fichiers (ou même des patterns, on utilise le globbing du shell) :
for line in *; do
...
done
Et si tu veux compter le nombre de lignes d'une commande, ce qui est à priori ce que tu veux faire :
ls | wc -l
Ça me semble un tout petit peu moins "tordu" que tes solutions ...
[^] # Re: Reprend les bases
Posté par Diagonale de Cantor (site web personnel) . Évalué à 1.
[^] # Re: Reprend les bases
Posté par benoar . Évalué à 0.
for line in `./script.sh`; do
...
done
Je te conseille d'aller lire un bon manuel de shell (j'ai pas de lien sous la main, désolé).
[^] # Re: Reprend les bases
Posté par Diagonale de Cantor (site web personnel) . Évalué à 2.
cantor@lenny:~$ ./toto
première ligne
seconde ligne
cantor@lenny:~ $ for line in `./toto` ; do echo $line; done
première
ligne
seconde
ligne
Et donc non... cela ne marche pas à cause des espaces...
D'où le truc bizarre avec le
./script.sh | while read line; do
...
done
qui lui marche. Mais le problème c'est que les variables modifiés dans le while sont des variables locales (car la pipe crée un nouveau processus).
Mais si tu as une solution plus élégante je suis prenant...
[^] # Re: Reprend les bases
Posté par benoar . Évalué à 2.
Mais bon, si tu pouvais directement nous présenter le problème en entier au lieu de rajouter des petits détails "cachés" à chaque fois qui changent tout, ça aiderait.
(genre je sens que ton traitement dans le if pourrait peut-être remplacer par un grep, etc)
Là, la solution simple serait de changer script.sh pour qu'il renvoie des chaînes correctement échappées.
Ou faire autre chose que du shell (encore plus du dash; c'est pour les traitements "simples"). Si tu es dans un environnement avec peu de ressources, j'ai déjà vu des gens utiliser awk par exemple (c'est le cas d'OpenWRT, qui n'a qu'un shell réduit, mais busybox fournit awk qui peut faire plein de chose quand on l'apprend)(oui, encore un nouveau langage).
[^] # Re: Reprend les bases
Posté par nodens . Évalué à 3.
Moi je trouve que c'est une bonne question, y'a plein de raisons qui poussent à utiliser un shell en particulier dans des circonstances particulières.
Il ne va pas non plus passer plus de temps à poser la question et nous pondre un roman sur le contexte qu'il n'en a passé à écrire le script, alors que son problème n'est pas "réaliser telle opération", mais "contourner une limite des shell bourne", à savoir la création de sous-shell en cas de pipe... C'est typiquement le genre de question intelligente et dont les réponses peuvent servir à d'autres que j'aime voir dans les forums.
[^] # Re: Reprend les bases
Posté par benoar . Évalué à 2.
Après, c'était aussi un peu sous-entendu que je n'avais pas vraiment de solution dans le sens qu'il voulait /o\ (mais bon, un sous-shell créé pour un pipe c'est dans la définition même du fonctionnement d'unix, alors si tu veux contourner ça .... ça va être coton).
[^] # Re: Reprend les bases
Posté par nodens . Évalué à 3.
Pas nécessairement. Le problème vient plus du while des shells bourne que du pipe. avec ksh ou zsh, il n'y a qu'un sous-shell dans ce cas là, et pas un par itération. Du coup, pas de problème. Pas de chance, POSIX a choisi la façon d'agir de sh...
Cela dit, pour un contournement sans fichier temporaire, cf mon post plus bas :-)
[^] # Re: Reprend les bases
Posté par Kerro . Évalué à 2.
[^] # Re: Reprend les bases
Posté par NeoX . Évalué à 2.
#!/bin/sh
VAR=0
./script.sh | while read line
do
...
if [ ... ]; then
VAR=$(($VAR +1))
fi
done
echo $VAR
[^] # Re: Reprend les bases
Posté par nodens . Évalué à 2.
À ce compte là on peut aussi dire que ce problème ne se poserait pas avec ksh ou zsh, puisqu'ils ne créent pas de sous-shell pour les pipes.
Franchement la question est pertinente, le coup du pipe et des variables locales c'est tordu.
Répondre un STFW (ou un RTFM) c'est un peu rude.
[^] # Re: Reprend les bases
Posté par nodens . Évalué à 7.
BACKUPIFS=$IFS
IFS=$'\n'
for trucmuche in $($script); do
...
done
IFS=$BACKUPIFS
(note : si tu n'a pas modifié l'IFS avant, unset IFS est plus simple que la sauvegarde / restauration).
Bon cela dit, c'est pas toujours une bonne solution, parce que si tu as plus de résultats que ton shell ne prend d'arguments, ça foire... Le while read est donc une solution bien plus élégante.
Dans ce cas là, tu peux utiliser un pipe nommé : ça complexifie un peu, parce qu'il faut le créer (et donc le supprimer, comme un fichier temporaire). L'avantage, c'est que c'est juste un fichier device en mode caractère et donc qu'il n'y a pas d'écriture sur disque.
#/bin/dash
script="/bin/ls"
TMPDIR=$(mktemp -dt script.XXXXXX)
FIFO=$TMPDIR/script.pipe
trap "rm -rf $TMPDIR" EXIT
mkfifo $FIFO
$script > $FIFO &
COUNT=0
while read trucmuche; do
COUNT=$(($COUNT +1))
done < $FIFO
echo $COUNT
[^] # Re: Reprend les bases
Posté par Diagonale de Cantor (site web personnel) . Évalué à 2.
la pipe nommé semble être la meilleur solution.
[^] # Re: Reprend les bases
Posté par fasthm . Évalué à 1.
La gent féminine, pas la "gente", pas de "e" ! La gent féminine ! Et ça se prononce comme "gens". Pas "jante".
[^] # Re: Reprend les bases
Posté par benoar . Évalué à 2.
[^] # Re: Reprend les bases
Posté par Pascal Terjan (site web personnel) . Évalué à 3.
[^] # Re: Reprend les bases
Posté par Pascal Terjan (site web personnel) . Évalué à 2.
[^] # Re: Reprend les bases
Posté par totof2000 . Évalué à 4.
script.sh |awk '{ if (...) { VAR+=1} } END {print VAR }'
tu peux même faire mieux : script.sh |awk ' /regexp/ {VAR+=1} } END {print VAR }'
[^] # Re: Reprend les bases
Posté par benoar . Évalué à 2.
[^] # Re: Reprend les bases
Posté par nodens . Évalué à 2.
Par contre si l'essentiel du script se résumé à ça, c'est en effet une solution plus élégante que le pipe nommé (perso je suis une loutre en awk alors j'y pense rarement au dela du one-liner jetable).
[^] # Re: Reprend les bases
Posté par totof2000 . Évalué à 3.
Je suis même presque convaincu que tu peux remplacer avantageusement ton script.sh + le awk que j'ai fourni ci-dessus par un awk bien senti.
[^] # Re: Reprend les bases
Posté par totof2000 . Évalué à 2.
Awk, c'est bon, mangez-en ... J'ai même réussi à lui faire résoudre les grilles de Sudoku ... :)
# < <( script )
Posté par fasthm . Évalué à 1.
while read line
do
VAR=$(($VAR +1))
done < <( ls )
La gent féminine, pas la "gente", pas de "e" ! La gent féminine ! Et ça se prononce comme "gens". Pas "jante".
[^] # Re: < <( script )
Posté par Bruno Muller . Évalué à 5.
Faut lire les questions jusqu'au bout avant de répondre...
[^] # Re: < <( script )
Posté par fasthm . Évalué à 3.
La gent féminine, pas la "gente", pas de "e" ! La gent féminine ! Et ça se prononce comme "gens". Pas "jante".
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.