Sự khác biệt giữa những người du -sh * Hồi và người du -sh.


26

Sự khác biệt giữa du -sh *và là du -sh ./*gì?

Lưu ý: Điều tôi quan tâm là *./*các bộ phận.


2
đầu ra ? một người sẽ chỉ cho bạn ./ trước tên tệp
Kiwy

Câu trả lời:


69
$ chạm ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 a
12 b
0 foo
Tổng cộng 0

Như bạn có thể thấy, -ctệp được lấy làm tùy chọn duvà không được báo cáo (và bạn thấy totaldòng này vì du -c). Ngoài ra, tệp được gọi a\n12\tblà làm cho chúng ta nghĩ rằng có các tệp được gọi ab.

$ du -hs -- *
0       a
12      b
0       -c
0       foo

Cái đó tốt hơn. Ít nhất thời gian -cnày không được coi là một lựa chọn.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Điều đó thậm chí còn tốt hơn. Các ./ngăn chặn tiền tố -ckhỏi mắc bẫy như một tùy chọn và sự vắng mặt của ./trước btrong đầu ra cho thấy rằng không có btập tin trong đó, nhưng có một tập tin với một ký tự xuống dòng (nhưng xem bên dưới 1 cho digressions thêm vào đó).

Cách tốt nhất là sử dụng ./tiền tố khi có thể và nếu không và đối với dữ liệu tùy ý, bạn nên luôn luôn sử dụng:

cmd -- "$var"

hoặc là:

cmd -- $patterns

Nếu cmdkhông hỗ trợ --đánh dấu sự kết thúc của các tùy chọn, bạn nên báo cáo đó là một lỗi cho tác giả của nó (trừ khi đó là do lựa chọn và được ghi lại như tài liệu echo).

Có những trường hợp ./*giải quyết vấn đề --không. Ví dụ:

awk -f file.awk -- *

thất bại nếu có một tệp được gọi a=b.txttrong thư mục hiện tại (đặt biến awk athành b.txtthay vì bảo nó xử lý tệp).

awk -f file.awk ./*

Không có vấn đề vì ./akhông phải là tên biến awk hợp lệ, do đó ./a=b.txtkhông được coi là một phép gán biến.

cat -- * | wc -l

thất bại nếu có một tệp được gọi -trong thư mục hiện tại, vì nó bảo catđọc từ stdin của nó ( -đặc biệt đối với hầu hết các tiện ích xử lý văn bản và cd/ pushd).

cat ./* | wc -l

Không sao vì ./-không đặc biệt cat.

Những thứ như:

grep -l -- foo *.txt | wc -l

để đếm số lượng tệp chứa foosai vì nó giả sử tên tệp không chứa các ký tự dòng mới ( wc -lđếm các ký tự dòng mới, các ký tự đầu ra grepcho mỗi tệp và các tên trong chính tên tệp). Bạn nên sử dụng thay thế:

grep -l foo ./*.txt | grep -c /

(đếm số lượng /ký tự đáng tin cậy hơn vì chỉ có thể có một ký tự cho mỗi tên tệp).

Đối với đệ quy grep, thủ thuật tương đương là sử dụng:

grep -rl foo .//. | grep -c //

./* có thể có một số tác dụng phụ không mong muốn mặc dù.

cat ./*

thêm hai ký tự cho mỗi tệp, do đó sẽ khiến bạn đạt đến giới hạn kích thước tối đa của đối số + môi trường sớm hơn. Và đôi khi bạn không muốn điều đó ./được báo cáo trong đầu ra. Như:

grep foo ./*

Sẽ xuất:

./a.txt: foobar

thay vì:

a.txt: foobar

Lạc đề

1 . Tôi cảm thấy như tôi phải mở rộng về điều đó ở đây, sau khi thảo luận trong các bình luận.

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Ở trên, việc ./đánh dấu phần đầu của mỗi tệp có nghĩa là chúng ta có thể xác định rõ ràng nơi mỗi tên tệp bắt đầu (tại ./) và nơi kết thúc (tại dòng mới trước phần tiếp theo ./hoặc phần cuối của đầu ra).

Điều đó có nghĩa là đầu ra của du ./*, trái ngược với du -- *) có thể được phân tích cú pháp một cách đáng tin cậy, mặc dù không dễ dàng như vậy trong một tập lệnh.

Khi đầu ra đi đến một thiết bị đầu cuối, có rất nhiều cách khác mà một tên tệp có thể đánh lừa bạn:

  • Kiểm soát các ký tự, chuỗi thoát có thể ảnh hưởng đến cách mọi thứ được hiển thị. Chẳng hạn, \rdi chuyển con trỏ đến đầu dòng, \bdi chuyển con trỏ trở lại, \e[Cchuyển tiếp (trong hầu hết các thiết bị đầu cuối) ...
  • nhiều nhân vật vô hình trên một thiết bị đầu cuối bắt đầu bằng một nhân vật rõ ràng nhất: nhân vật không gian.
  • Có các ký tự Unicode trông giống như dấu gạch chéo trong hầu hết các phông chữ

    $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
    /    

    (xem cách nó đi trong trình duyệt của bạn).

Một ví dụ:

$ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0       ./x
0       ./x
0       ./x
0       .∕x
0       ./x
0       ./x

Rất nhiều xnhưng ythiếu.

Một số công cụ như GNUls sẽ thay thế các ký tự không in được bằng dấu chấm hỏi (lưu ý rằng (U + 2215) có thể in được) khi đầu ra đi đến một thiết bị đầu cuối. GNU duthì không.

Có nhiều cách để khiến họ tự tiết lộ:

$ ls
x  x   x?0?.∕x  y  y?0?.?[Cx  y?x
$ LC_ALL=C ls
x  x?0?.???x  x   y  y?x  y?0?.?[Cx

Xem cách chuyển sang ???sau khi chúng tôi nói lsrằng bộ ký tự của chúng tôi là ASCII.

$ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$

$đánh dấu kết thúc của dòng, vì vậy chúng ta có thể phát hiện ra "x"vs "x ", tất cả các ký tự không in được và các ký tự không phải ASCII được biểu thị bằng một chuỗi dấu gạch chéo ngược (chính dấu gạch chéo ngược sẽ được biểu thị bằng hai dấu gạch chéo ngược) có nghĩa là nó không rõ ràng. Đó là GNU sed, nó phải giống nhau trong tất cả các sedtriển khai tuân thủ POSIX nhưng lưu ý rằng một số sedtriển khai cũ gần như không hữu ích.

$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$

(không chuẩn nhưng khá phổ biến, cũng cat -Acó một số triển khai). Đó là một hữu ích và sử dụng một đại diện khác nhau nhưng không rõ ràng ( "^I"<TAB>được hiển thị giống nhau chẳng hạn).

$ du -hs ./* | od -vtc
0000000   0  \t   .   /   x  \n   0  \t   .   /   x      \n   0  \t   .
0000020   /   x  \n   0  \t   . 342 210 225   x  \n   0  \t   .   /   y
0000040  \r   0  \t   . 033   [   C   x  \n   0  \t   .   /   y  \b   x
0000060  \n
0000061

Đó là một tiêu chuẩn và rõ ràng (và nhất quán từ thực hiện đến thực hiện) nhưng không dễ đọc.

Bạn sẽ nhận thấy rằng ykhông bao giờ xuất hiện ở trên. Đó là một hoàn toàn không liên quan vấn đề với du -hs *điều đó không có gì để làm với tên file nhưng cần lưu ý: vì dusử dụng các báo cáo đĩa, nó không báo cáo các liên kết khác vào một tập tin đã được liệt kê (không phải tất cả duhiện thực cư xử như thế mặc dù khi các liên kết cứng được liệt kê trên dòng lệnh).


+1, Đẹp và kỹ lưỡng (theo như tôi có thể nói ^^). Tôi đặc biệt yêu thích lợi thế "grep -c /". Cũng đáng chú ý: lợi thế của "./*" so với "*" xuất hiện trong một trong (nhiều) câu trả lời hay của Câu hỏi thường gặp về Unix (có lẽ trên faqs.org. Iirc, đó là câu hỏi về các tệp rm-ing bắt đầu bằng một "-").
Olivier Dulac

... và nó không xấu thực hành để có các file với newlines và tab trong tên của họ? Tôi biết tôi cố gắng giới hạn tên [a-z0-9.+-].
Blacklight Shining

5
@BlacklightShining, thật tệ khi ăn cắp xe hơi, nhưng thật tệ khi để xe của bạn được mở khóa (bỏ qua dòng mới), đặc biệt khi đó là một chiếc xe đắt tiền (tập lệnh chạy như một người dùng đặc quyền, trên máy chủ có dữ liệu nhạy cảm ...) hoặc khi bạn đỗ xe ở khu vực gồ ghề ( /tmp) hoặc khu vực có nhiều xe hơi đắt tiền ( $HOME) và thậm chí còn tệ hơn khi đến trang web Hỏi & Đáp và nói rằng luôn luôn không khóa xe của bạn mà không chỉ định điều kiện nào (trong nhà để xe bị khóa, kịch bản bạn đã viết chỉ chạy một mình trên một máy không được kết nối với bất kỳ mạng hoặc bộ lưu trữ di động nào ...)
Stéphane Chazelas

1
@BlacklightShining, dòng mới là không bình thường nhưng không gian nhúng rất phổ biến hiện nay, đặc biệt là đối với các tệp được tạo qua GUI.
alexis

2
@BlacklightShining, vâng, mặc dù một (như "b "hoặc "a\bb") sẽ đánh lừa người dùng trên thiết bị đầu cuối nhưng không phải là một đoạn mã phân tích cú pháp đầu ra của du ./*. Tôi có lẽ nên thêm một lưu ý về điều đó. Sẽ làm vào ngày mai. Lưu ý rằng trước đó tôi có nghĩa là đặc quyền theo nghĩa chung, không root(mặc dù áp dụng tất cả nhiều hơn cho roottất nhiên). dòng mới được cho phép, bỏ qua chúng là một lỗi. bọ có thói quen bị lợi dụng. Bạn phải đo lường rủi ro theo từng trường hợp. Thực hành mã hóa tốt có thể tránh được các vấn đề trong nhiều trường hợp. Chắc chắn trên SE, chúng ta nên nâng cao nhận thức.
Stéphane Chazelas

6

Không có sự khác biệt giữa a *./*về những gì tập tin sẽ liệt kê. Sự khác biệt duy nhất sẽ là với hình thức thứ 2, mỗi tệp sẽ có một dấu gạch chéo có ./tiền tố ở phía trước chúng, thường có nghĩa là thư mục hiện tại.

Hãy nhớ rằng .thư mục là một ký hiệu viết tắt cho thư mục hiện tại.

$ ls -la | head -4
total 28864
drwx------. 104 saml saml    12288 Jan 23 20:04 .
drwxr-xr-x.   4 root root     4096 Jul  8  2013 ..
-rw-rw-r--.   1 saml saml      972 Oct  6 20:26 abcdefg

Bạn có thể thuyết phục bản thân rằng 2 danh sách này về cơ bản là giống nhau bằng cách sử dụng echođể xem phần vỏ sẽ mở rộng chúng thành gì.

$ echo *
$ echo ./*

2 lệnh này sẽ liệt kê tất cả các tệp trong thư mục hiện tại của bạn.

Ví dụ

Chúng tôi có thể tạo một số dữ liệu giả như vậy:

$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5

Bây giờ khi chúng ta sử dụng các echolệnh trên, chúng ta thấy đầu ra sau:

$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5

Sự khác biệt này có vẻ không cần thiết nhưng có những tình huống bạn muốn đảm bảo với các công cụ dòng lệnh Unix khác nhau mà bạn đang truyền tên tệp cho chúng thông qua dòng lệnh, và không có gì nữa!

Vậy tại sao lại sử dụng ./*?

Như câu trả lời của @ Stephane chỉ ra , do bản chất của các ký tự là hợp pháp khi đặt tên tệp và thư mục trong Unix, tên tệp nguy hiểm có thể được tạo ra có tác dụng phụ không mong muốn khi chúng được chuyển đến các lệnh Unix khác nhau trong dòng lệnh.

Vì vậy, thường việc sử dụng ./sẽ được sử dụng để giúp đảm bảo rằng tên tệp mở rộng được coi là tên tệp khi được chuyển làm đối số cho các lệnh Unix khác nhau.

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.