Créer des répertoires avec un nom à partir du fichier txt contenant le caractère '/'

8

J'ai un fichier .txt qui contient un texte comme celui-ci

A1/B1/C1
A2/B2/C2 
A3/B3/C3

Je veux un script qui lit le fichier .txt pour chaque ligne puis crée un répertoire basé sur le premier mot (A1, A2, A3)

J'ai créé un script comme celui-ci:

file="test.txt"
while IFS='' read -r line
do
    name="line"
    mkdir -p $line
done <"$file"

Pendant que je l'exécute, il crée le répertoire A1 puis crée également les sous-répertoires B1 et C1. il en va de même pour une autre ligne (A2 * et A3 *)

Que dois-je faire pour créer uniquement des répertoires A1, A2, A3?

Je ne veux pas faire le nom comme A1 / B1 / C1 avec le caractère '/' dedans. Je veux juste prendre le mot avant le caractère '/' et lui donner le nom du répertoire. Juste "A1" "A2" "A3".

maulana
la source

Réponses:

13

Vous pouvez juste cutle champ 1délimité par st slash dde chaque ligne et donner la liste à mkdir:

mkdir $(<dirlist.txt cut -d/ -f1)

Exemple d'exécution

$ cat dirlist.txt 
A1/A2/A3
B1/B2/B3
C1/C2/C3
$ ls
dirlist.txt
$ mkdir $(<dirlist.txt cut -d/ -f1)
$ ls
A1  B1  C1  dirlist.txt

Vous pouvez rencontrer des problèmes ARG_MAX si votre liste contient un grand nombre de noms de répertoire, dans ce cas, utilisez GNU parallel Installer en parallèleou xargscomme suit:

parallel mkdir :::: <(<dirlist.txt cut -d/ -f1)
xargs -a<(<dirlist.txt cut -d/ -f1) mkdir

Bien que parallelcela soit couvert, l' xargsapproche ne fonctionnera pas si les noms de répertoire contiennent des espaces - vous pouvez soit utiliser \0comme délimiteur de ligne ou simplement demander xargsde diviser l'entrée uniquement sur les caractères de nouvelle ligne (comme proposé par Martin Bonner ) à la place:

xargs -0a<(<dirlist.txt tr \\{n,0} | cut -d/ -f1 -z) mkdir # \\{n,0} equals \\n \\0
xargs -d\\n -a<(<dirlist.txt cut -d/ -f1) mkdir

Dans le cas où l'un des champs contient un caractère de nouvelle ligne, il faudrait identifier les «vraies» fins de ligne et remplacer uniquement ces caractères de nouvelle ligne par exemple \0. Ce serait un cas pour awk, mais je pense que c'est trop tiré par les cheveux ici.

dessert
la source
Pourquoi xargs -a<(....)plutôt que <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner soutient Monica
1
@MartinBonner Merci d'avoir clarifié, c'est en effet plus simple que mon trapproche - je viens de réaliser que parallelcela est couvert par défaut.
dessert
@MartinBonner Pourquoi xargs -a<(....)plutôt qu'une pipe - parce que j'aime ça comme ça, aussi simple que ça. :)
dessert
@AndreaCorbellini Oh, maintenant je comprends. <(...)aide également avec les espaces, donc c'est certainement le meilleur choix - je ne pense pas que quiconque utilisait ce descripteur de fichier de toute façon.
dessert
@MartinBonner Plus intéressant: pourquoi cut <dirlist.txtau lieu de juste cut dirlist.txt?
dessert
11

Vous devez définir votre IFS='/'pour read, puis affecter chaque premier champ en variable distincte firstet le reste des champs en variable restet simplement travailler sur la valeur du premier champ (ou vous pouvez read -ar arrayutiliser un seul tableau et utiliser "${array[0]}"pour la première valeur du champ.):

while IFS='/' read -r first rest;
do
    echo mkdir "$first" 
done < test.txt

Ou en une seule ligne pour ceux qui l'aiment:

