Erstellen Sie Verzeichnisse mit einem Namen aus der txt-Datei, die das Zeichen '/' enthalten

8

Ich habe eine TXT-Datei, die einen solchen Text enthält

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

Ich möchte ein Skript, das die TXT-Datei für jede Zeile liest und dann ein Verzeichnis basierend auf dem ersten Wort erstellt (A1, A2, A3).

Ich habe ein Skript wie folgt erstellt:

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

Während ich es ausführe, erstellt es das Verzeichnis A1 und anschließend die Unterverzeichnisse B1 und C1. das gleiche passiert für eine andere Linie (A2 * und A3 *)

Was kann ich tun, um nur A1-, A2-, A3-Verzeichnisse zu erstellen?

Ich möchte den Namen nicht wie A1 / B1 / C1 mit dem Zeichen '/' machen. Ich möchte nur das Wort vor dem Zeichen '/' nehmen und es zum Verzeichnisnamen machen. Nur "A1" "A2" "A3".

Maulana
quelle

Antworten:

13

Sie können nur cutdas 1st-Schrägstrich- dFeld jeder Zeile eingeben und die Liste geben an mkdir:

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

Beispiellauf

$ 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

Sie können auf ARG_MAX- Probleme stoßen , wenn Ihre Liste eine große Anzahl von Verzeichnisnamen enthält. Verwenden Sie in diesem Fall GNU parallel Parallel installierenoder xargswie folgt:

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

Obwohl paralleldies behandelt wurde, xargsfunktioniert der Ansatz nicht, wenn die Verzeichnisnamen Leerzeichen enthalten. Sie können entweder \0als Zeilenbegrenzer verwenden oder einfach anweisen xargs, die Eingabe stattdessen nur in Zeilenumbruchzeichen (wie von Martin Bonner vorgeschlagen ) aufzuteilen :

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

Falls eines der Felder ein Zeilenumbruchzeichen enthält, müsste man die "wahren" Zeilenenden identifizieren und nur diese Zeilenumbruchzeichen durch z \0. Das wäre ein Fall für awk, aber ich denke, es ist hier zu weit hergeholt.

Dessert
quelle
Warum xargs -a<(....)eher als <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner unterstützt Monica
1
@MartinBonner Vielen Dank für die Klarstellung, das ist in der Tat einfacher als mein trAnsatz - ich habe gerade festgestellt, dass paralleles standardmäßig abgedeckt ist.
Nachtisch
@MartinBonner Warum xargs -a<(....)statt einer Pfeife - weil ich es so mag, so einfach ist das. :)
Nachtisch
@AndreaCorbellini Oh, jetzt verstehe ich. <(...)hilft auch bei Leerzeichen, daher ist es definitiv die bessere Wahl - ich glaube, niemand hat diesen Dateideskriptor verwendet.
Nachtisch
@MartinBonner Interessanter: Warum cut <dirlist.txtstatt nur cut dirlist.txt?
Nachtisch
10

Sie müssen Ihr IFS='/'for festlegen readund dann jedes erste Feld einer separaten Variablen firstund den Rest der Felder einer Variablen zuweisen restund nur den Wert des ersten Felds bearbeiten (oder Sie können read -ar arrayein einzelnes Array verwenden und "${array[0]}"für den Wert des ersten Felds verwenden.):

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

Oder in einer Zeile für diejenigen, die es mögen:

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

Oder erstellen Sie alle Verzeichnisse auf einmal:

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

Das ANSI-C-Angebot$'...' wird verwendet, um Verzeichnisnamen zu verarbeiten, die Sonderzeichen enthalten.

Beachten Sie, dass das _(kann ein beliebiges Zeichen oder eine beliebige Zeichenfolge sein) am Ende argv [0] für das ist bash -c '...'und das $@Testament den Rest der Parameter ab 1 enthält. ohne das im zweiten Befehl geht der erste Parameter zum mkdirverloren.

Bei ${1%%/*}Verwendung der Shell-Parameterersetzungserweiterung (POSIX sh / bash / Korn / zsh) wird die längstmögliche Übereinstimmung eines Schrägstrichs entfernt, gefolgt von irgendetwas bis zum Ende des Parameters, der in diese Zeile übergeht . Dies ist eine Zeile, die von xargs gelesen wird .

Ps:

  • Entfernen Sie echovor dem mkdir, um diese Verzeichnisse zu erstellen.
  • Ersetzen Sie -d'\n'durch, -0wenn Ihre Liste durch NUL-Zeichen anstelle einer \nEwline getrennt ist (vorausgesetzt, in Ihrem Verzeichnisnamen ist / sind Zeilenumbrüche eingebettet).
αғsнιη
quelle
6

Inhalt von test.txt:

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

Skript zum Erstellen von A[123]Ordnern:

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

Ausgabe von ls:

A 12"x4" dir
A1
A2
A3
Xiota
quelle
Wie gehen Sie mit Eingaben um wie : A 12"x4" dir/B b/C c?
Ole Tange
Ole Tange - Dieser Kommentar scheint für die Frage völlig unangebracht zu sein.
DocSalvager
4

Verwenden Sie für einen einfachen Fall, wie im Eingabebeispiel der Frage gezeigt, einfach cut die Ausgabe und übergeben Sie sie an mkdirviaxargs

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

Für Fälle, in denen der Verzeichnisname Leerzeichen enthalten kann, können wir -d '\n'der Liste der Optionen Folgendes hinzufügen :

$ 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

Bei komplexeren Variationen, wie A 12"x4" dir/B b/C csie von @OleTange in den Kommentaren vorgeschlagen wurden, können Sie awkeine durch Null getrennte Liste anstelle einer durch Zeilenumbrüche getrennten Liste erstellen.

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

@dessert in den Kommentaren fragte sich, ob printfes anstelle von verwendet werden kann cut, und technisch gesehen kann es verwendet werden, zum Beispiel indem die gedruckte Zeichenfolge auf die Breite von nur 3 Zeichen begrenzt wird:

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

Nicht der sauberste Weg, aber er beweist, printf dass er verwendet werden kann. Dies wird natürlich problematisch, wenn der Verzeichnisname länger als 3 Zeichen ist.

Sergiy Kolodyazhnyy
quelle
Wie gehen Sie mit Eingaben um wie : A 12"x4" dir/B b/C c?
Ole Tange
@OleTange Siehe die Bearbeitung.
Sergiy Kolodyazhnyy
2

mit Perl:

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

Oder

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

wenn wir Leerzeichen in Verzeichnisnamen akzeptieren wollen

αғsнιη
quelle
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtLeerzeichen in Verzeichnisnamen abdecken. Ich muss endlich Perl lernen - danke!
Nachtisch
0

GNU Parallel ist möglicherweise zu viel für die Aufgabe, aber wenn Sie für jede Zeile andere Aufgaben ausführen, kann dies hilfreich sein:

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

Es behandelt korrekt mit Eingaben wie:

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