web-dev-qa-db-fra.com

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

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 ceci:

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 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 un nom de répertoire. Juste "A1" "A2" "A3".

9
maulana

Vous pouvez simplement cut le champ 1st slash -delimited de chaque ligne et attribuer 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 ARG_MAX problèmes si votre liste contient un nombre énorme de noms de répertoires, dans ce cas, utilisez GNU parallelInstall parallel ou xargs comme suit:

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

Bien que parallel soit couvert, l’approche xargs ne fonctionnera pas si les noms de répertoire contiennent des espaces - vous pouvez utiliser \0 comme délimiteur de ligne ou simplement demander à xargs de ne scinder que 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

Si l'un des champs contient un caractère de nouvelle ligne, vous devez identifier les "vraies" fins de ligne et remplacer uniquement les caractères de nouvelle ligne par exemple. \0. Ce serait un cas pour awk, mais j’ai le sentiment que c’est trop aller ici.

13
dessert

Vous devez définir votre IFS='/' pour read, puis attribuer chaque premier champ à la variable distincte first et le reste des champs à la variable rest et ne travailler que sur la valeur du premier champ (ou vous pouvez utiliser read -ar array dans un tableau unique et utiliser "${array[0]}" pour le premier champ. valeur. ):

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 fois:

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

Le ANSI-C Quotation $'...' est utilisé 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] au bash -c '...' et que le $@ contiendra le reste des paramètres à partir de 1; sans cette deuxième commande, le premier paramètre du mkdir sera perdu.

Dans ${1%%/*} en utilisant Shell (expansion de substitution de paramètre de POSIX sh/bash/Korn/zsh) , supprime la correspondance la plus longue possible d’une barre oblique suivie de tout élément jusqu’à la fin du paramètre qui y passe par xargs ;

P.s:

  • Supprimez echo devant le mkdir pour créer ces répertoires.
  • Remplacez -d'\n' par -0 si votre liste est séparée par des caractères NUL au lieu d'un \newline (supposez qu'il y a/une nouvelle ligne est incorporée dans votre nom de répertoire).
10
αғsнιη

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 les dossiers A[123]:

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

Sortie de ls:

A 12"x4" dir
A1
A2
A3
6
xiota

Pour un cas simple, comme indiqué dans l'exemple de saisie de la question, utilisez simplement cutet passez le résultat à mkdirvia xargsname__

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

Pour 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 c, comme suggéré par @OleTange dans les commentaires, vous pouvez vous tourner vers awkpour créer une liste séparée par des caractères null au lieu de la liste séparée par des lignes.

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

@dessert dans les commentaires se demandait si printfpouvait être utilisé à la place de cutet techniquement, il pouvait ê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 le moyen le plus propre, mais cela prouve que printfpeut être utilisé. Bien sûr, cela devient problématique si le nom du répertoire dépasse 3 caractères.

5

en utilisant Perl:

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

Ou

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

si nous voulons accepter des espaces sur les noms de répertoires

2
JJoao

GNU Parallel est peut-être excessif pour la tâche, mais si vous voulez 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
0
Ole Tange