Par exemple, en ce moment, j'utilise ce qui suit pour modifier quelques fichiers dont les chemins Unix ont été écrits dans un fichier :
cat file.txt | while read in; do chmod 755 "$in"; done
Existe-t-il un moyen plus élégant et plus sûr ?
Par exemple, en ce moment, j'utilise ce qui suit pour modifier quelques fichiers dont les chemins Unix ont été écrits dans un fichier :
cat file.txt | while read in; do chmod 755 "$in"; done
Existe-t-il un moyen plus élégant et plus sûr ?
C'est parce qu'il n'y a pas qu'une seule réponse...
xargs
outil dédiéwhile read
avec quelques remarqueswhile read -u
en utilisant fd
pour interactive traitement (échantillon)En ce qui concerne la demande de l'OP : en cours d'exécution chmod
sur toutes les cibles listées dans le fichier , xargs
est l'outil indiqué. Mais pour d'autres applications, de petites quantités de fichiers, etc...
Si votre fichier n'est pas trop gros et que tous les fichiers sont bien nommé (sans espaces ou autres caractères spéciaux comme les guillemets), vous pouvez utiliser l'interpréteur de commandes expansion de la ligne de commande . Tout simplement :
chmod 755 $(<file.txt)
Pour de petites quantités de fichiers (lignes), cette commande est la plus légère.
xargs
est le bon outilPour une plus grande quantité de fichiers, ou presque cualquier nombre de lignes dans votre fichier d'entrée...
Pour beaucoup binutils des outils, comme chown
, chmod
, rm
, cp -t
...
xargs chmod 755 <file.txt
Si vous avez des caractères spéciaux et/ou un grand nombre de lignes dans le fichier file.txt
.
xargs -0 chmod 755 < <(tr \\n \\0 <file.txt)
Si votre commande doit être exécutée exactement 1 fois pour chaque entrée :
xargs -0 -n 1 chmod 755 < <(tr \\n \\0 <file.txt)
Ceci n'est pas nécessaire pour cet échantillon, car chmod
accepte plusieurs fichiers comme arguments, mais cela correspond au titre de la question.
Pour certains cas particuliers, vous pourriez même définir l'emplacement de l'argument fichier dans les commandes générées par xargs
:
xargs -0 -I '{}' -n 1 myWrapper -arg1 -file='{}' wrapCmd < <(tr \\n \\0 <file.txt)
seq 1 5
comme entréeEssayez ça :
xargs -n 1 -I{} echo Blah {} blabla {}.. < <(seq 1 5)
Blah 1 blabla 1..
Blah 2 blabla 2..
Blah 3 blabla 3..
Blah 4 blabla 4..
Blah 5 blabla 5..
où votre commande est exécutée une fois par ligne.
while read
et ses variantes.Comme le suggère l'OP,
cat file.txt |
while read in; do
chmod 755 "$in"
done
fonctionnera, mais il y a deux problèmes :
cat |
es un fourchette inutile et
| while ... ;done
deviendra un Sous-coque dont l'environnement disparaîtra après ;done
.
Donc ça pourrait être mieux écrit :
while read in; do
chmod 755 "$in"
done < file.txt
Mais
$IFS
y read
drapeaux :help read
read: read [-r] ... [-d delim] ... [name ...] ... Reads a single line from the standard input... The line is split into fields as with word splitting, and the first word is assigned to the first NAME, the second word to the second NAME, and so on... Only the characters found in $IFS are recognized as word delimiters. ... Options: ... -d delim continue until the first character of DELIM is read, rather than newline ... -r do not allow backslashes to escape any characters ... Exit Status: The return code is zero, unless end-of-file is encountered...
Dans certains cas, vous pouvez avoir besoin d'utiliser
while IFS= read -r in;do
chmod 755 "$in"
done <file.txt
pour éviter les problèmes avec les noms de fichiers étranges. Et peut-être si vous rencontrez des problèmes avec UTF-8 :
while LANG=C IFS= read -r in ; do
chmod 755 "$in"
done <file.txt
Si vous utilisez une redirection depuis l'entrée standard for reading
file.txt`, votre script ne peut pas lire les autres entrées de manière interactive (vous ne pouvez plus utiliser l'entrée standard pour les autres entrées).
while read
en utilisant des fd
.Syntaxe : while read ...;done <file.txt
redirigera l'entrée standard pour qu'elle provienne de file.txt
. Cela signifie que vous ne pourrez pas vous occuper des processus jusqu'à ce qu'ils soient terminés.
Cela vous permettra d'utiliser plus d'une entrée simultanément, vous pourriez fusionner deux fichiers (comme ici : scriptReplay.sh ), ou peut-être :
Vous envisagez de créer un interactive vous devez éviter d'utiliser l'entrée standard et utiliser un autre descripteur de fichier.
Les descripteurs de fichiers constants sont :
Vous pouviez les voir en passant :
ls -l /dev/fd/
ou
ls -l /proc/$$/fd/
À partir de là, vous devez choisir des nombres inutilisés entre 0 et 63 (plus, en fait, en fonction de l'âge de la personne). sysctl
superuser tool) comme descripteur de fichier.
Pour cette démo, je vais utiliser le descripteur de fichier 7 :
while read <&7 filename; do
ans=
while [ -z "$ans" ]; do
read -p "Process file '$filename' (y/n)? " foo
[ "$foo" ] && [ -z "${foo#[yn]}" ] && ans=$foo || echo '??'
done
if [ "$ans" = "y" ]; then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done 7<file.txt
Si vous voulez lire votre fichier d'entrée en plusieurs étapes différentes, vous devez utiliser :
exec 7<file.txt # Without spaces between `7` and `<`!
# ls -l /dev/fd/
read <&7 headLine
while read <&7 filename; do
case "$filename" in
*'----' ) break ;; # break loop when line end with four dashes.
esac
....
done
read <&7 lastLine
exec 7<&- # This will close file descriptor 7.
# ls -l /dev/fd/
Sous bash vous pouvez le laisser choisir librement fd
pour vous et le stocker dans une variable :exec {varname}</path/to/input
:
while read -ru ${fle} filename;do
ans=
while [ -z "$ans" ]; do
read -rp "Process file '$filename' (y/n)? " -sn 1 foo
[ "$foo" ] && [ -z "${foo/[yn]}" ] && ans=$foo || echo '??'
done
if [ "$ans" = "y" ]; then
echo Yes
echo "Processing '$filename'."
else
echo No
fi
done {fle}<file.txt
Ou
exec {fle}<file.txt
# ls -l /dev/fd/
read -ru ${fle} headline
while read -ru ${fle} filename;do
[[ -n "$filename" ]] && [[ -z ${filename//*----} ]] && break
....
done
read -ru ${fle} lastLine
exec {fle}<&-
# ls -l /dev/fd/
Oui.
while read in; do chmod 755 "$in"; done < file.txt
De cette façon, vous pouvez éviter un cat
processus.
cat
est presque toujours mauvais pour un objectif tel que celui-ci. Vous pouvez en savoir plus sur Utilisation inutile du chat.
Si vous voulez exécuter votre commande en parallèle pour chaque ligne, vous pouvez utiliser GNU Parallel
parallel -a <your file> <program>
Chaque ligne de votre fichier sera passée au programme comme un argument. Par défaut parallel
exécute autant de threads que votre nombre de CPUs. Mais vous pouvez le spécifier avec -j
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.