Crea directory con un nome dal file txt che contiene il carattere '/'


8

Ho un file .txt che contiene un testo come questo

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

Voglio uno script che legga il file .txt per ogni riga e quindi crei una directory basata sulla prima parola (A1, A2, A3)

Ho creato uno script come questo:

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

Mentre lo eseguo, crea la directory A1, quindi crea anche le sottodirectory B1 e C1. lo stesso accade per un'altra linea (A2 * e A3 *)

Cosa devo fare per creare solo directory A1, A2, A3?

Non voglio creare un nome come A1 / B1 / C1 con dentro il carattere '/'. Voglio solo prendere la parola prima del carattere '/' e renderlo il nome della directory. Solo "A1" "A2" "A3".

Risposte:


13

Si può solo cutla 1st Slash- dcampo elimited di ogni riga e dare l'elenco per mkdir:

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

Esempio di esecuzione

$ 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

Potresti riscontrare problemi ARG_MAX se l'elenco contiene un numero enorme di nomi di directory, in questo caso usa GNU parallel Installa in paralleloo xargscome segue:

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

Sebbene parallelsia coperto, l' xargsapproccio non funzionerà se i nomi delle directory contengono spazi: puoi usare \0come delimitatore di riga o semplicemente istruire xargsa dividere l'input solo sui caratteri di nuova riga (come proposto da Martin Bonner ) invece:

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

Nel caso in cui uno dei campi contenga un carattere di nuova riga, bisognerebbe identificare le terminazioni di linea "vere" e sostituire solo quei caratteri di nuova riga con es \0. Sarebbe un caso awk, ma penso che sia troppo preso qui.


Perché xargs -a<(....)piuttosto che <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner supporta Monica

1
@MartinBonner Grazie per il chiarimento, è davvero più semplice del mio trapproccio - ho appena capito che parallelè coperto per impostazione predefinita.
dessert

@MartinBonner Perché xargs -a<(....)piuttosto che una pipa - perché mi piace in questo modo, così semplice. :)
dessert

@AndreaCorbellini Oh, adesso capisco. <(...)aiuta anche con gli spazi, quindi è sicuramente la scelta migliore - non credo che nessuno stesse usando questo descrittore di file comunque.
dessert


11

È necessario impostare IFS='/'for reade quindi assegnare ogni primo campo in una variabile separata firste il resto dei campi in variabile reste lavorare solo sul valore del primo campo (oppure è possibile read -ar arrayun singolo array e utilizzare "${array[0]}"per il valore del primo campo):

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

O in linea singola per chi piace:

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

Oppure crea tutte le directory in una volta sola:

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

La quotazione ANSI-C$'...' viene utilizzata per gestire nomi di directory contenenti caratteri speciali.

Si noti che il _(può essere qualsiasi carattere o stringa) alla fine sarà argv [0] bash -c '...'e la $@volontà contiene il resto dei parametri a partire da 1; senza che nel secondo comando il primo parametro al mkdirverrà perso.

In ${1%%/*}utilizzando shell (POSIX sh / bash / Korn / zsh) parametro di espansione sostituzione , rimuove più lunga possibile corrispondenza di una barra seguita da nulla fino alla fine del parametro passando in esso che è una linea che letta da xargs ;

Ps:

  • Rimuovere echodavanti a mkdirper creare quelle directory.
  • Sostituisci -d'\n'con -0se la tua lista è separata da caratteri NUL invece che da una \newline (supponendo che ci siano / siano incorporati newline nel nome della tua directory).

6

Contenuto di test.txt:

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

Script per creare A[123]cartelle:

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

Uscita di ls:

A 12"x4" dir
A1
A2
A3

Come gestisci input come A 12"x4" dir/B b/C c:?
Ole Tange,

Ole Tange - Questo commento sembra completamente fuori portata per la domanda.
DocSalvager,

4

Per un caso semplice come mostrato nell'esempio di input della domanda, basta usare cut e passare l'output a mkdirviaxargs

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

Per la gestione dei casi in cui il nome della directory può contenere spazi, è possibile aggiungere -d '\n'all'elenco di opzioni:

$ 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

Per variazioni più complesse, A 12"x4" dir/B b/C ccome suggerito da @OleTange nei commenti, è possibile passare awka creare un elenco separato da null anziché un elenco separato da nuova riga.

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

@dessert nei commenti si chiedeva se printfpotesse essere usato al posto di cut, e tecnicamente parlando può essere usato, ad esempio limitando la stringa stampata alla larghezza di soli 3 caratteri:

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

Non è il modo più pulito, ma dimostra che printf può essere utilizzato. Naturalmente, ciò diventa problematico se il nome della directory supera i 3 caratteri.


Come gestisci input come A 12"x4" dir/B b/C c:?
Ole Tange,

@OleTange Vedi la modifica.
Sergiy Kolodyazhnyy,

2

usando Perl:

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

O

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

se vogliamo accettare spazi su dir-names


1
perl -ne 'mkdir for /^([^\/]+)/' list.txtper coprire gli spazi nei nomi delle directory. Finalmente ho bisogno di imparare il Perl - grazie!
dessert

0

GNU Parallel potrebbe essere eccessivo per l'attività, ma se hai intenzione di fare altre cose per ogni linea, potrebbe essere utile:

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

Gestisce correttamente input come:

A 12"x4" dir/B b/C c
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.