Twórz katalogi o nazwie z pliku txt, które zawierają znak „/”

8

Mam plik .txt, który zawiera taki tekst

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

Chcę skryptu, który wczyta plik .txt dla każdej linii, a następnie utworzy katalog na podstawie pierwszego słowa (A1, A2, A3)

Stworzyłem następujący skrypt:

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

Kiedy go uruchamiam, tworzy katalog A1, a następnie tworzy podkatalogi B1 i C1. to samo dzieje się w przypadku innej linii (A2 * i A3 *)

Co powinienem zrobić, aby utworzyć tylko katalogi A1, A2, A3?

Nie chcę tworzyć nazwy A1 / B1 / C1 ze znakiem „/”. Chcę tylko wziąć słowo przed znak „/” i nadać mu nazwę katalogu. Tylko „A1” „A2” „A3”.

maulana
źródło

Odpowiedzi:

13

Można tylko cutw 1st slash- delimited pole każdej linii i podać listę do mkdir:

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

Przykładowy przebieg

$ 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

Możesz napotkać problemy ARG_MAX , jeśli twoja lista zawiera ogromną liczbę nazw katalogów, w tym przypadku użyj GNU parallel Zainstaluj równoleglelub xargsw następujący sposób:

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

Chociaż paralleljest to opisane, xargspodejście nie będzie działać, jeśli nazwy katalogów zawierają spacje - możesz użyć \0jako separatora wiersza lub po prostu poinstruować, xargsaby podzielić dane wejściowe tylko na znaki nowego wiersza (zgodnie z propozycją Martina Bonnera ):

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

W przypadku, gdy którekolwiek z pól zawiera znak nowej linii, należy zidentyfikować „prawdziwe” zakończenia linii i zastąpić tylko te znaki nowej linii np \0. To byłby przypadek awk, ale wydaje mi się, że jest to zbyt daleko idące.

deser
źródło
Dlaczego xargs -a<(....)zamiast <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner wspiera Monikę
1
@MartinBonner Dzięki za wyjaśnienie, to jest rzeczywiście prostsze niż moje trpodejście - właśnie zdałem sobie sprawę, że parallelma to domyślnie uwzględnione.
deser
@MartinBonner Dlaczego xargs -a<(....)zamiast fajki - ponieważ lubię to w ten sposób, tak proste. :)
deser
@AndreaCorbellini Och, teraz rozumiem. <(...)pomaga również ze spacjami, więc jest to zdecydowanie lepszy wybór - nie sądzę, aby ktokolwiek i tak używał tego deskryptora pliku.
deser
11

Musisz ustawić IFS='/'dla, reada następnie przypisać każde pierwsze pole do osobnej zmiennej, firsta resztę pól do zmiennej resti po prostu pracować na wartości pierwszego pola (Lub możesz read -ar arraydo pojedynczej tablicy i użyć "${array[0]}"dla wartości pierwszego pola.):

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

Lub w pojedynczej linii dla tych, którzy to lubią:

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

Lub utwórz wszystkie katalogi za jednym razem:

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

ANSI-C Cytowanie$'...' służy do czynienia z nazw katalogów zawierających znaki specjalne.

Zauważ, że _(może być dowolnym znakiem lub łańcuchem) na końcu będzie argv [0] do, bash -c '...'a $@wola będzie zawierać pozostałe parametry, począwszy od 1; bez tego w drugim poleceniu pierwszy parametr do mkdirzostanie utracony.

W ${1%%/*}pomocą powłoki (POSIX sh / atakujących / Korn / zsh) Parametry ekspansji podstawienie usuwa najdłuższy dopasowanie ukośnikiem następnie niczego do końca parametru przechodzącej w nią, która to linia odczytywane przez xargs ;

Ps:

  • Usuń echoprzed, mkdiraby utworzyć te katalogi.
  • Wymień -d'\n'się -0, czy lista jest oddzielone znakami NUL zamiast o \newline (niby tam jest / są osadzone nowego wiersza w nazwie katalogu).
αғsнιη
źródło
6

Zawartość test.txt:

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

Skrypt do tworzenia A[123]folderów:

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

Wyjście ls:

A 12"x4" dir
A1
A2
A3
Xiota
źródło
Jak radzisz sobie z danymi wejściowymi, takimi jak A 12"x4" dir/B b/C c:?
Ole Tange
Ole Tange - Ten komentarz wydaje się całkowicie poza zakresem pytania.
DocSalvager
4

W prostym przypadku, jak pokazano w przykładzie wprowadzania pytania, wystarczy użyć cut i przekazać dane wyjściowe mkdirprzez viaxargs

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

Do obsługi przypadków, w których nazwa katalogu może zawierać spacje, możemy dodać -d '\n'do listy opcji:

$ 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

W przypadku bardziej skomplikowanych odmian, takich jak A 12"x4" dir/B b/C csugerowane przez @OleTange w komentarzach, można przejść awkdo tworzenia listy rozdzielonej wartościami zerowymi zamiast listy rozdzielanej znakiem nowej linii.

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

@dessert w komentarzach zastanawiał się, czy printfmożna go użyć cut, a technicznie rzecz biorąc można go użyć, na przykład poprzez ograniczenie drukowanego ciągu do szerokości tylko 3 znaków:

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

Nie jest to najczystszy sposób, ale okazuje się, że printf można go użyć. Oczywiście staje się to problematyczne, jeśli nazwa katalogu staje się dłuższa niż 3 znaki.

Sergiy Kolodyazhnyy
źródło
Jak radzisz sobie z danymi wejściowymi, takimi jak A 12"x4" dir/B b/C c:?
Ole Tange
@OleTange Zobacz edycję.
Sergiy Kolodyazhnyy
2

używając Perla:

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

Lub

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

jeśli chcemy zaakceptować spacje w nazwach katalogów

αғsнιη
źródło
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtna spacje w nazwach katalogów. W końcu muszę się nauczyć Perla - dziękuję!
deser
0

GNU Parallel może być przesadą dla zadania, ale jeśli zamierzasz robić inne rzeczy dla każdej linii, może to być przydatne:

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

Prawidłowo radzi sobie z danymi wejściowymi, takimi jak:

A 12"x4" dir/B b/C c
Ole Tange
źródło