Dis, j'ai un fichier foo.txt
spécifiant des 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?
Dis, j'ai un fichier foo.txt
spécifiant des 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?
Réponses:
Si votre shell est bash (entre autres), un raccourci pour $(cat afile)
est $(< afile)
, donc vous écrirez:
mycommand "$(< file.txt)"
Documenté dans la page de manuel bash dans la section 'Command Substitution'.
Alternativement, faites lire votre commande à partir de stdin, donc: mycommand < file.txt
<
opérateur. Cela signifie que le shell lui-même effectuera la redirection plutôt que d'exécuter le cat
binaire pour ses propriétés de redirection.
"$(…)"
, le contenu de file.txt
serait passé à l'entrée standard de mycommand
, pas en tant qu'argument. "$(…)"
signifie exécuter la commande et restituer la sortie sous forme de chaîne ; ici la «commande» ne lit que le fichier mais cela pourrait être plus complexe.
Comme déjà mentionné, vous pouvez utiliser les backticks ou $(cat filename)
.
Ce qui n'a pas été mentionné, et je pense qu'il est important de le noter, c'est que vous devez vous rappeler que le shell divisera le contenu de ce fichier selon des espaces, 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 afin qu'il puisse contenir des espaces, des séquences d'échappement, etc., la lecture à partir 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 souhaitez extraire chaque ligne comme argument, utilisez la construction while / read / do:
while read i ; do command_name $i ; done < filename
read -r
moins que vous ne souhaitiez étendre les séquences d'échappement anti-slash - et NUL est un délimiteur plus sûr à utiliser que le saut de ligne, en particulier si les arguments que vous passez sont des choses comme des noms de fichiers, qui peuvent contenir des sauts de ligne littéraux. De plus, sans effacer IFS, les espaces de début et de fin sont supprimés implicitement i
.
command `< file`
transmettra le contenu du fichier à la commande sur stdin, mais supprimera les nouvelles lignes, ce qui signifie que vous ne pouvez pas parcourir chaque ligne individuellement. Pour cela, vous pouvez écrire un script avec une boucle 'for':
for line in `cat input_file`; do some_command "$line"; done
Ou (la variante multiligne):
for line in `cat input_file`
do
some_command "$line"
done
Ou (variante multiligne avec $()
au lieu de ``
):
for line in $(cat input_file)
do
some_command "$line"
done
< file
backticks signifie qu'il n'effectue pas du tout de redirection command
.
command `< file`
Ne fonctionne ni dans bash ni dans POSIX sh; zsh est une autre affaire, mais ce n'est pas le shell sur lequel porte cette question).
Hello World
sur une ligne. Vous le verrez d'abord s'exécuter some_command Hello
, alors some_command World
, pas some_command "Hello World"
ou some_command Hello World
.
Vous faites cela en utilisant des backticks:
echo World > file.txt
echo Hello `cat file.txt`
echo "Hello * Starry * World" > file.txt
dans la première étape, vous obtiendrez au moins quatre arguments séparés passés à la deuxième commande - et probablement plus, car le *
s s'étendrait aux noms des fichiers présents dans le répertoire courant.
fork()
en dehors d'un sous-shell avec un FIFO attaché à son stdout, puis en l'appelant en /bin/cat
tant qu'enfant de ce sous-shell, puis en lisant la sortie via le FIFO; compare to $(<file.txt)
, qui lit le contenu du fichier dans bash directement sans sous-shell ou FIFO impliqué.
Si vous souhaitez le faire d'une manière robuste qui fonctionne pour tous les arguments de ligne de commande possibles (valeurs avec espaces, valeurs avec sauts de ligne, valeurs avec guillemets littéraux, valeurs non imprimables, valeurs avec caractères glob, etc.), il obtient un peu plus intéressant.
Pour écrire dans un fichier, étant donné un tableau d'arguments:
printf '%s\0' "${arguments[@]}" >file
... remplacer par "argument one"
, "argument two"
, etc. , selon le cas.
Pour lire ce fichier et utiliser son contenu (dans bash, ksh93 ou dans un autre shell récent avec des tableaux):
declare -a args=()
while IFS='' read -r -d '' item; do
args+=( "$item" )
done <file
run_your_command "${args[@]}"
Pour lire à partir de ce fichier et utiliser son contenu (dans un shell sans tableaux; notez que cela écrasera votre liste d'arguments de ligne de commande locale, et qu'il est donc préférable de le faire à l'intérieur d'une fonction, de sorte que vous écrasiez les arguments de la fonction et non la liste globale):
set --
while IFS='' read -r -d '' item; do
set -- "$@" "$item"
done <file
run_your_command "$@"
Notez que -d
(autoriser l'utilisation d'un délimiteur de fin de ligne différent) est une extension non POSIX, et un shell sans tableaux peut également ne pas la prendre en charge. Si tel est le cas, vous devrez peut-être utiliser un langage non-shell pour transformer le contenu délimité par NUL en une eval
forme -safe:
quoted_list() {
## Works with either Python 2.x or 3.x
python -c '
import sys, pipes, shlex
quote = pipes.quote if hasattr(pipes, "quote") else shlex.quote
print(" ".join([quote(s) for s in sys.stdin.read().split("\0")][:-1]))
'
}
eval "set -- $(quoted_list <file)"
run_your_command "$@"
Voici comment je passe le contenu d'un fichier comme argument à une commande:
./foo --bar "$(cat ./bar.txt)"
$(<bar.txt)
(lors de l'utilisation d'un shell, tel que bash, avec l'extension appropriée). $(<foo)
est un cas particulier: contrairement à la substitution de commandes régulière, telle qu'utilisée dans $(cat ...)
, elle ne crée pas un sous-shell pour fonctionner, et évite ainsi une surcharge importante.
Si tout ce que vous avez à faire est de tourner le fichier arguments.txt
avec le contenu
arg1
arg2
argN
en my_command arg1 arg2 argN
vous pouvez simplement utiliser xargs
:
xargs -a arguments.txt my_command
Vous pouvez mettre des arguments statiques supplémentaires dans l' xargs
appel, comme celui xargs -a arguments.txt my_command staticArg
qui appelleramy_command staticArg arg1 arg2 argN
Dans ma coquille 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 évident, cela vous permet d'exécuter plusieurs commandes avec chaque ligne de input_file, une jolie petite astuce que j'ai apprise ici .
$(somecommand)
, vous obtiendrez cette commande exécutée plutôt que transmise sous forme de texte. De même, >/etc/passwd
sera traité comme une redirection et un écrasement /etc/passwd
(si exécuté avec les autorisations appropriées), etc.
xargs -d $'\n' sh -c 'for arg; do command1 "$arg"; command2 "arg"; command3 "arg"; done' _
- et aussi plus efficace, car il transmet autant d'arguments à chaque shell que possible plutôt que de démarrer un shell par ligne dans votre fichier d'entrée.
cat
Après avoir édité la réponse de @Wesley Rice à quelques reprises, j'ai décidé que mes changements devenaient trop importants pour continuer à changer sa réponse au lieu d'écrire la mienne. Alors, j'ai décidé que je devais écrire le mien!
Lisez chaque ligne d'un fichier et opérez-la ligne par ligne comme ceci:
#!/bin/bash
input="/path/to/txt/file"
while IFS= read -r line
do
echo "$line"
done < "$input"
Cela vient directement de l'auteur Vivek Gite ici: https://www.cyberciti.biz/faq/unix-howto-read-line-by-line-from-file/ . Il en a le mérite!
Syntaxe: Lire un fichier ligne par ligne sur un shell Bash Unix & Linux:
1. La syntaxe est la suivante pour bash, ksh, zsh et tous les autres shells pour lire un fichier ligne par ligne
2.while read -r line; do COMMAND; done < input.file
3. L'-r
option passée à la commande read empêche l'interprétation des échappements antislash.
4. Ajoutez uneIFS=
option avant la commande de lecture pour éviter que les espaces de début / fin ne soient coupés -
5.while IFS= read -r line; do COMMAND_on $line; done < input.file
Et maintenant pour répondre à cette question maintenant fermée que j'avais aussi: est-il possible de `git add` une liste de fichiers à partir d'un fichier? - voici ma réponse:
Notez qu'il FILES_STAGED
s'agit d'une variable contenant le chemin absolu vers un fichier qui contient un tas de lignes où chaque ligne est un chemin relatif vers un fichier sur lequel j'aimerais faire git add
. Cet extrait de code est sur le point de faire partie du fichier «eRCaGuy_dotfiles / useful_scripts / sync_git_repo_to_build_machine.sh» de ce projet, pour permettre une synchronisation facile des fichiers en développement d'un PC (ex: un ordinateur sur lequel je code) à un autre (ex: a ordinateur plus puissant sur lequel je construis): https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles .
while IFS= read -r line
do
echo " git add \"$line\""
git add "$line"
done < "$FILES_STAGED"
git add
dessus: Est-il possible de `git add` une liste de fichiers à partir d'un fichier?