Создать каталоги с именем из текстового файла, которые содержат символ «/»

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 О, теперь я понимаю. <(...)также помогает с пробелами, так что это определенно лучший выбор - я не думаю, что кто-либо использовал этот файловый дескриптор в любом случае.
десерт
@MartinBonner Еще интереснее: почему cut <dirlist.txtвместо просто cut dirlist.txt?
десерт
10

Вам нужно установить значение IFS='/'for, 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 sh / bash / Korn / zsh) удаляется максимально длинное совпадение косой черты, за которым следует что-либо до конца передаваемого в него параметра, представляющего собой строку, читаемую xargs ;

Ps:

  • Удалите echoперед, mkdirчтобы создать эти каталоги.
  • Замените -d'\n'на, -0если ваш список разделен символами NUL вместо \newline (предполагается, что в названии каталога есть / встроенная новая строка).
αғsнιη
источник
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
xiota
источник
Как вы справляетесь с входом , как: 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

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

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

Не самый чистый способ, но он доказывает, что printf может быть использован. Конечно, это становится проблематичным, если имя каталога становится длиннее 3 символов.

Сергей Колодяжный
источник
Как вы справляетесь с входом , как: 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-именах

αғsнιη
источник
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtчтобы закрыть пробелы в именах dir. Мне, наконец, нужно выучить Perl - спасибо!
десерт
0

GNU Parallel может быть излишним для этой задачи, но если вы собираетесь делать другие вещи для каждой строки, то это может быть полезно:

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

Он правильно обрабатывает ввод, как:

A 12"x4" dir/B b/C c
Оле Танге
источник