Liste des éléments avec des espaces dans zsh

10

J'ai étudié les scripts zsh pendant toutes les 2 heures à ce stade et j'ai heurté un mur. Je souhaite parcourir une liste de fichiers pouvant contenir des espaces. Je suis ouvert à des approches complètement différentes de l'exemple suivant tant qu'elles sont zsh puisque zsh est ce que j'étudie, pas la tâche que j'essaie de scénariser.

files=(`ls`)
for f in $files; do
    print $f
done

Je ne suis évidemment pas en train de recréer ls, je veux juste $fcapturer le nom de fichier entier à chaque itération de la boucle.

Felix
la source

Réponses:

12

Tout d'abord, je suppose que l'utilisation de lsn'est qu'un exemple. Vous ne pouvez pas analyser la sortie de lsdans aucun shell, car elle est ambiguë. Lisez Pourquoi vous ne devriez pas analyser la sortie de ls (1) si c'est une nouvelle pour vous. Dans n'importe quel shell, pour obtenir une liste de fichiers, utilisez des caractères génériques, par exemple files=(*).

Dans zsh, comme dans d'autres shells, le résultat de la substitution de commande est divisé en mots au niveau des espaces (plus précisément, en fonction de la valeur de IFS). (Contrairement à d'autres shells, le résultat de la substitution de commande n'est pas soumis à la globalisation dans zsh.) Donc, si la sortie de la lscommande est

hello world
wibble

puis files=($(ls))définit la filesmatrice pour contenir 3 éléments: hello, worldet wibble.

Si la substitution de commande est entre guillemets, aucun fractionnement n'est effectué. Vous pouvez effectuer un fractionnement personnalisé avec des indicateurs d'extension de paramètres . Utilisez l' @indicateur pour indiquer que le résultat du fractionnement doit être un tableau (curieusement, vous devez conserver l'expansion entre guillemets doubles, c'est "${(@)…}"-à- dire , même si la chaîne entre guillemets doubles s'étendra sur plusieurs mots). Pour le fractionnement, utilisez le sdrapeau, par exemple "${(@s:,:)…}"pour séparer les virgules; le fdrapeau se divise uniquement aux sauts de ligne.

files=("${(@f)$(ls)}")

Notez que la bonne façon d'itérer sur un tableau en général est for f in $files[@]de $filessupprimer les éléments vides (ici, cela n'a pas d'importance car les éléments ne seront pas vides).

print $finterprète $fcomme un commutateur s'il commence par un -et développe des barres obliques inverses $f. Utilisez print -r -- $f, ou print -rn -- $fsi vous ne souhaitez pas ajouter de nouvelle ligne après la chaîne.

Gilles 'SO- arrête d'être méchant'
la source
Je vous remercie. Oui, c'était juste un exemple. Il n'y avait pas d'objectif spécifique à l'esprit pour le moment, je veux juste savoir comment citer ou échapper les éléments de la liste de toute commande qui pourrait avoir des résultats individuels avec un espace blanc.
Felix
Une chose que je trouve confuse est la raison pour laquelle l'expression external () dans vos fichiers = ("$ {(@ f) $ (ls)}") est nécessaire avec le @f? Et juste jouer autour de (f) semble bien fonctionner tant que () apparaît à l'extérieur.
Koobz
@Koobz 1. L'extérieur (…)est nécessaire pour qu'il s'agisse d' filesun tableau et non d'une chaîne (qui contiendrait la concaténation des lignes de la commande, annulant le fractionnement effectué par (f)). 2. Avec foo=$'one\n\nthree', contrast print -rl $ {(f) foo} `et print "${(@f)foo}". Les guillemets doubles sont nécessaires pour conserver les lignes vides, dont vous n'obtiendrez pas lsmais qui peuvent arriver avec d'autres commandes.
Gilles 'SO- arrête d'être méchant'
Très appréciée. Y a-t-il une rime ou une raison à cette folie? Même là, ce qui prête à confusion, c'est pourquoi le externe () ne re-divise pas simplement la chaîne sur des mots individuels si la valeur intermédiaire est ma chaîne concaténée. Cela ressemble à une interpolation de chaîne en rubis ou php par exemple.
Koobz
@Koobz La raison du traitement spécial des mots vides est la compatibilité historique avec les anciennes versions oubliées de zsh. La raison pour laquelle le fractionnement n'est pas automatique est qu'il s'agit d'un comportement surprenant et souvent non souhaitable. Le fractionnement automatique est un comportement du shell Bourne que zsh n'émule pas sauf si vous le lui dites.
Gilles 'SO- arrête d'être méchant'
2

Dans zsh, vous pouvez utiliser l'expansion du shell qui n'effectue pas le fractionnement de mots par défaut. Essayer

for f in /path/to/files/*; do
    print ${f}
done
u-punkt
la source