Comment passer chaque ligne d'un fichier texte en argument d'une commande?

42

Je cherche à écrire un script qui prend un .txtnom de fichier comme argument, lit le fichier ligne par ligne et transmet chaque ligne à une commande. Par exemple, il s'exécute command --option "LINE 1", puis command --option "LINE 2", etc. Le résultat de la commande est écrit dans un autre fichier. Comment puis-je faire cela? Je ne sais pas où commencer.

WojciechF
la source

Réponses:

26

Utilisez la while readboucle:

: > another_file  ## Truncate file.

while IFS= read -r LINE; do
    command --option "$LINE" >> another_file
done < file

Une autre consiste à rediriger la sortie par bloc:

while IFS= read -r LINE; do
    command --option "$LINE"
done < file > another_file

Le dernier consiste à ouvrir le fichier:

exec 4> another_file

while IFS= read -r LINE; do
    command --option "$LINE" >&4
    echo xyz  ## Another optional command that sends output to stdout.
done < file

Si l'une des commandes lit l'entrée, ce serait une bonne idée d'utiliser un autre fichier fd pour que l'entrée ne le mange pas (ici en supposant ksh, zshou bashpour -u 3, utilisez <&3plutôt portable):

while IFS= read -ru 3 LINE; do
    ...
done 3< file

Enfin pour accepter les arguments, vous pouvez faire:

#!/bin/bash

FILE=$1
ANOTHER_FILE=$2

exec 4> "$ANOTHER_FILE"

while IFS= read -ru 3 LINE; do
    command --option "$LINE" >&4
done 3< "$FILE"

Lequel pourrait fonctionner comme:

bash script.sh file another_file

Idée supplémentaire. Avec bash, utilisez readarray:

readarray -t LINES < "$FILE"

for LINE in "${LINES[@]}"; do
    ...
done

Remarque: vous IFS=pouvez l'omettre si vous ne craignez pas de supprimer les valeurs de ligne des espaces de début et de fin.

Konsolebox
la source
28

Une autre option est xargs.

Avec GNU xargs:

< file xargs -I{} -d'\n' command --option {} other args

{} est l'espace réservé pour la ligne de texte.

D'autres xargsn'ont pas -d, mais certains ont -0pour l'entrée délimitée par NUL. Avec ceux-ci, vous pouvez faire:

< file tr '\n' '\0' | xargs -0 -I{} command --option {} other args

Sur les systèmes conformes à Unix ( -Ifacultatif dans POSIX et requis uniquement pour les systèmes conformes à Unix), vous devez prétraiter l'entrée pour indiquer les lignes dans le format attendu par xargs:

< file sed 's/"/"\\""/g;s/.*/"&"/' |
  xargs -E '' -I{} command --option {} other args

Notez cependant que certaines xargsimplémentations ont une limite très basse sur la taille maximale de l'argument (255 sur Solaris par exemple, le minimum autorisé par la spécification Unix).

ctrl-alt-delor
la source
réductible à<file xargs -L 1 -I{} command --option {} other args
iruvar
@iruvar, non, cela traiterait les guillemets et supprimerait les blancs.
Stéphane Chazelas
7

Garder précisément à la question:

#!/bin/bash

# xargs -n param sets how many lines from the input to send to the command

# Call command once per line
[[ -f $1 ]] && cat $1 | xargs -n1 command --option

# Call command with 2 lines as args, such as an openvpn password file
# [[ -f $1 ]] && cat $1 | xargs -n2 command --option

# Call command with all lines as args
# [[ -f $1 ]] && cat $1 | xargs command --option
miigotu
la source
1
a travaillé comme un charme :-)
BugHunter
3

La meilleure réponse que j'ai trouvée est:

for i in `cat`; do "$cmd" "$i"; done < $file

MODIFIER:

... quatre ans plus tard ...

après plusieurs votes négatifs et un peu plus d'expérience, je recommanderais maintenant ce qui suit

xargs -l COMMAND < file
Steffen Roller
la source
3
Cela invoquera la commande une fois pour chaque mot du fichier. En outre, vous devriez toujours citer des références aux variables du shell (comme dans do "$cmd" "$i";), sauf si vous avez une raison de ne pas le faire; si le fichier contient un *mot en tant que mot seul, votre code sera exécuté $cmd *, ce qui, bien sûr, exécutera la commande avec une liste des fichiers du répertoire en cours.
G-Man dit 'Réintégrez Monica' le
1
@ G-Man, sauf dans zsh, le `cat`développerait déjà le *(le non cité $ipourrait encore développer un caractère générique (un second tour) si le développement de `cat`introduit certains). En tout cas, cette approche est en effet fausse.
Stéphane Chazelas
1
+1 Cela fonctionnera correctement si chaque ligne ne contient que l’entrée nécessaire à la commande utilisée sans espaces.
Subin Sebastian
Cela agit sur chaque mot, existe-t-il une variante qui agit sur chaque ligne?
thebunnyrules
La question était de traiter une liste de noms de fichiers ligne par ligne.
Steffen Roller le
2
    sed "s/'/'\\\\''/g;s/.*/\$* '&'/" <<\FILE |\
    sh -s -- command echo --option
all of the{&}se li$n\es 'are safely shell
quoted and handed to command as its last argument
following --option, and, here, before that echo
FILE

SORTIE

--option all of the{&}se li$n\es 'are safely shell
--option quoted and handed to command as its last argument
--option following --option, and, here, before that echo
Mikeserv
la source
-2
ed file.txt
%g/^/s// /
2,$g/^/-,.j
1s/^/command/
wq
chmod 755 file.txt
./file.txt

Prenez toutes les lignes d’un fichier et transmettez-les en tant qu’arguments à une seule commande, c.-à-d.

command line1 line2 line3 ....

Si vous avez besoin que l' --optionindicateur précède chaque ligne, modifiez la deuxième commande en:

%g/^/s// --option /
utilisateur2217522
la source
1
Qu'est-ce que -1 pour utiliser ed ... vraiment?
user2217522
3
Je ne vous ai pas voté vers le bas, mais la personne qui l'a fait avait probablement ces raisons: (1) Cela ne fait pas ce que la question demande. Cela appelle la commande une fois avec tout le contenu du fichier sur la ligne de commande; plutôt qu'une fois par ligne . (2) Cela ne fait rien pour gérer les caractères spéciaux du shell (qui peuvent être dans le fichier); par exemple, ', ", <, >, ;, etc. (3) Cela crée un fichier temporaire inutile. (4) Ce genre de chose se fait généralement avec «ici des documents». (5) Vos edordres sont maladroits; les deux premières commandes peuvent être réduites à %s/^/ /et %j.
G-Man dit 'Réintégrez Monica' le