Cree directorios con un nombre del archivo txt que contenga el carácter '/'

8

Tengo un archivo .txt que contiene un texto como este

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

Quiero un script que lea el archivo .txt para cada línea y luego cree un directorio basado en la primera palabra (A1, A2, A3)

He creado un script como este:

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

Mientras lo ejecuto, crea el directorio A1 y luego también crea los subdirectorios B1 y C1. Lo mismo ocurre con otra línea (A2 * y A3 *)

¿Qué debo hacer para crear solo directorios A1, A2, A3?

No quiero que el nombre sea A1 / B1 / C1 con el carácter '/'. Solo quiero tomar la palabra antes del carácter '/' y convertirla en el nombre del directorio. Solo "A1" "A2" "A3".

maulana
fuente

Respuestas:

13

Puede simplemente cutel campo de barra 1diagonal deliminada de cada línea y dar la lista a mkdir:

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

Ejecución de ejemplo

$ 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

Puede encontrarse con problemas ARG_MAX si su lista contiene una gran cantidad de nombres de directorio, en este caso use GNU parallel Instalar en paraleloo xargslo siguiente:

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

Si bien parallelestá cubierto, el xargsenfoque no funcionará si los nombres de directorio contienen espacios; puede usarlo \0como delimitador de línea o simplemente indicar xargsque solo divida la entrada en los caracteres de nueva línea (como lo propuso 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

En caso de que alguno de los campos contenga un carácter de nueva línea, sería necesario identificar las terminaciones de línea "verdaderas" y reemplazar solo esos caracteres de nueva línea, por ejemplo \0. Ese sería un caso awk, pero siento que es demasiado descabellado aquí.

postre
fuente
¿Por qué en xargs -a<(....)lugar de <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner apoya a Monica
1
@ MartininBonner Gracias por aclarar, eso es de hecho más simple que mi trenfoque, me acabo de dar cuenta de que lo paralleltiene cubierto por defecto.
postre
@MartinBonner ¿xargs -a<(....) Por qué en lugar de una tubería ? Porque me gusta de esta manera, tan simple como eso. :)
postre
@AndreaCorbellini Oh, ahora entiendo. <(...)ayuda con los espacios también, por lo que definitivamente es la mejor opción: no creo que nadie estuviera usando este descriptor de archivos de todos modos.
postre
11

Debe configurar su IFS='/'para ready luego asignar cada primer campo en una variable separada firsty el resto de los campos en variable resty simplemente trabajar en el valor del primer campo (o puede read -ar arrayhacerlo en una sola matriz y usar "${array[0]}"para el valor del primer campo):

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

O en una sola línea para quienes les gusta:

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

O cree todos los directorios de una vez:

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

La cita ANSI-C$'...' se utiliza para tratar con nombres de directorio que contienen caracteres especiales.

Tenga en cuenta que el _(puede ser cualquier carácter o cadena) al final será argv [0] bash -c '...'y el $@testamento contiene el resto de los parámetros a partir de 1; sin eso en el segundo comando mkdirse perderá el primer parámetro al .

Al ${1%%/*}usar la expansión de sustitución de parámetros de shell (POSIX sh / bash / Korn / zsh) , elimina la coincidencia más larga posible de una barra inclinada seguida de cualquier cosa hasta el final del parámetro que pasa, que es una línea que se lee por xargs ;

PD:

  • Eliminar echodelante de mkdirpara crear esos directorios.
  • Reemplace -d'\n'con -0si su lista está separada con caracteres NUL en lugar de una línea \nelectrónica (se supone que hay / hay una nueva línea incrustada en el nombre de su directorio).
αғsнιη
fuente
6

Contenido de test.txt:

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

Script para crear A[123]carpetas:

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

Salida de ls:

A 12"x4" dir
A1
A2
A3
xiota
fuente
¿Cómo manejas las entradas como A 12"x4" dir/B b/C c:?
Ole Tange
Ole Tange: ese comentario parece completamente fuera de alcance para la pregunta.
DocSalvager
4

Para un caso simple como se muestra en el ejemplo de entrada de la pregunta, simplemente use cut y pase la salida a mkdirtravés dexargs

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

Para manejar casos donde el nombre del directorio puede contener espacios, podríamos agregar -d '\n'a la lista de opciones:

$ 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 variaciones más complejas, A 12"x4" dir/B b/C ccomo la sugerida por @OleTange en los comentarios, uno puede recurrir awka crear una lista separada por nulos en lugar de una lista separada por nueva línea.

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

@dessert en los comentarios se preguntó si printfse puede usar en lugar de cut, y técnicamente hablando, se puede usar, por ejemplo, limitando la cadena impresa al ancho de solo 3 caracteres:

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

No es la forma más limpia, pero demuestra que printf se puede usar. Por supuesto, esto se vuelve problemático si el nombre del directorio tiene más de 3 caracteres.

Sergiy Kolodyazhnyy
fuente
¿Cómo manejas las entradas como A 12"x4" dir/B b/C c:?
Ole Tange
@OleTange Ver la edición.
Sergiy Kolodyazhnyy
2

usando Perl:

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

O

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

si queremos aceptar espacios en los nombres de directorio

αғsнιη
fuente
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtpara cubrir espacios en nombres de directorios. Finalmente necesito aprender Perl, ¡gracias!
postre
0

GNU Parallel puede ser excesivo para la tarea, pero si va a hacer otras cosas para cada línea, entonces puede ser útil:

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

Trata correctamente con entradas como:

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