Comment joindre plusieurs lignes de noms de fichiers en une seule avec un délimiteur personnalisé?

441

Je voudrais joindre le résultat de ls -1en une seule ligne et le délimiter avec tout ce que je veux.

Existe-t-il des commandes Linux standard que je peux utiliser pour y parvenir?

JavaRocky
la source

Réponses:

689

Similaire à la toute première option, mais omet le délimiteur de fin

ls -1 | paste -sd "," -
Artem
la source
29
Tout comme une note, la version de pâte que j'ai essayée nécessite un argument "-" à la fin pour lui dire de lire depuis STDIN. Par exemple, ls -1 | paste -s -d ":" - je ne sais pas si c'est universel avec toutes les versions de pâte
Andy White
4
celui-ci est meilleur car il permet un délimiteur vide :)
Yura Purbeev
2
Remarque pasteobtient -(entrée standard) par défaut, au moins sur mon paste (GNU coreutils) 8.22.
fedorqui 'SO arrête de nuire'
1
je viens de voter, c'est et maintenant il a les mêmes votes que la réponse sélectionnée. C'EST LA RÉPONSE. aucun délimiteur arrière
thebugfinder
1
Le délimiteur vide peut être spécifié en utilisant "\0", donc a paste -sd "\0" -fonctionné pour moi!
Brad Parks
379

EDIT : Simplement " ls -m " Si vous voulez que votre délimiteur soit une virgule

Ah, la puissance et la simplicité!

ls -1 | tr '\n' ','

Changez la virgule " , " en ce que vous voulez. Notez que cela inclut une "virgule de fin"

zaf
la source
46
+1, mais une version plus élaborée devrait gérer la dernière \ n différemment
mouviciel
5
Si le nom du fichier contient un \n, cela le remplacera également.
codaddict
3
@ShreevatsaR: il veut dire ne pas ajouter de "", je crois. comme çals -1 | tr "\\n" "," | sed 's/\(.*\),/\1/'
Chris
7
@Chris: vous sedpourriez être un peu plus efficace avec le caractère marqueur de fin:ls -1 | tr "\\n" "," | sed 's/,$//'; echo ''
pieman72
2
Utiliser sedaprès trsemble juste pour supprimer le dernier symbole semble déraisonnable. Je pars avecls -1 | tr '\n' ',' | head -c -1
reddot
29

Cela remplace la dernière virgule par une nouvelle ligne:

ls -1 | tr '\n' ',' | sed 's/,$/\n/'

ls -m inclut des sauts de ligne au niveau de la largeur de l'écran (80e par exemple).

Surtout Bash (uniquement lsexterne):

saveIFS=$IFS; IFS=$'\n'
files=($(ls -1))
IFS=,
list=${files[*]}
IFS=$saveIFS

Utiliser readarray(aka mapfile) dans Bash 4:

readarray -t files < <(ls -1)
saveIFS=$IFS
IFS=,
list=${files[*]}
IFS=$saveIFS

Merci à gniourf_gniourf pour les suggestions.

En pause jusqu'à nouvel ordre.
la source
Cela ne prendra pas en charge les fichiers avec des espaces blancs dans le nom. Essayez celui-ci: dir = / tmp / testdir; rm -rf $ dir && mkdir $ dir && cd / $ dir && touch "ceci est un fichier" this_is_another_file && ls -1 && files = ($ (ls -1)) && list = $ {files [@] /% / ,} && list = $ {list% *,} && echo $ list
dimir
1
@dimir: Beaucoup de réponses à cette question souffrent de ce problème. J'ai modifié ma réponse pour autoriser les noms de fichiers avec des tabulations ou des espaces, mais pas les retours à la ligne.
pause jusqu'à nouvel ordre.
Votre version bash souffre également d'extensions de chemin d'accès. Pour construire un tableau à partir des lignes, s'il vous plaît envisager d' utiliser mapfile(Bash ≥4) comme: mapfile -t files < <(ls -1). Pas besoin de jouer avec IFS. Et c'est plus court aussi.
gniourf_gniourf
Et quand vous avez votre tableau, vous pouvez utiliser IFSpour rejoindre les champs: saveIFS=$IFS; IFS=,; list=${files[*]}; IFS=$saveIFS. Ou utilisez une autre méthode si vous voulez un séparateur avec plus d'un caractère.
gniourf_gniourf
1
@gniourf_gniourf: J'ai inclus vos suggestions dans ma réponse. Merci.
pause jusqu'à nouvel ordre.
24

Je pense que celui-ci est génial

ls -1 | awk 'ORS=","'

ORS est le "séparateur d'enregistrements de sortie" donc maintenant vos lignes seront jointes par une virgule.

majkinetor
la source
6
Cela n'exclut pas le délimiteur de fin.
Derek Mahar
6
C'est particulièrement génial en raison de la gestion des séparateurs d'enregistrements à plusieurs caractères (par exemple, " OR ")
Mat Schaffer
15

La combinaison de la configuration IFSet de l'utilisation de "$*"peut faire ce que vous voulez. J'utilise un sous-shell donc je n'interfère pas avec $ IFS de ce shell

