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 ?
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 ?
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
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.
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
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.
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
.
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
Aussi, mettre < file
entre guillemets signifie qu'il n'effectue pas réellement de redirection pour command
du tout.
(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).
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.
...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.
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í .
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.
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.
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.
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