Створіть каталоги з назвою з файлу txt, який містить символ '/'


8

У мене є .txt файл, який містить такий текст

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

Я хочу, щоб сценарій, який читає .txt-файл для кожного рядка, потім створив каталог на основі першого слова (A1, A2, A3)

Я створив такий сценарій:

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

Поки я його запускаю, він створює каталог A1, а також створює підкаталоги B1 і C1. те ж саме відбувається і для іншого рядка (A2 * і A3 *)

Що мені робити, щоб створити лише каталоги A1, A2, A3?

Я не хочу робити ім’я типу A1 / B1 / C1 із символом '/'. Я просто хочу взяти слово перед символом '/' і зробити його ім'ям каталогу. Просто "А1" "А2" "А3".

Відповіді:


13

Ви можете тільки cutв 1ST slash- delimited поле кожного рядка і дати список на 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

Ви можете зіткнутися з проблемами ARG_MAX, якщо ваш список містить величезну кількість імен каталогів, у цьому випадку використовуйте GNU parallel Встановіть паралельноабо xargsнаступним чином:

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

Хоча parallelвін охоплений, xargsпідхід не буде працювати, якщо імена каталогів містять пробіли - ви можете використовувати \0як роздільник рядків, або просто вказувати xargsрозділити введення на символи нового рядка (як запропонував Мартін Боннер ):

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

Якщо будь-яке з полів містить символ нового рядка, потрібно було б визначити «справжні» закінчення рядка та замінити лише ті символи нового рядка, наприклад \0. Це було б справою awk, але я відчуваю, що тут занадто далеко.


Чому, xargs -a<(....)а не <dirlist.txt cut -d/ -f1 | xargs?
Мартін Боннер підтримує Моніку

1
@MartinBonner Дякую за уточнення, це дійсно простіше, ніж мій trпідхід - я просто зрозумів, що parallelвін охоплюється за замовчуванням.
десерт

@MartinBonner Чому xargs -a<(....), ніж труба - тому що мені це подобається так просто, як це. :)
десерт

@AndreaCorbellini О, тепер я розумію. <(...)допомагає і з пробілами, тому це, безумовно, кращий вибір - я не думаю, що хтось використовував цей дескриптор файлів.
десерт


11

Вам потрібно встановити IFS='/'для, readа потім призначити кожне перше поле в окрему змінну, firstа решта поля - на змінну restі просто працювати над значенням першого поля (Або ви можете read -ar arrayна один масив і використовувати "${array[0]}"для значення першого поля.):

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 "$'{@%%/*}'"' _

ANSI-C Цитування$'...' використовується для боротьби з іменами каталогів , що містять спеціальні символи.

Зауважте, що _(може бути будь-який символ або рядок) в кінці буде argv [0] до bash -c '...'та $@заповіт містить решту параметрів, починаючи з 1; без цього у другій команді перший параметр до mkdirволі буде втрачено.

У ${1%%/*}використанні оболонки (POSIX ш / Баш / Корн / ЗШ) розширення підстановки параметрів , видаляє довгий можливий матч косою риси з подальшим що - небудь до кінця параметра , що проходить в нього, що лінія , яка читається xargs ;

Ps:

  • Видаліть echoперед, mkdirщоб створити ці каталоги.
  • Замініть -d'\n'на те, -0якщо ваш список розділено символами NUL замість \newline (припускається, що в назві вашого каталогу є вбудований новий рядок).

6

Зміст test.txt:

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

Сценарій для створення A[123]папок:

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

Вихід ls:

A 12"x4" dir
A1
A2
A3

Як ви маєте справу з таким входом A 12"x4" dir/B b/C c:?
Оле Танге

Оле Танге - цей коментар видається абсолютно поза рамками питання.
DocSalvager

4

Для простого випадку, як показано у прикладі введення запитання, просто використовуйте cut та передайте висновок до mkdirчерезxargs

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

Для обробки випадків, коли ім'я каталогу може містити пробіли, ми можемо додати -d '\n'до списку параметрів:

$ 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

Для більш складних варіантів, таких як A 12"x4" dir/B b/C cзапропонований @OleTange в коментарях, можна звернутися awkдо створення списку, розділеного на нуль, замість списку, розділеного новим рядком .

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

@ десерт у коментарях цікавився, чи printfможна використовувати замість цього cut, а технічно кажучи, його можна використовувати, наприклад, обмежуючи надруковану рядок лише шириною 3 символів:

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

Не найчистіший спосіб, але це, як виявляється, printf може бути використаний. Звичайно, це стає проблематичним, якщо ім'я каталогу стає довше трьох символів.


Як ви маєте справу з таким входом A 12"x4" dir/B b/C c:?
Оле Танге

@OleTange Див. Редагування.
Сергій Колодяжний

2

за допомогою Perl:

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

Або

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

якщо ми хочемо прийняти пробіли в іменах dir


1
perl -ne 'mkdir for /^([^\/]+)/' list.txtдля покриття пробілів у назвах реж. Нарешті мені потрібно навчитися Perl - дякую!
десерт

0

Паралель GNU може бути непосильним для виконання завдання, але якщо ви збираєтеся робити інші речі для кожного рядка, то це може бути корисним:

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

Він правильно стосується введення даних, таких як:

A 12"x4" dir/B b/C c
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.