(set -- *; IFS=,; echo "$*")

Pour capturer la sortie,

output=$(set -- *; IFS=,; echo "$*")
glenn jackman
la source
2
Avez-vous plus d'informations sur le setfonctionnement? Ça me ressemble un peu au vaudou. un regard superficiel à travers man setne m'a pas rapporté beaucoup d'informations non plus.
Ehtesh Choudhury
3
Si vous donnez setun tas d'arguments mais pas d'options, il définit les paramètres de position ($ 1, $ 2, ...). --est là pour protéger setau cas où le premier argument (ou nom de fichier dans ce cas) arriverait à commencer par un tiret. Voir la description de l' --option dans help set. Je trouve que les paramètres positionnels sont un moyen pratique de gérer une liste de choses. J'aurais également pu implémenter cela avec un tableau:output=$( files=(*); IFS=,; echo "${files[*]}" )
glenn jackman
C'est génial car il ne nécessite pas d'exécuter de programmes supplémentaires et il fonctionne avec des noms de fichiers contenant des espaces ou même des retours à la ligne.
Eric
1
@EhteshChoudhury Comme type setvous le dira, set is a shell builtin. Donc, ça man setn'aidera pas, mais help setça ira. Réponse: "- Attribuez tous les arguments restants aux paramètres de position."
Stéphane Gourichon
Après a set -- *. L' expansion Retarder d' *un niveau vous pouvez obtenir la sortie correcte sans qu'il soit nécessaire d'une coquille sous: IFS=',' eval echo '"$*"'. Bien sûr, cela changera les paramètres de position.
Isaac
13

L'analyse lsen général n'est pas conseillée , donc une meilleure alternative est d'utiliser find, par exemple:

find . -type f -print0 | tr '\0' ','

Ou en utilisant findet paste:

find . -type f | paste -d, -s

Pour joindre généralement plusieurs lignes (non liées au système de fichiers), vérifiez: «Join» concis et portable sur la ligne de commande Unix .

kenorb
la source
9

Ne réinventez pas la roue.

ls -m

C'est exactement cela.

Tulains Córdova
la source
L'OP voulait n'importe quel délimiteur donc vous auriez toujours besoin d'un tr pour convertir les virgules. Il ajoute également un espace après les virgules, c.-à-d. File1, file2, file3
vol
Donc, en utilisant ls -met trpour supprimer l'espace après la virgule, vous feriezls -m | tr -d ' '
Andy
2
cette utilisation de tr supprimera les espaces à l'intérieur des noms de fichiers. mieux utilisersed 's/, /,/g
glenn jackman
7

bash juste

mystring=$(printf "%s|" *)
echo ${mystring%|}
ghostdog74
la source
5
Un peu plus efficace serait d'utiliser "printf -v mystring"% s | "*" - qui évite un fork pour le $ ()
camh
Mais notamment ne réduit pas la fin |, @camh.
Christopher
1
Eh bien, juste bashet gnu coreutilsprintf
ThorSummoner
@camh Mais printf -vne fonctionnera qu'en bash, tandis que la réponse présentée fonctionne sur de nombreux types de shell.
Isaac
@Christopher Oui, qui va enlever la fuite |, à condition que les deux lignes sont utilisées: printf -v mystring "%s|" * ; echo ${mystring%|}.
Isaac
7

Cette commande est destinée aux fans de PERL:

ls -1 | perl -l40pe0

Ici 40 est le code ascii octal pour l'espace.

-p traitera ligne par ligne et imprimera

-l se chargera de remplacer le \ n de fin par le caractère ascii que nous fournissons.

-e est d'informer PERL que nous faisons l'exécution en ligne de commande.

0 signifie qu'il n'y a en fait aucune commande à exécuter.

perl -e0 est identique à perl -e ''

Sidharth C. Nadhan
la source
6

Pour éviter une confusion potentielle de nouvelle ligne pour tr, nous pourrions ajouter le drapeau -b à ls:

ls -1b | tr '\n' ';'
yabt
la source
5

Il semble que les réponses existent déjà.

Si vous voulez un a, b, cformat, utilisez ls -m( réponse de Tulains Córdova )

Ou si vous voulez un a b cformat, utilisez ls | xargs(version simplifiée de la réponse de Chris J )

