Buat direktori dengan nama dari file txt yang berisi karakter '/'

8

Saya memiliki file .txt yang berisi teks seperti ini

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

Saya ingin skrip yang membaca file .txt untuk setiap baris kemudian membuat direktori berdasarkan kata pertama (A1, A2, A3)

Saya telah membuat skrip seperti ini:

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

Ketika saya menjalankannya, ia menciptakan direktori A1 kemudian juga membuat sub-direktori B1 dan C1. hal yang sama terjadi pada saluran lain (A2 * dan A3 *)

Apa yang harus saya lakukan untuk membuat hanya direktori A1, A2, A3?

Saya tidak ingin membuat nama seperti A1 / B1 / C1 dengan karakter '/' di dalamnya. Saya hanya ingin mengambil kata sebelum karakter '/' dan menjadikannya nama direktori. Hanya "A1" "A2" "A3".

maulana
sumber

Jawaban:

13

Anda hanya bisa cutdengan 1st slash- dbidang dieliminasi dari setiap baris dan memberikan daftar ke mkdir:

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

Contoh dijalankan

$ 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

Anda dapat mengalami masalah ARG_MAX jika daftar Anda menyimpan banyak nama direktori, dalam hal ini gunakan GNU parallel Instal paralelatau xargssebagai berikut:

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

Meskipun parallelsudah dibahas, xargspendekatan tidak akan berfungsi jika nama direktori berisi spasi - Anda dapat menggunakan \0sebagai pembatas garis atau hanya menginstruksikan xargsuntuk hanya membagi input pada karakter baris baru (seperti yang diusulkan oleh Martin Bonner ) sebagai gantinya:

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

Dalam hal salah satu bidang berisi karakter baris baru, seseorang perlu mengidentifikasi ujung baris "benar" dan hanya mengganti karakter baris baru tersebut dengan mis \0. Itu akan menjadi kasus untuk awk, tapi saya merasa itu terlalu jauh di sini.

pencuci mulut
sumber
Kenapa xargs -a<(....)bukan <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner mendukung Monica
1
@ MartinBonner Terima kasih telah mengklarifikasi, itu memang lebih sederhana daripada trpendekatan saya - baru menyadari bahwa parallelsudah dibahas secara default.
hidangan penutup
@ Martin Mengapa xargs -a<(....)bukan pipa - karena saya suka seperti ini, sesederhana itu. :)
hidangan penutup
@AndreaCorbellini Oh, sekarang saya mengerti. <(...)membantu dengan spasi juga, jadi itu jelas pilihan yang lebih baik - saya tidak berpikir siapa pun menggunakan deskriptor file ini pula.
hidangan penutup
@MartinBonner Lebih menarik: Kenapa cut <dirlist.txtbukannya adil cut dirlist.txt?
hidangan penutup
11

Anda perlu mengatur IFS='/'untuk readdan kemudian menetapkan masing-masing bidang pertama ke dalam variabel yang terpisah firstdan sisa bidang ke dalam variabel restdan hanya bekerja pada nilai bidang pertama (Atau Anda bisa read -ar arrayke satu array dan gunakan "${array[0]}"untuk nilai bidang pertama.):

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

Atau dalam satu baris untuk mereka yang menyukainya:

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

Atau buat semua direktori dalam sekali jalan:

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

The ANSI-C Mengutip$'...' digunakan untuk menangani nama direktori yang berisi karakter khusus.

Perhatikan bahwa _(dapat berupa karakter atau string) di akhir akan menjadi argv [0] ke bash -c '...'dan $@akan berisi sisa parameter mulai dari 1; tanpa itu di perintah kedua parameter pertama ke mkdirakan hilang.

Dalam ${1%%/*}menggunakan ekspansi substitusi parameter shell (POSIX sh / bash / Korn / zsh) , menghapus kecocokan terpanjang yang mungkin dari sebuah slash diikuti oleh apa pun hingga akhir parameter lewat ke dalamnya yang merupakan garis yang dibaca oleh xargs ;

Ps:

  • Hapus echodi depan mkdiruntuk membuat direktori tersebut.
  • Ganti -d'\n'dengan -0jika daftar Anda dipisahkan dengan karakter NUL dan bukan \newline (seharusnya ada / tertanam baris baru dalam nama direktori Anda).
αғsнιη
sumber
6

Isi dari test.txt:

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

Script untuk membuat A[123]folder:

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

Output dari ls:

A 12"x4" dir
A1
A2
A3
xiota
sumber
Bagaimana Anda menangani input seperti A 12"x4" dir/B b/C c:?
Ole Tange
Ole Tange - Komentar itu tampaknya benar-benar di luar jangkauan untuk pertanyaan.
DocSalvager
4

Untuk kasus sederhana seperti yang ditunjukkan pada contoh input pertanyaan, cukup gunakan cut dan berikan output ke mkdirviaxargs

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

Untuk menangani kasus-kasus di mana nama direktori dapat berisi spasi, kami dapat menambahkan -d '\n'ke daftar opsi:

$ 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

Untuk variasi yang lebih kompleks, seperti yang A 12"x4" dir/B b/C cdisarankan oleh @OleTange dalam komentar, orang dapat beralih awkuntuk membuat daftar yang dipisahkan nol alih-alih daftar yang dipisahkan baris baru.

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

@dabut dalam komentar bertanya-tanya apakah printfdapat digunakan sebagai ganti cut, dan secara teknis dapat digunakan, misalnya melalui membatasi string yang dicetak hanya dengan lebar 3 karakter saja:

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

Bukan cara terbersih, tetapi terbukti printf bisa digunakan. Tentu saja, ini menjadi bermasalah jika nama direktori menjadi lebih dari 3 karakter.

Sergiy Kolodyazhnyy
sumber
Bagaimana Anda menangani input seperti A 12"x4" dir/B b/C c:?
Ole Tange
@OleTange Lihat hasil edit.
Sergiy Kolodyazhnyy
2

menggunakan Perl:

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

Atau

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

jika kita ingin menerima spasi pada dir-names

αғsнιη
sumber
1
perl -ne 'mkdir for /^([^\/]+)/' list.txtuntuk menutupi spasi dalam nama dir. Saya akhirnya perlu belajar Perl - terima kasih!
hidangan penutup
0

GNU Parallel mungkin berlebihan untuk tugas tersebut, tetapi jika Anda akan melakukan hal-hal lain untuk setiap baris, maka mungkin berguna:

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

Ini berkaitan dengan input seperti:

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