227 votes

Comment utiliser les lignes d'un fichier comme arguments d'une commande ?

Disons que j'ai un fichier foo.txt spécifiant N arguments

arg1
arg2
...
argN

que je dois passer à la commande my_command

Comment utiliser les lignes d'un fichier comme arguments d'une commande ?

16 votes

"arguments", au pluriel, ou "argument", au singulier ? La réponse acceptée n'est correcte que dans le cas d'un seul argument -- ce sur quoi le corps du texte s'interroge -- mais pas dans le cas de plusieurs arguments, sur lequel le sujet semble s'interroger.

0 votes

Les 4 réponses ci-dessous donnent généralement des résultats identiques, mais elles ont une sémantique légèrement différente. Maintenant je me souviens pourquoi j'ai arrêté d'écrire des bash scripts :P

304voto

glenn jackman Points 69748

Si votre shell est bash (entre autres), un raccourci pour $(cat afile) es $(< afile) alors vous écrivez :

mycommand "$(< file.txt)"

Documenté dans la page du manuel bash dans la section 'Substitution de commande'.

Autrement, faites en sorte que votre commande lise depuis stdin, donc : mycommand < file.txt

24 votes

Pour être pédant, il ne s'agit pas d'un raccourci pour utiliser la fonction < opérateur. Cela signifie que le shell lui-même effectuera la redirection plutôt que d'exécuter l'opérateur cat binaire pour ses propriétés de redirection.

0 votes

Y a-t-il une limite au nombre d'arguments ? Si oui, comment puis-je la surmonter ?

2 votes

C'est un paramètre du noyau, il faut donc recompiler le noyau. Ou étudiez la commande xargs

45voto

Will Points 1164

Comme déjà mentionné, vous pouvez utiliser les backticks ou les $(cat filename) .

Ce qui n'a pas été mentionné, et qu'il me semble important de noter, c'est que vous devez vous rappeler que l'interpréteur de commandes décomposera le contenu de ce fichier en fonction des espaces blancs, en donnant chaque "mot" qu'il trouve à votre commande comme argument. Et bien que vous puissiez mettre un argument de ligne de commande entre guillemets pour qu'il puisse contenir des espaces, des séquences d'échappement, etc., la lecture du fichier ne fera pas la même chose. Par exemple, si votre fichier contient :

a "b c" d

les arguments que vous obtiendrez sont :

a
"b
c"
d

Si vous voulez tirer chaque ligne comme un argument, utilisez la construction while/read/do :

while read i ; do command_name $i ; done < filename

0 votes

J'aurais dû préciser que je suppose que vous utilisez bash. Je réalise qu'il existe d'autres shells, mais presque toutes les machines *nix sur lesquelles j'ai travaillé utilisaient bash ou un équivalent. IIRC, cette syntaxe devrait fonctionner de la même manière avec ksh et zsh.

1 votes

La lecture doit être read -r à moins que vous ne vouliez étendre les séquences backslash-escape -- et NUL est un délimiteur plus sûr à utiliser que la nouvelle ligne, particulièrement si les arguments que vous passez sont des choses comme des noms de fichiers, qui peuvent contenir des nouvelles lignes littérales. De plus, sans effacer IFS, vous obtenez l'espace blanc de tête et de queue implicitement effacé de i .

32voto

Wesley Rice Points 963
command `< file`

transmettra le contenu du fichier à la commande sur stdin, mais supprimera les nouvelles lignes, ce qui signifie que vous ne pourrez pas itérer sur chaque ligne individuellement. Pour cela, vous pourriez écrire un script avec une boucle 'for' :

for line in `cat input_file`; do some_command "$line"; done

Ou (la variante multi-lignes) :

for line in `cat input_file`
do
    some_command "$line"
done

Ou (variante à plusieurs lignes avec $() au lieu de `` ):

for line in $(cat input_file)
do
    some_command "$line"
done

Références :

  1. Syntaxe de la boucle for : https://www.cyberciti.biz/faq/bash-for-loop/

0 votes

Aussi, mettre < file entre guillemets signifie qu'il n'effectue pas réellement de redirection pour command du tout.

4 votes

(Les personnes qui ont voté pour cette proposition l'ont-elles réellement testée ? command `< file` ne fonctionne pas dans bash ou POSIX sh ; zsh est une autre affaire, mais ce n'est pas le shell sur lequel porte cette question).

0 votes

@CharlesDuffy Pouvez-vous être plus précis sur la façon dont cela ne fonctionne pas ?

16voto

Tommy Lacroix Points 703

Vous faites cela en utilisant des backticks :

echo World > file.txt
echo Hello `cat file.txt`

4 votes

Cela ne crée pas un parce qu'il n'est pas cité, il est sujet à la séparation des chaînes de caractères, donc si vous émettez echo "Hello * Starry * World" > file.txt dans la première étape, vous obtiendrez au moins quatre arguments distincts transmis à la deuxième commande - et probablement plus, puisque le paramètre * s s'étendrait aux noms des fichiers présents dans le répertoire courant.

3 votes

...et parce qu'il utilise l'expansion de glob, il n'émet pas de exactement ce qu'il y a dans le dossier. Et parce qu'il effectue une opération de substitution de commande, c'est extrêmement inefficace - c'est en réalité fork() en lançant un sous-shell avec une FIFO attachée à son stdout, puis en invoquant /bin/cat en tant qu'enfant de ce sous-shell, puis lire la sortie à travers la FIFO ; comparer à $(<file.txt) qui lit le contenu du fichier dans bash directement, sans sous-shells ou FIFOs.

2voto

honkaboy Points 131

Dans mon shell bash, ce qui suit a fonctionné comme un charme :

cat input_file | xargs -I % sh -c 'command1 %; command2 %; command3 %;'

où input_file est

arg1
arg2
arg3

Comme il est évident, cela vous permet d'exécuter plusieurs commandes avec chaque ligne du fichier d'entrée, un petit truc sympa que j'ai appris. aquí .

3 votes

Votre "joli petit truc" est dangereux du point de vue de la sécurité -- si vous avez un argument contenant $(somecommand) vous obtiendrez l'exécution de cette commande plutôt que son passage en tant que texte. De même, >/etc/passwd sera traité comme une redirection et écrasera /etc/passwd (s'il est exécuté avec les permissions appropriées), etc.

2 votes

Il est beaucoup plus sûr de faire ce qui suit à la place (sur un système avec des extensions GNU) : xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _ -- et aussi plus efficace, puisqu'il passe autant d'arguments à chaque shell que possible plutôt que de démarrer un shell par ligne dans votre fichier d'entrée.

0 votes

Et bien sûr, perdre le utilisation inutile de cat

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X