web-dev-qa-db-fra.com

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

Je cherche à écrire un script qui prend un .txt filename comme argument, lit le fichier ligne par ligne et passe chaque ligne à une commande. Par exemple, il exécute command --option "LINE 1", puis command --option "LINE 2", etc. La sortie de la commande est écrite dans un autre fichier. Comment dois-je procéder? Je ne sais pas où commencer.

44
WojciechF

Utilisation while read boucle:

: > 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

La dernière 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 fd pour l'entrée afin que les commandes ne le mangent pas (en supposant ici ksh, zsh ou bash pour -u 3, utilisation <&3 à la place de manière 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: IFS= peut être omis si cela ne vous dérange pas de supprimer les valeurs de ligne des espaces de début et de fin.

28
konsolebox

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.

Les autres xargs n'ont pas -d, mais certains ont -0 pour une 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 (-I est facultatif dans POSIX et n'est requis que pour les systèmes conformes à Unix), vous devez prétraiter l'entrée pour quote les lignes au format attendu par xargs:

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

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

29
ctrl-alt-delor

En restant 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
9
miigotu

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

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

ÉDITER:

... 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
5
Steffen Roller
    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

PRODUCTION

--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
2
mikeserv