Ou si vous voulez un autre délimiteur comme |, utilisez ls | paste -sd'|'(application de la réponse d' Artem )

plhn
la source
5

La voie sed,

sed -e ':a; N; $!ba; s/\n/,/g'
  # :a         # label called 'a'
  # N          # append next line into Pattern Space (see info sed)
  # $!ba       # if it's the last line ($) do not (!) jump to (b) label :a (a) - break loop
  # s/\n/,/g   # any substitution you want

Remarque :

Ceci est de complexité linéaire, ne se substituant qu'une seule fois après que toutes les lignes ont été ajoutées dans le Pattern Space de sed.

@ La réponse d'AnandRajaseka , et quelques autres réponses similaires, comme ici , sont O (n²), car sed doit faire un substitut chaque fois qu'une nouvelle ligne est ajoutée dans le Pattern Space.

Comparer,

seq 1 100000 | sed ':a; N; $!ba; s/\n/,/g' | head -c 80
  # linear, in less than 0.1s
seq 1 100000 | sed ':a; /$/N; s/\n/,/; ta' | head -c 80
  # quadratic, hung
zhazha
la source
5

En plus de la réponse de majkinetor, voici la façon de supprimer le délimiteur de fin (car je ne peux pas encore commenter sous sa réponse):

ls -1 | awk 'ORS=","' | head -c -1

Supprimez simplement autant d'octets de fin que votre délimiteur compte.

J'aime cette approche car je peux utiliser des délimiteurs multi-caractères et d'autres avantages de awk:

ls -1 | awk 'ORS=", "' | head -c -2

ÉDITER

Comme Peter l'a remarqué, le nombre d'octets négatifs n'est pas pris en charge dans la version native de head de MacOS. Cependant, cela peut être facilement résolu.

Tout d'abord, installez coreutils. "Les utilitaires GNU Core sont les utilitaires de base de manipulation de fichiers, de shell et de texte du système d'exploitation GNU."

brew install coreutils

Les commandes également fournies par MacOS sont installées avec le préfixe "g". Par exemple gls.

Une fois que vous avez fait cela, vous pouvez utiliser gheadun nombre d'octets négatif, ou mieux, créer un alias:

alias head="ghead"
Aleksander Stelmaczonek
la source
Remarque: le nombre d'octets négatifs n'est pris en charge que sur certaines versions de head, donc cela ne fonctionnera pas sur les macos par exemple.
Peter
Merci d'avoir fait remarquer cela. J'ai ajouté une solution de contournement pour MacOS.
Aleksander Stelmaczonek
3

Si votre version de xargs prend en charge l'indicateur -d, cela devrait fonctionner

ls  | xargs -d, -L 1 echo

-d est le drapeau de délimitation

Si vous n'avez pas -d, vous pouvez essayer ce qui suit

ls | xargs -I {} echo {}, | xargs echo

Le premier xargs vous permet de spécifier votre délimiteur qui est une virgule dans cet exemple.

Chris J
la source
3
-dspécifie le délimiteur d'entrée avec des xargs GNU, donc ne fonctionnera pas. Le deuxième exemple présente le même problème que d'autres solutions ici d'un délimiteur errant à la fin.
Thor
3
sed -e :a -e '/$/N; s/\n/\\n/; ta' [filename]

Explication:

-e- indique une commande à exécuter
:a- est une étiquette
/$/N- définit la portée de la correspondance pour la ligne actuelle et la ligne (N) ext
s/\n/\\n/;- remplace tous les EOL par \n
ta;- goto étiquette a si la correspondance est réussie

Tiré de mon blog .

Anand Rajasekar
la source
2

Vous pouvez utiliser:

ls -1 | perl -pe 's/\n$/some_delimiter/'
codaddict
la source
Cela n'exclut pas le délimiteur de fin.
Derek Mahar
2

lsproduit une sortie de colonne lorsqu'il est connecté à un tuyau, il -1est donc redondant.

Voici une autre réponse Perl utilisant la joinfonction intégrée qui ne laisse pas de délimiteur de fin:

ls | perl -F'\n' -0777 -anE 'say join ",", @F'

L'obscur -0777fait perl lire toutes les entrées avant d'exécuter le programme.

alternative sed qui ne laisse pas de délimiteur de fin

ls | sed '$!s/$/,/' | tr -d '\n'
Thor
la source
0

lsa la possibilité -mde délimiter la sortie avec ", "une virgule et un espace.

ls -m | tr -d ' ' | tr ',' ';'

canaliser ce résultat pour trsupprimer l'espace ou la virgule vous permettra de rediriger le résultat trpour remplacer le délimiteur.

dans mon exemple, je remplace le délimiteur ,par le délimiteur;

remplacez-le ;par le délimiteur que vous préférez, car tr ne représente que le premier caractère des chaînes que vous passez en arguments.

Andy
la source
0

Vous pouvez utiliser chomp pour fusionner plusieurs lignes en une seule ligne:

perl -e 'while (<>) {if (/ \ $ /) {chomp; } print;} 'bad0> test

mettre la condition de saut de ligne dans l'instruction if. Il peut s'agir d'un caractère spécial ou d'un délimiteur.

Suman
la source
0

Version Perl rapide avec gestion des barres obliques de fin:

ls -1 | perl -E 'say join ", ", map {chomp; $_} <>'

Expliquer:

  • perl -E: exécutez Perl avec les fonctionnalités supportées (disons, ...)
  • dire: imprimer avec un retour du transporteur
  • joindre ",", ARRAY_HERE: joindre un tableau avec ","
  • map {chomp; $ _} ROWS: supprimez de chaque ligne le retour du transporteur et retournez le résultat
  • <>: stdin, chaque ligne est une ligne, couplée à une carte, elle créera un tableau de chaque ligne
Celogeek San
la source