J'ai vu des guides de script Bash suggérant l'utilisation d'un tableau pour travailler avec des noms de fichiers contenant des espaces. DashAsBinSh suggère cependant que les tableaux ne sont pas portables, donc je recherche une manière compatible POSIX de travailler avec des listes de noms de fichiers pouvant contenir des espaces.
Je cherche à modifier l'exemple de script ci-dessous afin qu'il echo
foo/target/a.jar
foo/target/b.jar
bar/target/lol whitespace.jar
Voici le script
#!/usr/bin/env sh
INPUT="foo/target/a.jar
foo/target/b.jar
bar/target/b.jar
bar/target/lol whitespace.jar"
# this would be produced by a 'ls' command
# We can execute the ls within the script, if it helps
dostuffwith() { echo $1; };
F_LOCATIONS=$INPUT
ALL_FILES=$(for f in $F_LOCATIONS; do echo `basename $f`; done)
ALL_FILES=$(echo "$ALL_FILES" | sort | uniq)
for f in $ALL_FILES
do
fpath=$(echo "$F_LOCATIONS" | grep -m1 $f)
dostuffwith $fpath
done
shell-script
filenames
quoting
posix
whitespace
Eero Aaltonen
la source
la source
Réponses:
Coquilles ont une matrice POSIX: les paramètres de position (
$1
,$2
, etc., collectivement refered comme"$@"
).Cela n'est pas pratique car il n'y en a qu'un et cela détruit toute autre utilisation des paramètres de position. Les paramètres de position sont locaux à une fonction, ce qui est parfois une bénédiction et parfois une malédiction.
Si les noms de vos fichiers ne contiennent pas de sauts de ligne, vous pouvez utiliser des sauts de ligne comme séparateur. Lorsque vous développez la variable, désactivez d'abord la globalisation avec
set -f
et définissez la liste des caractères de fractionnement de champIFS
pour qu'elle ne contienne qu'une nouvelle ligne.Les éléments de votre liste étant séparés par des sauts de ligne, vous pouvez utiliser de nombreuses commandes de traitement de texte, en particulier
sort
.N'oubliez pas de toujours mettre des guillemets doubles autour des substitutions de variables, sauf lorsque vous souhaitez explicitement que le fractionnement de champ se produise (ainsi que la globalisation, sauf si vous l'avez désactivé).
la source
sort | uniq
étape d' origine comme prévu.Puisque votre
$INPUT
variable utilise des sauts de ligne comme séparateurs, je vais supposer que vos fichiers n'auront pas de retours à la ligne dans les noms. En tant que tel, oui, il existe un moyen simple d'itérer sur les fichiers et de préserver les espaces.L'idée est d'utiliser le
read
shell intégré. Normalement, ilread
se divisera sur n'importe quel espace, et ainsi les espaces le briseront. Mais vous pouvez définirIFS=$'\n'
et il se divisera uniquement sur les nouvelles lignes. Vous pouvez donc parcourir chaque ligne de votre liste.Voici la plus petite solution que j'ai pu trouver:
Fondamentalement, il envoie "$ INPUT" à
awk
laquelle dédoublonne en fonction du nom du fichier (il se divise/
puis imprime la ligne si le dernier élément n'a pas été vu auparavant). Ensuite, une fois que awk a généré la liste des chemins de fichiers, nous utilisonswhile read
pour parcourir la liste.la source
while
boucle, et doncdostuffwith
est exécutée dans un sous-shell. Ainsi, toutes les variables ou modifications apportées au shell en cours d'exécution seront perdues à la fin de la boucle. La seule alternative est d'utiliser un hérédoc complet, ce qui n'est pas si désagréable, mais j'ai pensé que ce serait préférable.IFS="\n"
se divise en barre oblique inverse et n caractères. Maisread file
, il n'y a pas de division.IFS="\n"
est toujours utile dans la mesure où il supprime les caractères vierges de $ IFS qui autrement auraient été supprimés au début et à la fin de l'entrée. Pour lire une ligne, la syntaxe canonique estIFS= read -r line
, cependantIFS=anything read -r line
(à condition que rien ne contienne des blancs) fonctionnera également.