Vấn đề với khoảng trắng trong tên tệp


8

Tôi muốn làm một cái gì đó lặp đi lặp lại trong một danh sách các tập tin. Các tập tin trong câu hỏi có khoảng trắng trong tên của chúng:

david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha
-rw-rw-r-- 1 david david  0 Mai  8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai  8 11:55 haha (copy)

Bây giờ tôi muốn thống kê từng tệp này:

david@david: echo '
for file in $(ls)
do
stat $file
done' | bash

(Tôi sử dụng echo và pipe để viết các lệnh nhiều dòng.)

Khi tôi làm điều đó, nó hoạt động chính xác trên những tệp không có bất kỳ khoảng trắng nào trong tên của chúng. Nhưng những người khác ...

stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory

Thay đổi $(ls)để "$(ls)"hoặc $fileđể "$file"không làm việc. Tôi có thể làm gì?

Biên tập:

echo '
for files in *
do
stat "$files"
done' | bash

lừa Vì tôi mới sử dụng bash, tôi muốn giữ mọi thứ đơn giản nhất có thể - vì vậy không có gì bằng cách cố gắng thoát khỏi không gian, hoặc sử dụng xargshoặc giải pháp với read -r, mặc dù chúng giải quyết được vấn đề.

Như một số người đã hỏi: Có, sử dụng điều này thay vì stat *là lạ. Nhưng tôi chỉ muốn tìm một cách chung để áp dụng cùng một lệnh trên một loạt các tên tệp trong bash, sử dụng vòng lặp for. Vì vậy, statcó thể đứng cho gzip, gpghoặc rm.


1
có chuyện gì với bạn stat *vậy (;-)
Rmano

Đồng thời xem mywiki.wooledge.org/ParsingLs
Rmano

Tôi chỉ sử dụng stat làm ví dụ. :) Tôi muốn thu thập tên tệp bằng ls, và sau đó sử dụng kết quả của ls trong vòng lặp bash.
dùng258532

Câu trả lời:


12

Nhiều trích dẫn từ echo 'là phức tạp điều.

Bạn chỉ có thể sử dụng:

for f in *; do stat -- "$f"; done

Nhưng cũng

stat -- * 

... và nếu bạn muốn thu thập các tệp và sau đó áp dụng lệnh (tại sao?), bạn có thể sử dụng (nhưng hãy cẩn thận với tệp chứa các dòng mới ... (1))

for f in *; do echo "$f"; done | xargs stat --

... Và nếu bạn cũng muốn các tập tin ẩn, chỉ cần sử dụng * .*như một mẫu, nhưng sau đó hãy nhớ rằng ...sẽ có trong tập hợp .

Như một bên, bạn không nên phân tích lsđầu ra .


(1) nhưng nếu bạn có tên tệp với dòng mới, bạn phần nào xứng đáng với nó ... ;-)


"và sau đó áp dụng lệnh (tại sao?)" -> stat chỉ đóng vai trò là một lệnh tùy ý, khi tôi đang thử cách thực hiện các vòng lặp bash với tên tệp. Nó có thể là gpg, gzip hoặc bất cứ điều gì khác.
dùng258532

@ user258532 bất cứ lệnh nào, luôn luôn sử dụng for f in *; do command "$f"; done. Không bao giờ phân tích ls, chắc chắn không bao giờ làm điều đó trong một for vòng lặp và tại sao sử dụng echo?
terdon

echo: Bởi vì tôi thích viết các lệnh trên nhiều dòng ... :)
user258532

2
@ user258532 Hả? Tại sao bạn cần tiếng vang cho điều đó? Chỉ cần nhấn enter và tiếp tục trên một dòng mới. Nếu bạn kết thúc một dòng với một quote mở hoặc trên một trong những do, |, &&vv, bạn có thể tiếp tục trên dòng mới. Hoặc là hoặc sử dụng heredocs. Không có lý do để sử dụng echovà nó cũng có thể gây ra vấn đề.
terdon

"Chỉ cần nhấn enter và tiếp tục trên một dòng mới." Ôi. :-D Bây giờ IQ của tôi chính thức dưới 0 - bạn biết đấy, tôi thực sự mới đối với bash và những thứ tương tự. Nhưng tôi thực sự kiếm được tiền với R (bộ thống kê và ngôn ngữ kịch bản).
dùng258532

6

Lưu ý phụ: bạn có thể phân chia các lệnh dài / phức tạp trên nhiều dòng bằng cách thêm khoảng trắng theo sau dấu gạch chéo ngược và nhấn Entermỗi lần bạn muốn bắt đầu viết vào một dòng mới, thay vì bỏ qua nhiều quy trình bằng cách sử dụng echo [...] | bash; Ngoài ra, bạn nên gửi kèm theo $filedấu ngoặc kép để tránh bị statphá vỡ trong trường hợp tên tệp chứa dấu cách:

for file in $(ls); \
do \
stat "$file"; \
done

Vấn đề là $(ls)mở rộng ra một danh sách tên tệp chứa khoảng trắng, và điều tương tự cũng sẽ xảy ra với "$(ls)".

Ngay cả khi giải quyết vấn đề này, phương pháp này vẫn sẽ phá vỡ tên tệp chứa dấu gạch chéo ngược và tên tệp chứa dòng mới (như được chỉ ra bởi terdon).

