web-dev-qa-db-fra.com

Ecrivez un script Shell qui déplacera tous les fichiers du répertoire actuel contenant le mot "hello" dans un dossier séparé appelé "hello-world"

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?

1
carlox warrior

Divisons cela en tâches plus petites. Nous devons le faire

  • créer un répertoire hello-world, s'il n'existe pas
  • identifier de manière fiable les fichiers contenant le texte hello
  • déplacer de manière fiable les fichiers dans le nouveau répertoire.

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.

4
Zanna

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
0
muclux