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

9

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 eu o executo, ele cria o diretório A1 e também cria sub-diretórios B1 e C1. o mesmo acontece para outra linha (A2 * e A3 *)

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

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

    
por maulana 24.05.2018 / 06:36

6 respostas

13

Você pode apenas cut o 1 st slash- d elimited de cada linha e fornecer 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 encontrar problemas ARG_MAX se sua lista contiver um número enorme de nomes de diretório , neste caso use GNU parallel ouxargsdaseguinteforma:

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

Embora parallel tenha sido coberto, a abordagem xargs não funcionará se os nomes dos diretórios contiverem espaços - você pode usar xargs como o delimitador de linha ou simplesmente instruir awk para dividir somente a entrada em caracteres de nova linha (como 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

No caso de qualquer um dos campos conter um caractere de nova linha, seria necessário identificar os finais de linha "verdadeiros" e substituir apenas os caracteres de nova linha por, por exemplo, %código%. Isso seria um caso para %code% , mas acho que é muito improvável aqui.

    
por dessert 24.05.2018 / 07:37
10

Você precisa definir seu IFS='/' para read e, em seguida, atribuir cada primeiro campo à variável separada first e o restante dos campos à variável rest e apenas trabalhar no valor do primeiro campo (ou você pode read -ar array para um único array e use "${array[0]}" para o primeiro valor do campo.):

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

Ou em linha ú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 ANSI-C Quoting $'...' é usada para lidar com nomes de diretório contendo caracteres especiais.

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

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

P:

  • Remova echo na frente do mkdir para criar esses diretórios.
  • Substitua -d'\n' por -0 se sua lista estiver separada por caracteres NUL em vez de \n ewline (supõe-se que haja nova linha no nome do diretório e, claro, não deve haver um nome de diretório com caracteres NUL) .
por devWeek 24.05.2018 / 06:54
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
    
por xiota 24.05.2018 / 06:55
5

Para casos simples, como mostrado no exemplo de entrada da pergunta, use apenas cut e passe a saída para mkdir via xargs

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, como A 12"x4" dir/B b/C c , como sugerido por @OleTange nos comentários, pode-se transformar em awk para criar uma lista separada por nulo em vez de uma lista separada por nova linha.

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

@dessert nos comentários questionou se printf pode 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:

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

Não é o caminho mais limpo, mas prova que printf pode ser usado. Claro, isso fica problemático se o nome do diretório se tornar mais longo que 3 caracteres.

    
por Sergiy Kolodyazhnyy 24.05.2018 / 07:32
2

usando o Perl:

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

Ou

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

se quisermos aceitar espaços em nomes de diretas

    
por JJoao 24.05.2018 / 21:06
0

O GNU Parallel pode ser um exagero para a tarefa, mas se você for fazer outras coisas para cada linha, então pode ser útil:

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

lida corretamente com entradas como:

A 12"x4" dir/B b/C c
    
por Ole Tange 25.05.2018 / 13:26