Một giải pháp cho cả hai vấn đề sẽ là dẫn đầu ra của findmột whilevòng lặp chạy read -rđể mỗi lần lặp lại read -rsẽ lưu một dòng findđầu ra vào $file:

find . -maxdepth 1 -type f | while read -r file; do \
    stat "$file"; \
done

1
và các tập tin ẩn? :)
AB

5
Điều đó vẫn sẽ thất bại đối với tên tệp có chứa dòng mới. Đừng phân tích cú pháp ls. Không bao giờ.
terdon

4
@ user258532 không, không. Nghiêm túc mà nói, đừng phân tíchls . Có những cách tốt hơn và mạnh mẽ hơn. Bạn cũng có thể muốn đọc điều này: Tại sao * không * phân tích `ls`? để biết thêm chi tiết.
terdon

1
Vấn đề ở đây không phải là ls, đó là for- forlặp trên (IFS tách ra) Nói cách đưa ra sau khi các inkeyword`
glenn Jackman

1
@glennjackman cũng đúng, đó là sự kết hợp của forls. for f in *sẽ tốt, ví dụ.
terdon

3

Sử dụng tốt find, hoạt động với các tập tin ẩn, dòng mới và không gian.

find . -print0 | xargs -I {} -0 stat {}

hoặc bất kỳ khác thay vì stat

find . -print0 | xargs -I {} -0 file {}
find . -print0 | xargs -I {} -0 cat {}

1

Là một người R, tôi đã tìm thấy một cách giải quyết trong R:

filenames <- dir(); # reads file names into an array.
                    # It works also recursively
                    # with dir(recursive=TRUE)
for (i in 1:length(filenames)) {
system(     # calls a system function
 paste(     # join stat and the file name
  "stat",
  filenames[i]
 )
)
}

Tôi biết, nó thật điên rồ. Tôi muốn đầu ra của lsphân tích sẽ dễ dàng hơn ... R có thể xử lý khoảng trắng, bởi vì dir () trả về giá trị ký tự được trích dẫn. Bất cứ điều gì giữa các trích dẫn sau đó là một tên tệp hợp lệ với khoảng trắng.


3
Đừng bận tâm (nhưng +1 cho nỗ lực! :). Chỉ cần sử dụng for f in *, nhấn enter và tiếp tục trên một dòng mới : do, nhấn enter lần nữa stat "$f", nhập lại, donenhập. Đó là một lệnh được phân chia độc đáo trên 4 dòng và sẽ không phá vỡ bất kỳ loại tên tệp nào.
terdon

1

Tôi đã gặp các trường hợp khác về các vấn đề khoảng trắng trong các vòng lặp, vì vậy lệnh sau (imo mạnh hơn) là những gì tôi thường sử dụng. Nó cũng phù hợp độc đáo vào đường ống.

$ ls | while read line; do stat "$line"; done;

Bạn có thể kết hợp điều này với grephoặc sử dụng findthay thế:

$ find ./ -maxdepth 2 | grep '^\./[/a-z]+$' | while read line; do stat "$line"; done;

Câu trả lời đầu tiên của bạn không thành công với tên tệp có dấu gạch chéo ngược, hoặc khoảng trắng ở đầu hoặc cuối. Câu trả lời thứ hai của bạn thất bại hoàn toàn trừ khi bạn thêm -Etùy chọn vào grep, mà không có nó sẽ không nhận ra +trong một biểu thức thông thường. Ngay cả sau đó, đó là một cú swing và bỏ lỡ câu hỏi này, vì bạn grep loại bỏ tên tệp có chứa khoảng trắng . Nó cũng loại bỏ tên tệp chứa chữ số (chữ số) và dấu chấm câu (ví dụ: dấu ngoặc đơn), như các ví dụ trong câu hỏi làm. Và thậm chí không đề cập đến tên tệp có chứa dòng mới hoặc bắt đầu bằng -(dấu gạch ngang).
Scott

-1

Câu trả lời này sẽ giải quyết vấn đề phân tích cú pháp lsvà chăm sóc các khoảng trống và các dòng mới

Hãy thử điều này, nó sẽ giải quyết vấn đề của bạn bằng cách sử dụng IFS Bộ phân tách trường nội bộ.

IFS="\n" for f in $(ls); do   stat "$f"; done

Nhưng bạn cũng có thể giải quyết nó một cách hăng hái mà không cần phân tích đầu ra ls bằng cách sử dụng

for f in *; do   stat "$f"; done

1
không hoạt động cho các tập tin ẩn.
AB

2
OP đã không yêu cầu các tập tin ẩn
Maythux

1
Tôi không thấy lý do tại sao bạn cần sửa đổi IFSở đây: trích dẫn biến phải đủ để ngăn chặn việc chia tách từ, chắc chắn?
Steeldo

để phân tích cú pháp ls .
Maythux

Vì cái gì là downvote !!!
Maythux

-1

Thay vào đó, bạn có thể đổi tên các tệp của mình thay thế khoảng trắng bằng một số ký tự khác, chẳng hạn như gạch dưới, để bạn thoát khỏi vấn đề này:

Để làm điều đó dễ dàng chạy lệnh:

for file in * ; do mv "$f" "${f// /_}" ; done
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.