Ecrivez un script Shell qui déplacera tous les fichiers du répertoire en cours contenant le mot _ hello
dans un dossier distinct appelé hello-world
.
Le répertoire en cours contient les fichiers a b c d e
et f
. Les fichiers a
, c
et f
contiennent le mot hello
.
Comment puis-je rechercher le mot hello
dans les fichiers du répertoire actuel et déplacer les fichiers satisfaisant la condition dans un autre répertoire hello-world
?
Divisons cela en tâches plus petites. Nous devons le faire
hello-world
, s'il n'existe pashello
La création d'un répertoire se fait avec mkdir
. Il refusera toujours de créer un répertoire existant (refusant ainsi d'écraser de tels répertoires), mais il possède également un indicateur -p
qui, comme indiqué dans man mkdir
-p, --parents
no error if existing, make parent directories as needed
ne vous dérange pas si le répertoire que vous essayez de créer existe déjà.
Donc, la première commande de notre script pourrait être (vérifiez que vous êtes d’abord dans le bon répertoire).
mkdir -p hello-world
La deuxième étape consiste à identifier les fichiers que nous voulons. La commande la plus évidente pour rechercher du texte dans des fichiers est grep
. Si nous ne voulions que les noms de fichiers des fichiers contenant hello
, nous pouvons utiliser le drapeau -l
qui, selon man grep
-l, --files-with-matches
Suppress normal output; instead print the name of each input file
from which output would normally have been printed. The scanning
will stop on the first match.
supprime la sortie normale. Mais, bien que nous puissions nous en tenir à votre exemple, nous ne voulons vraiment pas les noms de fichiers en sortie, car le résultat d’une commande n’est que du texte, et les noms de fichiers peuvent contenir toutes sortes de caractères (du plus humble espace à l’exotique newline) qui fera que le shell verra quelque chose de différent du nom réel du fichier auquel vous voulez faire quelque chose et se comportera d’une manière que vous ne voudriez pas. Par exemple, après avoir configuré un répertoire de test pour votre script, si j'ajoute un fichier avec un espace dans son nom, voyez ce qui se passe:
$ echo hello > with\ space
$ ls
a b c d e f with space
$ for i in $(grep -l hello *); do echo "$i"; done
a
c
f
with
space
Le fichier with space
est traité comme deux fichiers distincts.
Pour identifier les fichiers afin que le shell les manipule, nous devons éviter d'analyser les noms de fichiers. Nous pourrions tester chaque fichier, voir s'il contient le texte et, le cas échéant, le déplacer. Pour boucler sur les fichiers, comme indiqué ci-dessus, nous pouvons utiliser for
dont la syntaxe est la suivante:
for var in things; do stuff $var; done
Dans une boucle for
(et n'importe où ailleurs), vous pouvez utiliser la commande test
, qui ressemble parfois à [
et qui ressemble également à [[
pour faire des instructions conditionnelles, et si vous vouliez seulement vérifier si le fichier était vide ou non, ou s'il était d'un type particulier, je vous conseillerais d'utiliser la commande test
.
Mais vous voulez trouver un texte particulier, nous devons donc appeler grep
, mais nous voulons seulement savoir si la recherche de hello
dans le fichier a réussi. Nous savons donc que nous devons faire quelque chose. avec le fichier.
Pour faire une déclaration conditionnelle basée sur le succès d'une commande, nous pouvons utiliser if
. if
est souvent utilisé avec test
/[
/[[
, mais vous n'en avez pas besoin, car grep
a un autre indicateur utile:
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately
with zero status if any match is found, even if an error was detected.
Zero in Bash est synonyme de réussite. Donc, pour faire ce que nous voulons, nous pourrions écrire quelque chose comme
if grep -q hello file; then mv file hello-world; fi
(le fi
fait partie de la syntaxe de la commande if
. Il indique au Shell que vous avez terminé votre if
)
J'écris habituellement des scripts de shell dans un shell interactif et ne les scriptifie que dans un fichier ultérieurement, car je suis paresseux et je n'écris que des scripts très triviaux. Quoi qu'il en soit, vous pouvez écrire un court script sous la forme d'une commande d'une ligne en séparant les commandes avec ;
. Si quelque chose ne va pas, appuyez sur la flèche vers le haut pour modifier la dernière commande ...
Nous ne voulons pas exécuter cette commande une fois pour chaque fichier. Cela éliminerait l’objet d’écrire un script pour faire le travail et nous épargnerait la frappe, donc pour faire une boucle sur les fichiers, nous pouvons utiliser for
. Pour éviter d'obtenir une erreur de grep
à propos du répertoire hello-world
, nous pouvons ajouter un autre indicateur pour l'exclure.
Voici ma commande de test pour effectuer ce travail et la sortie que j'en tire dans mon environnement de test:
$ mkdir -p hello-world; for file in *; do if grep -q --exclude-dir=hello-world -- hello "$file"; then echo mv -v -- "$file" hello-world; fi; done
mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
mv -v -- with space hello-world
Ce script ne déplace aucun fichier, à cause de echo
avant mv
qui indique quelle commande sera exécutée à chaque itération de la boucle. Supprimez echo
après avoir vérifié si les commandes sont correctes (notez que vous ne pouvez pas toujours utiliser echo
de manière fiable, et vous devrez peut-être introduire des guillemets temporaires pour le faire, mais cela fonctionne très bien ici). .
*
correspond à tous les fichiers non cachés du répertoire en cours. Il est plus sûr d'utiliser ./*
qui signifie la même chose, mais veille à ce que tous les chemins commencent par ./
(qui est l'adresse du répertoire de travail actuel .
), en empêchant les noms de fichiers commençant par -
en cours d'interprétation. Nous avons traité cette possibilité en ajoutant --
à grep
et à la commande mv
pour indiquer la fin des options. -v
est le drapeau prolixe de mv
.
Nous devrions citer des variables pour supprimer les extensions du shell. Si je supprime les guillemets autour de "$file"
, ma sortie est
mv -v -- a hello-world
mv -v -- c hello-world
mv -v -- f hello-world
grep: with: No such file or directory
grep: space: No such file or directory
Voici le script en tant que script:
#!/bin/bash
mkdir -p hello-world
for file in *; do
if grep -q --exclude-dir=hello-world -- hello "$file"; then
echo mv -v -- "$file" hello-world
fi
done
N'oubliez pas de supprimer echo
lorsque vous êtes prêt à déplacer les fichiers pour de vrai.
PS, il pourrait bien y avoir des moyens plus efficaces de le faire. Mon chemin n'est qu'un exemple.
Ce serait une solution suivant votre idée avec grep et xargs:
#!/bin/bash
mkdir hello-world
grep -l hello * | xargs mv -t hello-world