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".
Vous pouvez simplement cut
le champ 1
st slash -d
elimited de chaque ligne et attribuer la liste à mkdir
:
mkdir $(<dirlist.txt cut -d/ -f1)
$ 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 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.
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
<test.txt xargs -d'\n' -n1 sh -c 'echo mkdir "$'{1%%/*}'"' _
<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:
echo
devant le mkdir
pour créer ces répertoires.-d'\n'
par -0
si votre liste est séparée par des caractères NUL au lieu d'un \n
ewline (supposez qu'il y a/une nouvelle ligne est incorporée dans votre nom de répertoire).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
Pour un cas simple, comme indiqué dans l'exemple de saisie de la question, utilisez simplement cut
et passez le résultat à mkdir
via xargs
name__
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 awk
pour 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 printf
pouvait être utilisé à la place de cut
et 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 printf
peut être utilisé. Bien sûr, cela devient problématique si le nom du répertoire dépasse 3 caractères.
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
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