Crie diretórios com um nome do arquivo txt que contenha o caractere '/'

8

Eu tenho um arquivo .txt que contém um texto como este

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

Eu quero um script que leia o arquivo .txt para cada linha e crie um diretório com base na primeira palavra (A1, A2, A3)

Eu criei um script como este:

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

Enquanto o executo, ele cria o diretório A1 e também os subdiretórios B1 e C1. o mesmo acontece com outra linha (A2 * e A3 *)

O que devo fazer para criar apenas diretórios A1, A2, A3?

Não quero criar o nome como A1 / B1 / C1 com o caractere '/'. Eu só quero pegar a palavra antes do caractere '/' e torná-la nome do diretório. Apenas "A1" "A2" "A3".

maulana
fonte

Respostas:

13

Você só pode cuto 1st slash- dcampo elimited de cada linha e dar a lista para mkdir:

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

Exemplo de execução

$ 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

Você pode ter problemas com ARG_MAX se a sua lista tiver um grande número de nomes de diretórios; nesse caso, use o GNU parallel Instalar paraleloou da xargsseguinte maneira:

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

Enquanto parallelisso estiver coberto, a xargsabordagem não funcionará se os nomes de diretório contiverem espaços - você pode usar \0como delimitador de linha ou simplesmente instruir xargsa dividir apenas a entrada em caracteres de nova linha (conforme proposto por Martin Bonner ):

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

Caso algum dos campos contenha um caractere de nova linha, é necessário identificar as terminações "verdadeiras" da linha e substituir apenas os caracteres de nova linha por, por exemplo \0. Seria o caso awk, mas acho que é muito longe aqui.

sobremesa
fonte
Por xargs -a<(....)que ao invés de <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner apoia Monica
1
@MartinBonner Obrigado por esclarecer, isso é realmente mais simples do que minha trabordagem - acabei de perceber que parallelela é coberta por padrão.
dessert
@MartinBonner Por xargs -a<(....)que ao invés de um cachimbo - porque eu gosto dessa maneira, simples assim. :)
dessert
@AndreaCorbellini Oh, agora eu entendo. <(...)também ajuda com os espaços, por isso é definitivamente a melhor escolha - acho que ninguém estava usando esse descritor de arquivos.
dessert
@MartinBonner Mais interessante: por que ao cut <dirlist.txtinvés de apenas cut dirlist.txt?
dessert
11

Você precisa definir seu IFS='/'para reade, em seguida, atribuir cada primeiro campo à variável separada firste o restante dos campos à variável reste apenas trabalhar no valor do primeiro campo (ou você pode read -ar arraycriar uma única matriz e usar "${array[0]}"o valor do primeiro campo).

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

Ou em fila única para quem gosta:

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

Ou crie todos os diretórios de uma só vez:

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

A cotação ANSI-C$'...' é usada para lidar com nomes de diretório que contêm caracteres especiais.

Note que o _(pode ser qualquer caractere ou string) no final será argv [0] para o bash -c '...'e o $@will contém o restante dos parâmetros a partir de 1; sem isso no segundo comando, o primeiro parâmetro para o mkdirserá perdido.

Ao ${1%%/*}usar a expansão de substituição de parâmetro shell (POSIX sh / bash / Korn / zsh) , remove a maior correspondência possível de uma barra seguida por qualquer coisa até o final do parâmetro que passa para ela, que é uma linha que é lida por xargs ;

Ps:

  • Remova echona frente do mkdirpara criar esses diretórios.
  • Substitua -d'\n'por -0se sua lista estiver separada por caracteres NUL em vez de uma linha \neletrônica (suponha que exista / esteja nova linha incorporada no nome do diretório).
αғsнιη
fonte
6

Conteúdo de test.txt:

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

Script para criar A[123]pastas:

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

Saída de ls:

A 12"x4" dir
A1
A2
A3
xiota
fonte
Como você lida com informações como A 12"x4" dir/B b/C c:?
Ole Tange
Ole Tange - Esse comentário parece completamente fora de escopo para a pergunta.
DocSalvager
4

Para um caso simples, como mostrado no exemplo de entrada da pergunta, basta usar cut e passar a saída para mkdirviaxargs

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

Para lidar com casos em que o nome do diretório pode conter espaços, podemos adicionar -d '\n'à lista de opções:

$ 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

Para variações mais complexas, A 12"x4" dir/B b/C ccomo sugerido por @OleTange nos comentários, é possível awkcriar uma lista separada por nulos em vez de lista separada por nova linha.

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

@dessert nos comentários se perguntou se printfpode ser usado em vez de cut, e tecnicamente falando, pode ser usado, por exemplo, limitando a sequência impressa à largura de apenas três caracteres:

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

Não é a maneira mais limpa, mas prova que printf pode ser usada. Obviamente, isso fica problemático se o nome do diretório tiver mais de 3 caracteres.

Sergiy Kolodyazhnyy
fonte
Como você lida com informações como A 12"x4" dir/B b/C c:?
Ole Tange
@OleTange Veja a edição.
Sergiy Kolodyazhnyy
2

usando Perl:

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

Ou

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

se queremos aceitar espaços em nomes de diretórios

αғsнιη
fonte
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtpara cobrir espaços em nomes de dir. Finalmente preciso aprender Perl - obrigado!
sobremesa
0

O GNU Parallel pode ser um exagero para a tarefa, mas se você quiser fazer outras coisas para cada linha, poderá ser útil:

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

Ele lida corretamente com entradas como:

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