list=`ls -a R*`
echo $list
Dans un script shell, cette commande echo listera tous les fichiers du répertoire courant commençant par R, mais sur une seule ligne. Comment imprimer chaque article sur une seule ligne?
J'ai besoin d' une commande générique pour tous les scénarios qui se passe avec ls
, du
, find -type -d
, etc.
Réponses:
Si la sortie de la commande contient plusieurs lignes, citez vos variables pour conserver ces nouvelles lignes lors de l'écho:
Sinon, le shell se développera et divisera le contenu des variables sur les espaces (espaces, nouvelles lignes, tabulations) et ceux-ci seront perdus.
la source
Au lieu de placer à mauvais escient la
ls
sortie dans une variable, puis de l'echo
intégrer, ce qui supprime toutes les couleurs, utilisezDe
man ls
Je ne vous conseille pas de faire quoi que ce soit avec la sortie de
ls
, sauf l'afficher :)Utilisez, par exemple, des globes shell et une
for
boucle pour faire quelque chose avec des fichiers ...* Cela ne comprend pas le répertoire courant
.
ou son parent..
sila source
echo "$i"
parprintf "%s\n" "$i"
(que l'on ne s'étouffera pas si un nom de fichier commence par un "-"). En fait, la plupart du tempsecho something
devrait être remplacé parprintf "%s\n" "something"
.R
ici, mais en général, oui. Pourtant, même pour les scripts, essayer de toujours prendre en compte les-
chemins de début entraîne des problèmes: les gens supposent--
que seules certaines commandes prennent en charge signifient la fin des options. J'ai vufind . [tests] -exec [cmd] -- {} \;
où[cmd]
ne supporte pas--
. Chaque chemin commence de.
toute façon! Mais ce n'est pas un argument contre le remplacementecho
parprintf '%s\n'
. Ici, on peut remplacer la boucle entière parprintf '%s\n' R/*
. Bash aprintf
une fonction intégrée, donc il n'y a effectivement aucune limite au nombre / longueur d'arguments.Tout en le mettant entre guillemets comme @muru l'a suggéré fera en effet ce que vous avez demandé, vous pouvez également envisager d'utiliser un tableau pour cela. Par exemple:
Le
IFS=$'\n'
dit à bash de ne diviser la sortie que sur les caractères de nouvelle ligne pour obtenir chaque élément du tableau. Sans cela, il se divisera en espaces, il ya file name with spaces.txt
aurait donc 5 éléments distincts au lieu d'un. Cette approche ne fonctionnera pas si les noms de vos fichiers / répertoires peuvent contenir des sauts de ligne (\n
). Il enregistrera chaque ligne de la sortie de la commande en tant qu'élément du tableau.Notez que j'ai aussi changé le style ancien
`command`
à$(command)
qui est la syntaxe préférée.Vous avez maintenant un tableau appelé
$dirs
, chaque bof dont les éléments est une ligne de la sortie de la commande précédente. Par exemple:Maintenant, en raison d'une certaine étrangeté bash (voir ici ), vous devrez réinitialiser
IFS
la valeur d'origine après avoir fait cela. Donc, soit enregistrez-le et reconcevoir:Ou faites-le manuellement:
Sinon, fermez simplement le terminal actuel. Votre nouveau aura à nouveau l'IFS d'origine.
la source
du
qui donne l'impression que OP est plus intéressée par la préservation du format de sortie.Si vous devez stocker la sortie et que vous voulez un tableau,
mapfile
c'est facile.Tout d'abord, déterminez si vous avez besoin de stocker la sortie de votre commande. Si vous ne le faites pas, exécutez simplement la commande.
Si vous décidez que vous souhaitez lire la sortie d'une commande sous forme de tableau de lignes, il est vrai qu'une façon de le faire consiste à désactiver la globalisation, à définir la
IFS
répartition sur les lignes, à utiliser la substitution de commandes à l'intérieur de la(
)
syntaxe de création du tableau et à la réinitialiserIFS
par la suite, ce qui est plus complexe qu'il n'y paraît. La réponse de terdon couvre une partie de cette approche. Mais je vous suggère d'utiliser àmapfile
la place la commande intégrée du shell Bash pour lire le texte sous forme de tableau de lignes. Voici un cas simple où vous lisez un fichier:Cela lit les lignes dans un tableau appelé
MAPFILE
. Sans la redirection d'entrée , vous liriez à partir de l' entrée standard du shell (généralement votre terminal) au lieu du fichier nommé par . Pour lire dans un tableau autre que celui par défaut , passez son nom. Par exemple, cela lit dans le tableau :< filename
filename
MAPFILE
lines
Un autre comportement par défaut que vous pourriez choisir de changer est que les caractères de nouvelle ligne à la fin des lignes sont laissés en place; ils apparaissent comme le dernier caractère de chaque élément du tableau (sauf si l'entrée s'est terminée sans caractère de nouvelle ligne, auquel cas le dernier élément n'en a pas). Pour couper ces nouvelles lignes afin qu'elles n'apparaissent pas dans le tableau, passez l'
-t
option àmapfile
. Par exemple, cela lit le tableaurecords
et n'écrit pas de caractères de fin de ligne dans ses éléments de tableau:Vous pouvez également utiliser
-t
sans passer de nom de tableau; c'est-à-dire qu'il fonctionne aussi avec un nom de tableau implicite deMAPFILE
.Le
mapfile
shell intégré prend en charge d'autres options, et il peut également être appelé en tant quereadarray
. Exécutezhelp mapfile
(ethelp readarray
) pour plus de détails.Mais vous ne voulez pas lire à partir d'un fichier, vous voulez lire à partir de la sortie d'une commande. Pour ce faire, utilisez la substitution de processus . Cette commande lit les lignes de la commande
some-command
avecarguments...
comme arguments de ligne de commande et les place dans lemapfile
tableau par défaut deMAPFILE
, avec leurs caractères de fin de ligne supprimés:La substitution de processus remplace par un nom de fichier réel à partir duquel la sortie de l'exécution peut être lue. Le fichier est un canal nommé plutôt qu'un fichier normal et, sur Ubuntu, il sera nommé comme (parfois avec un autre numéro que ), mais vous n'avez pas besoin de vous préoccuper des détails, car le shell s'en charge tous dans les coulisses.
<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
Vous pourriez penser que vous pourriez renoncer à la substitution de processus en utilisant à la place, mais cela ne fonctionnera pas car, lorsque vous avez un pipeline de plusieurs commandes séparées par , Bash s'exécute exécute toutes les commandes en sous-coquilles . Ainsi , les deux et courir dans leurs propres environnements , initialisées à partir mais distincte de l'environnement de la coquille dans laquelle vous exécutez le pipeline. Dans le sous-shell où s'exécute, le tableau est rempli, mais ce tableau est rejeté à la fin de la commande. n'est jamais créé ou modifié pour l'appelant.
some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
Voici à quoi ressemble l'exemple de la réponse de terdon , si vous utilisez
mapfile
:C'est ça. Vous n'avez pas besoin de vérifier si elle a
IFS
été définie , de savoir si elle a été définie et avec quelle valeur, de la définir sur une nouvelle ligne, puis de la réinitialiser ou de la réinitialiser plus tard. Vous ne devez pas désactiver globbing (par exemple,set -f
) - ce qui est vraiment nécessaire si vous voulez utiliser cette méthode au sérieux, puisque les noms de fichiers peuvent contenir*
,?
et[
--then réactiviez (set +f
) par la suite.Vous pouvez également remplacer cette boucle particulière entièrement par une seule
printf
commande - bien que ce ne soit pas réellement un avantagemapfile
, car vous pouvez le faire que vous utilisiezmapfile
une autre méthode pour remplir le tableau. Voici une version plus courte:Il est important de garder à l'esprit que, comme le mentionne Terdon , l'exploitation ligne par ligne n'est pas toujours appropriée et ne fonctionnera pas correctement avec les noms de fichiers contenant des sauts de ligne. Je déconseille de nommer les fichiers de cette façon, mais cela peut arriver, y compris par accident.
Il n'y a pas vraiment de solution unique.
Vous avez demandé «une commande générique pour tous les scénarios» et l'approche consistant à utiliser
mapfile
quelque peu l'approche de cet objectif, mais je vous invite à reconsidérer vos besoins.La tâche illustrée ci-dessus est mieux réalisée avec une seule
find
commande:Vous pouvez également utiliser une commande externe comme
sed
ajouterDIR:
au début de chaque ligne. C'est sans doute quelque peu moche, et contrairement à cette commande find, elle ajoutera des «préfixes» supplémentaires dans les noms de fichiers qui contiennent des nouvelles lignes, mais cela fonctionne indépendamment de son entrée, donc il répond en quelque sorte à vos besoins et il est toujours préférable de lire la sortie dans un variable ou tableau :Si vous devez répertorier et également effectuer une action sur chaque répertoire trouvé, telle que l'exécution
some-command
et la transmission du chemin du répertoire en tant qu'argument,find
vous pouvez également le faire:Comme autre exemple, revenons à la tâche générale d'ajouter un préfixe à chaque ligne. Supposons que je veuille voir la sortie de
help mapfile
mais numéroter les lignes. Je n'utiliserais pas réellementmapfile
pour cela, ni aucune autre méthode qui le lit dans une variable shell ou un tableau shell. Supposonshelp mapfile | cat -n
que je ne donne pas la mise en forme que je veux, je peux utiliserawk
:La lecture de toutes les sorties d'une commande dans une seule variable ou un seul tableau est parfois utile et appropriée, mais elle présente des inconvénients majeurs. Non seulement vous devez faire face à la complexité supplémentaire de l'utilisation de votre shell lorsqu'une commande existante ou une combinaison de commandes existantes peut déjà faire ce dont vous avez besoin ou mieux, mais la sortie entière de la commande doit être stockée en mémoire. Parfois, vous savez peut-être que ce n'est pas un problème, mais parfois vous traitez un fichier volumineux.
Une alternative qui est couramment tentée - et parfois bien faite - consiste à lire l'entrée ligne par ligne
read -r
dans une boucle. Si vous n'avez pas besoin de stocker les lignes précédentes lorsque vous travaillez sur des lignes ultérieures et que vous devez utiliser une entrée longue, cela peut être mieux quemapfile
. Mais cela devrait également être évité dans les cas où vous pouvez simplement le rediriger vers une commande qui peut faire le travail, ce qui est la plupart des cas .la source