สร้างไดเรกทอรีที่มีชื่อจากไฟล์ txt ซึ่งมีอักขระ '/'


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 ด้วยอักขระ '/' ในนั้น ฉันต้องการคำก่อนหน้าอักขระ '/' และทำให้เป็นชื่อไดเรกทอรี เพียงแค่ "A1" "A2" "A3"

คำตอบ:


13

คุณก็สามารถเซนต์ slash- ฟิลด์ elimited ของแต่ละบรรทัดและให้รายการเพื่อ:cut1dmkdir

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ให้แยกอินพุตกับอักขระขึ้นบรรทัดใหม่ (ตามที่เสนอโดย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

ในกรณีใด ๆ ของฟิลด์ประกอบด้วยอักขระขึ้นบรรทัดใหม่หนึ่งจะต้องระบุ“true” \0ปลายสายและแทนที่เฉพาะผู้ที่ตัวอักษรขึ้นบรรทัดใหม่ด้วยเช่น นั่นเป็นกรณีสำหรับawkแต่ฉันรู้สึกว่ามันมาไกลเกินกว่าที่นี่


ทำไมxargs -a<(....)มากกว่า<dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner สนับสนุน Monica

1
@ มาร์ตินโบนเนอร์ขอบคุณที่ให้ความกระจ่างว่ามันง่ายกว่าtrวิธีของฉัน- เพิ่งรู้ว่าparallelมันครอบคลุมโดยค่าเริ่มต้น
ของหวาน

@ มาร์ตินโบนเนอร์ทำไมxargs -a<(....)มากกว่าเป็นท่อ - เพราะฉันชอบวิธีนี้ง่ายเหมือนที่ :)
ของหวาน

@AndreaCorbellini โอ้ตอนนี้ฉันเข้าใจ <(...)ช่วยในเรื่องของช่องว่างด้วยดังนั้นมันจึงเป็นตัวเลือกที่ดีกว่าแน่นอน - ฉันไม่คิดว่ามีใครใช้ตัวอธิบายไฟล์นี้อยู่ดี
ของหวาน


11

คุณจำเป็นต้องตั้งค่าIFS='/'สำหรับ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 quoting$'...'จะใช้ในการจัดการกับชื่อไดเรกทอรีที่มีตัวอักษรพิเศษ

โปรดทราบว่า_(สามารถเป็นอักขระหรือสตริงใด ๆ ) ในตอนท้ายจะเป็น argv [0] ถึงbash -c '...'และ$@จะมีส่วนที่เหลือของพารามิเตอร์เริ่มต้นจาก 1; โดยไม่มีคำสั่งที่สองพารามิเตอร์แรกที่mkdirจะหายไป

ใน${1%%/*}การใช้เปลือก (POSIX SH / ทุบตี / Korn / zsh) ขยายตัวเปลี่ยนตัวพารามิเตอร์เอาการแข่งขันเป็นไปได้ที่ยาวที่สุดของการเฉือนตามด้วยอะไรจนถึงสิ้นพารามิเตอร์ผ่านเข้าไปในนั้นซึ่งเป็นเส้นที่อ่านโดยที่xargs ;

PS:

  • ลบechoด้านหน้าmkdirเพื่อสร้างไดเร็กทอรีเหล่านั้น
  • แทนที่-d'\n'ด้วย-0หากรายการของคุณถูกคั่นด้วยอักขระ NUL แทน\newline (ควรมี / ถูกฝังบรรทัดใหม่ในชื่อไดเรกทอรีของคุณ)

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

คุณจัดการกับอินพุตเช่นA 12"x4" dir/B b/C cอย่างไร:
Ole Tange

Ole Tange - ความคิดเห็นนั้นดูไม่ครอบคลุมสำหรับคำถาม
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การสร้างรายการที่คั่นด้วย null แทนรายการที่คั่นด้วยบรรทัดใหม่

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อย่างไร:
Ole Tange

@OleTange ดูการแก้ไข
Sergiy Kolodyazhnyy

2

ใช้ Perl:

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

หรือ

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

ถ้าเราต้องการยอมรับช่องว่างในชื่อ dir


1
perl -ne 'mkdir for /^([^\/]+)/' list.txtเพื่อครอบคลุมช่องว่างในชื่อ dir ในที่สุดฉันก็ต้องเรียนรู้ Perl - ขอบคุณ!
ของหวาน

0

GNU Parallel อาจ overkill สำหรับงาน แต่ถ้าคุณจะทำสิ่งอื่น ๆ สำหรับแต่ละบรรทัดก็อาจเป็นประโยชน์:

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

มันเกี่ยวข้องอย่างถูกต้องกับการป้อนข้อมูลเช่น:

A 12"x4" dir/B b/C c
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.