<test.txt xargs -d'\n' -n1 sh -c 'echo mkdir "$'{1%%/*}'"' _

Ou créez tous les répertoires en une seule fois:

<test.txt xargs -d'\n' bash -c 'echo mkdir "$'{@%%/*}'"' _

La citation ANSI-C$'...' est utilisée pour traiter les noms de répertoire contenant des caractères spéciaux.

Notez que le _(peut être n'importe quel caractère ou chaîne) à la fin sera argv [0] à la bash -c '...'et la $@volonté contiendra le reste des paramètres à partir de 1; sans cela dans la seconde commande, le premier paramètre du mkdirsera perdu.

En ${1%%/*}utilisant l' expansion de substitution de paramètres shell (POSIX sh / bash / Korn / zsh) , supprime la correspondance la plus longue possible d'une barre oblique suivie de tout jusqu'à la fin du paramètre qui y passe, qui est une ligne lue par xargs ;

Ps:

  • Supprimer echodevant le mkdirpour créer ces répertoires.
  • Remplacez -d'\n'par -0si votre liste est séparée par des caractères NUL au lieu d'une ligne \nélectronique (supposé qu'il y ait / soit une nouvelle ligne intégrée dans le nom de votre répertoire).
αғsнιη
la source
6

Contenu de test.txt:

A 12"x4" dir/B b/C c
A1/B1/C1
A2/B2/C2
A3/B3/C3

Script pour créer des A[123]dossiers:

file="test.txt"
while read -r line ; do
   mkdir "${line%%/*}"
done < "$file"

Sortie de ls:

A 12"x4" dir
A1
A2
A3
xiota
la source
Comment gérez-vous les entrées comme A 12"x4" dir/B b/C c:?
Ole Tange
Ole Tange - Ce commentaire semble complètement hors de portée pour la question.
DocSalvager
4

Pour un cas simple, comme indiqué dans l'exemple d'entrée de la question, utilisez cut et transmettez simplement la sortie à mkdirviaxargs

cut -f1 -d '/' file.txt | xargs -L1 mkdir 

Pour gérer les cas où le nom du répertoire peut contenir des espaces, nous pourrions ajouter -d '\n'à la liste des options:

$ cat input.txt 
A 1/B 1/C 1
A 2/B 2/C 2
A 3/B 2/C 2
$ cut -f1 -d '/' input.txt | xargs -d '\n' mkdir 
$ ls
A 1  A 2  A 3  input.txt

Pour des variantes plus complexes, telles que A 12"x4" dir/B b/C csuggérées par @OleTange dans les commentaires, on peut se tourner vers awkpour créer une liste séparée par des valeurs nulles au lieu d'une liste séparée par des sauts de ligne.

awk -F'/' '{printf  "%s\0",$1}' input.txt |  xargs -0 mkdir

@dessert s'est demandé dans les commentaires s'il printfpouvait être utilisé à la place de cut, et techniquement parlant, il peut être utilisé, par exemple en limitant la chaîne imprimée à la largeur de 3 caractères seulement:

xargs -d '\n' printf "%.3s\n"  < input.txt | xargs -L1 mkdir 

Ce n'est pas la manière la plus propre, mais cela prouve printf qu'on peut l'utiliser. Bien sûr, cela devient problématique si le nom du répertoire dépasse 3 caractères.

Sergiy Kolodyazhnyy
la source
Comment gérez-vous les entrées comme A 12"x4" dir/B b/C c:?
Ole Tange
@OleTange Voir l'édition.
Sergiy Kolodyazhnyy
2

en utilisant Perl:

perl -ne 'mkdir for /^(\w+)/' list.txt

Ou

perl -ne 'mkdir for /^([^\/]+)/' list.txt

si nous voulons accepter des espaces sur dir-names

αғsнιη
la source
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtpour couvrir les espaces dans les noms dir. J'ai enfin besoin d'apprendre Perl - merci!
dessert
0

GNU Parallel peut être exagéré pour la tâche, mais si vous allez faire d'autres choses pour chaque ligne, cela peut être utile:

cat myfile.txt | parallel --colsep / mkdir {1}
parallel -a myfile.txt --colsep / mkdir {1}

Il traite correctement les entrées comme:

A 12"x4" dir/B b/C c
Ole Tange
la source