Tạo thư mục có tên từ tệp txt chứa ký tự '/'


8

Tôi có một tệp .txt chứa văn bản như thế này

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

Tôi muốn một tập lệnh đọc tệp .txt cho mỗi dòng sau đó tạo một thư mục dựa trên từ đầu tiên (A1, A2, A3)

Tôi đã tạo tập lệnh như thế này:

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

Trong khi tôi chạy nó, nó tạo thư mục A1 thì nó cũng tạo thư mục con B1 và ​​C1. điều tương tự cũng xảy ra với một dòng khác (A2 * và A3 *)

Tôi nên làm gì để chỉ tạo các thư mục A1, A2, A3?

Tôi không muốn đặt tên như A1 / B1 / C1 với ký tự '/' trong đó. Tôi chỉ muốn lấy từ trước ký tự '/' và đặt tên thư mục. Chỉ cần "A1" "A2" "A3".

Câu trả lời:


13

Bạn có thể chỉ cutcác 1st slash- dlĩnh vực elimited của mỗi dòng và đưa ra danh sách công việc mkdir:

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

Chạy ví dụ

$ 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

Bạn có thể gặp phải các vấn đề ARG_MAX nếu danh sách của bạn chứa một số lượng lớn tên thư mục, trong trường hợp này sử dụng GNU parallel Cài đặt song songhoặc xargsnhư sau:

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

Mặc dù parallelđược bảo vệ, xargscách tiếp cận sẽ không hoạt động nếu tên thư mục chứa khoảng trắng - thay vào đó, bạn có thể sử dụng \0làm dấu phân cách dòng hoặc chỉ đơn giản là xargschỉ phân tách đầu vào trên các ký tự dòng mới (như đề xuất của 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

Trong trường hợp bất kỳ trường nào có chứa một ký tự dòng mới, người ta sẽ cần xác định các kết thúc dòng thực sự của YouTube và chỉ thay thế các ký tự dòng mới đó bằng vd \0. Đó sẽ là một trường hợp awk, nhưng tôi cảm thấy nó quá xa vời ở đây.


Tại sao xargs -a<(....)chứ không phải <dirlist.txt cut -d/ -f1 | xargs?
Martin Bonner hỗ trợ Monica

1
@MartinBonner Cảm ơn bạn đã làm rõ, điều đó thực sự đơn giản hơn trcách tiếp cận của tôi - chỉ cần nhận ra rằng parallelnó được bao phủ theo mặc định.
tráng miệng

@MartinBonner Tại sao xargs -a<(....)chứ không phải là một đường ống - bởi vì tôi thích nó theo cách này, đơn giản như vậy. :)
món tráng miệng

@AndreaCorbellini ơi, giờ thì tôi đã hiểu. <(...)cũng giúp với không gian, vì vậy chắc chắn đó là sự lựa chọn tốt hơn - tôi không nghĩ có ai đang sử dụng bộ mô tả tệp này.
tráng miệng


11

Bạn cần đặt IFS='/'cho readvà sau đó gán từng trường đầu tiên thành biến riêng biệt firstvà phần còn lại của trường thành biến restvà chỉ hoạt động trên giá trị của trường đầu tiên (Hoặc bạn có thể read -ar arraycho một mảng duy nhất và sử dụng "${array[0]}"cho giá trị của trường đầu tiên.):

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

Hoặc trong một dòng duy nhất cho những người thích nó:

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

Hoặc tạo tất cả các thư mục trong một lần:

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

Các ANSI-C trích dẫn$'...' được sử dụng để đối phó với tên thư mục chứa ký tự đặc biệt.

Lưu ý rằng _(có thể là bất kỳ ký tự hoặc chuỗi) nào ở cuối sẽ được argv [0] thành bash -c '...'$@sẽ chứa phần còn lại của các tham số bắt đầu từ 1; không có lệnh đó trong tham số thứ hai, tham số đầu tiên mkdirsẽ bị mất.

Khi ${1%%/*}sử dụng mở rộng thay thế tham số shell (POSIX sh / bash / Korn / zsh) , loại bỏ khớp dài nhất có thể của dấu gạch chéo theo sau là bất cứ thứ gì cho đến khi kết thúc tham số truyền vào nó, đó là một dòng được đọc bởi xargs ;

Ps:

  • Xóa echoở phía trước mkdirđể tạo các thư mục.
  • Thay thế -d'\n'bằng -0nếu danh sách của bạn được phân tách bằng các ký tự NUL thay vì \newline (giả sử có / được nhúng dòng mới trong tên thư mục của bạn).

6

Nội dung của test.txt:

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

Script để tạo A[123]thư mục:

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

Đầu ra của ls:

A 12"x4" dir
A1
A2
A3

Làm thế nào để bạn đối phó với đầu vào như : A 12"x4" dir/B b/C c?
Ole Tange

Ole Tange - Nhận xét đó dường như hoàn toàn nằm ngoài phạm vi của câu hỏi.
DocSalvager

4

Đối với trường hợp đơn giản như trong ví dụ đầu vào của câu hỏi, chỉ cần sử dụng cut và chuyển đầu ra mkdirquaxargs

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

Để xử lý các trường hợp tên thư mục có thể chứa dấu cách, chúng ta có thể thêm -d '\n'vào danh sách các tùy chọ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

Đối với các biến thể phức tạp hơn, A 12"x4" dir/B b/C cnhư được đề xuất bởi @OleTange trong các nhận xét, người ta có thể chuyển awksang tạo danh sách được phân tách bằng null thay vì danh sách được phân tách bằng dòng mới.

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

@dPlay trong các ý kiến ​​tự hỏi liệu printfcó thể được sử dụng thay thế hay không cut, và về mặt kỹ thuật, nó có thể được sử dụng, chẳng hạn như thông qua việc giới hạn chuỗi in chỉ có chiều rộng 3 ký tự:

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

Không phải là cách sạch nhất, nhưng nó chứng minh printf có thể được sử dụng. Tất nhiên, điều này có vấn đề nếu tên thư mục trở nên dài hơn 3 ký tự.


Làm thế nào để bạn đối phó với đầu vào như : A 12"x4" dir/B b/C c?
Ole Tange

@OleTange Xem chỉnh sửa.
Sergiy Kolodyazhnyy

2

sử dụng Perl:

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

Hoặc là

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

nếu chúng ta muốn chấp nhận khoảng trắng trên tên dir


1
perl -ne 'mkdir for /^([^\/]+)/' list.txtđể bao phủ không gian trong tên dir. Cuối cùng tôi cần học Perl - cảm ơn bạn!
tráng miệng

0

GNU Parallel có thể quá mức cần thiết cho tác vụ, nhưng nếu bạn định làm những thứ khác cho mỗi dòng, thì nó có thể hữu ích:

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

Nó xử lý chính xác với đầu vào như:

A 12"x4" dir/B b/C c
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.