web-dev-qa-db-fra.com

Comment lancez-vous une commande pour chaque ligne d'un fichier?

Par exemple, pour le moment, j'utilise ce qui suit pour modifier quelques fichiers dont les chemins Unix ont été écrits dans un fichier:

cat file.txt | while read in; do chmod 755 "$in"; done

Y a-t-il un moyen plus élégant et plus sûr?

112
hawk

Si votre fichier n'est pas trop gros et que tous les fichiers sont bien nommés (sans espaces ni autres caractères spéciaux comme des guillemets), vous pourriez simplement:

chmod 755 $(<file.txt)

Si vous avez des caractères spéciaux et/ou beaucoup de lignes dans file.txt.

xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)

si votre commande doit être exécutée exactement 1 fois par entrée:

xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)

Ceci n'est pas nécessaire pour cet exemple, car chmod accepte plusieurs fichiers en tant qu'argument, mais cela correspond au titre de la question.

Dans certains cas particuliers, vous pouvez même définir l'emplacement de l'argument de fichier dans les commandes générées par xargs:

xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
79
F. Hauri

Oui.

while read in; do chmod 755 "$in"; done < file.txt

De cette façon, vous pouvez éviter un processus cat.

cat est presque toujours mauvais pour un objectif comme celui-ci. Vous pouvez en savoir plus sur Utilisation inutile de Cat.

120
P.P.

Si vous savez que vous n'avez aucun espace dans l'entrée:

xargs chmod 755 < file.txt

S'il peut y avoir des espaces dans les chemins et si vous avez GNU xargs:

tr '\n' '\0' < file.txt | xargs -0 chmod 755
13
glenn jackman

si vous avez un sélecteur Nice (par exemple tous les fichiers .txt dans un répertoire), vous pouvez faire:

for i in *.txt; do chmod 755 "$i"; done

bash pour la boucle

ou une variante de votre part: 

while read line; do chmod 755 "$line"; done <file.txt
11
d.raev

Si vous voulez exécuter votre commande en parallèle pour chaque ligne, vous pouvez utiliser GNU Parallel

parallel -a <your file> <program>

Chaque ligne de votre fichier sera transmise au programme en tant qu’argument. Par défaut, parallel exécute autant de threads que votre CPU compte. Mais vous pouvez le spécifier avec -j

10
janisz

Je vois que vous avez marqué bash, mais Perl serait aussi un bon moyen de faire ceci:

Perl -p -e '`chmod 755 $_`' file.txt

Vous pouvez également appliquer une expression régulière pour vous assurer d'obtenir les bons fichiers, par exemple. traiter uniquement les fichiers .txt:

Perl -p -e 'if(/\.txt$/) `chmod 755 $_`' file.txt

Pour "prévisualiser" ce qui se passe, remplacez simplement les guillemets par des guillemets et ajoutez le préfixe print:

Perl -p -e 'if(/\.txt$/) print "chmod 755 $_"' file.txt
2
1.618

Vous pouvez également utiliser AWK, ce qui peut vous donner plus de flexibilité pour gérer le fichier.

awk '{ print "chmod 755 "$0"" | "/bin/sh"}' file.txt

si votre fichier a un séparateur de champ comme:

champ1, champ2, champ3

Pour obtenir uniquement le premier champ que vous faites

awk -F, '{ print "chmod 755 "$1"" | "/bin/sh"}' file.txt

Vous pouvez vérifier plus de détails sur GNU Documentation https://www.gnu.org/software/gawk/manual/html_node/Very-Simple.html#Very-Simple

0
brunocrt

La logique s'applique à de nombreux autres objectifs. Et comment lire .sh_history de chaque utilisateur à partir de/home/système de fichiers? Et s'il y en avait des milliers?

#!/bin/ksh
last |head -10|awk '{print $1}'|
 while IFS= read -r line
 do
su - "$line" -c 'tail .sh_history'
 done

Voici le script https://github.com/imvieira/SysAdmin_DevOps_Scripts/blob/master/get_and_run.sh

0
igor.monteiro