Làm thế nào để có được danh sách các tập tin trong một thư mục trong shell script?


157

Tôi đang cố gắng để có được nội dung của một thư mục bằng cách sử dụng shell script.

Kịch bản của tôi là:

for entry in `ls $search_dir`; do
    echo $entry
done

nơi $search_dirlà một con đường tương đối. Tuy nhiên, $search_dirchứa nhiều tệp có khoảng trắng trong tên của chúng. Trong trường hợp đó, tập lệnh này không chạy như mong đợi.

Tôi biết tôi có thể sử dụng for entry in *, nhưng nó chỉ hoạt động cho thư mục hiện tại của tôi.

Tôi biết tôi có thể thay đổi thư mục đó, sử dụng for entry in *rồi thay đổi lại, nhưng tình huống cụ thể của tôi ngăn cản tôi làm điều đó.

Tôi có hai đường dẫn tương đối $search_dir$work_dir, và tôi phải làm việc trên cả hai cùng một lúc, đọc chúng tạo / xóa các tập tin trong họ, vv

Vậy tôi phải làm gì bây giờ?

PS: Tôi sử dụng bash.

Câu trả lời:


270
for entry in "$search_dir"/*
do
  echo "$entry"
done

4
Bạn có thể giải thích tại sao for entry in "$search_dir/*"không làm việc? Tại sao chúng ta cần đặt /*bên ngoài dấu ngoặc kép?
mrgloom

6
@mrgloom: Bởi vì bạn cần phải để vỏ toàn cầu ký tự đại diện.
Ignacio Vazquez-Abrams

sẽ không findnhanh hơn?
Alexej Magura

4
Giải pháp cho đường dẫn đầy đủ. Nếu tôi chỉ muốn liệt kê những gì trong thư mục hiện tại thì sao?
ThatsRightJack

1
@mrgloom nếu bạn muốn làm như vậy bạn có thể đạt được điều đó vớifor entry in "${search_dir}/*"
funilrys

28

Các câu trả lời khác ở đây rất hay và trả lời câu hỏi của bạn, nhưng đây là kết quả hàng đầu của google cho "bash get list of files in", (tôi đang tìm cách lưu danh sách các tập tin) vì vậy tôi nghĩ rằng tôi sẽ đăng một Trả lời cho vấn đề đó:

ls $search_path > filename.txt

Nếu bạn chỉ muốn một loại nhất định (ví dụ: bất kỳ tệp .txt nào):

ls $search_path | grep *.txt > filename.txt

Lưu ý rằng $ search_path là tùy chọn; ls> filename.txt sẽ làm thư mục hiện tại.


Không cần sử dụng grep để chỉ nhận các tệp .txt: `ls $ search_path / *. Txt> filename.txt '. Nhưng quan trọng hơn, người ta không nên sử dụng đầu ra của lệnh ls để phân tích tên tệp.
Victor Zamanian

1
@VictorZamanian, bạn có thể giải thích lý do tại sao chúng ta không nên sử dụng đầu ra lsđể phân tích tên tệp không? Chưa từng nghe về điều này trước đây.
samurai_jane

1
@samurai_jane Có rất nhiều liên kết để cung cấp về chủ đề này, nhưng đây là một kết quả tìm kiếm đầu tiên: mywiki.wooledge.org/ParsingLs . Tôi thậm chí đã thấy một câu hỏi ở đây về SO tuyên bố lý do không phân tích cú pháp đầu ra của ls là BS và rất chi tiết về nó. Nhưng các câu trả lời / câu trả lời vẫn cho rằng đó là một ý tưởng tồi. Có một cái nhìn: unix.stackexchange.com/questions/128985/ Khăn
Victor Zamanian

26

Đây là một cách để làm điều đó trong đó cú pháp đơn giản hơn để tôi hiểu:

yourfilenames=`ls ./*.txt`
for eachfile in $yourfilenames
do
   echo $eachfile
done

./là thư mục làm việc hiện tại nhưng có thể được thay thế bằng bất kỳ đường dẫn nào
*.txttrả về any.txt
Bạn có thể kiểm tra những gì sẽ được liệt kê dễ dàng bằng cách nhập lslệnh thẳng vào thiết bị đầu cuối.

Về cơ bản, bạn tạo một biến yourfilenameschứa mọi thứ mà lệnh danh sách trả về dưới dạng một phần tử riêng biệt và sau đó bạn lặp qua nó. Vòng lặp tạo một biến tạm thời eachfilechứa một thành phần duy nhất của biến mà nó lặp qua, trong trường hợp này là tên tệp. Điều này không nhất thiết phải tốt hơn các câu trả lời khác, nhưng tôi thấy nó trực quan vì tôi đã quen với lslệnh và cú pháp vòng lặp for.


Điều này hoạt động tốt đối với một kịch bản nhanh, không chính thức hoặc một lớp lót, nhưng nó sẽ bị hỏng nếu tên tệp chứa dòng mới, không giống như các giải pháp dựa trên toàn cầu.
Soren Bjornstad

@SorenBjornstad cảm ơn vì lời khuyên! Tôi không biết các dòng mới được cho phép trong tên tệp - loại tệp nào có thể có chúng? Giống như, đây là một cái gì đó xảy ra phổ biến?
rrr

Các dòng mới trong tên tệp là xấu vì lý do này và theo tôi biết không có lý do chính đáng để sử dụng chúng. Tôi chưa bao giờ nhìn thấy một trong tự nhiên. Điều đó nói rằng, hoàn toàn có thể xây dựng tên tệp một cách độc hại với các dòng mới theo cách khai thác điều này. (Ví dụ, hãy tưởng tượng một thư mục chứa các file A, BC. Bạn tạo file gọi B\nCD, sau đó chọn để xóa chúng. Phần mềm không xử lý quyền này có thể sẽ xóa từ trước file B và C thay vì ngay cả khi bạn không có cho phép làm điều đó.)
Soren Bjornstad

19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done

11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"

1
Tôi biết điều này khá cũ nhưng dường như tôi không thể nhận được xargs -0 -i echo "{}"lệnh cuối cùng , quan tâm giải thích cho tôi một chút? Cụ thể là -i echo "{}"phần làm gì? Ngoài ra tôi đọc từ mantrang -ibị phản đối và chúng ta nên sử dụng -Iinsted.
drkg4b

1
-ithay thế {}bằng arg.
Noel Yap

Cảm ơn! Điều này rất hữu ích, đối với những người chậm hiểu như tôi, tôi nghĩ rằng đó {}là chuỗi được thay thế bằng các trận đấu bằng findlệnh.
drkg4b

3
tại sao bạn sử dụng xargs? theo mặc định, findin những gì nó tìm thấy ... bạn có thể xóa mọi thứ từ đó -print0.
gniourf_gniourf

1
Làm như vậy sẽ không xử lý tốt các mục nhập tệp có khoảng trắng.
Noel Yap

4
$ pwd; ls -l
/home/victoria/test
total 12
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  a
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  c
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'c d'
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:31  d
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_a
drwxr-xr-x 2 victoria victoria 4096 Apr 23 11:32  dir_b
-rw-r--r-- 1 victoria victoria    0 Apr 23 11:32 'e; f'

$ find . -type f
./c
./b
./a
./d
./c d
./e; f

$ find . -type f | sed 's/^\.\///g' | sort
a
b
c
c d
d
e; f

$ find . -type f | sed 's/^\.\///g' | sort > tmp

$ cat tmp
a
b
c
c d
d
e; f

Biến thể

$ pwd
/home/victoria

$ find $(pwd) -maxdepth 1 -type f -not -path '*/\.*' | sort
/home/victoria/new
/home/victoria/new1
/home/victoria/new2
/home/victoria/new3
/home/victoria/new3.md
/home/victoria/new.md
/home/victoria/package.json
/home/victoria/Untitled Document 1
/home/victoria/Untitled Document 2

$ find . -maxdepth 1 -type f -not -path '*/\.*' | sed 's/^\.\///g' | sort
new
new1
new2
new3
new3.md
new.md
package.json
Untitled Document 1
Untitled Document 2

Ghi chú:

  • . : thư mục hiện tại
  • loại bỏ -maxdepth 1để tìm kiếm đệ quy
  • -type f: tìm tệp, không phải thư mục ( d)
  • -not -path '*/\.*' : không trở về .hidden_files
  • sed 's/^\.\///g': xóa dự phòng ./khỏi danh sách kết quả

0

Đây là một cách khác để liệt kê các tệp trong một thư mục (sử dụng một công cụ khác, không hiệu quả như một số câu trả lời khác).

cd "search_dir"
for [ z in `echo *` ]; do
    echo "$z"
done

echo *Xuất ra tất cả các tập tin của thư mục hiện tại. Các forvòng lặp qua mỗi tên tập tin và in để stdout.

Ngoài ra, nếu tìm kiếm các thư mục bên trong thư mục thì hãy đặt nó trong forvòng lặp:

if [ test -d $z ]; then
    echo "$z is a directory"
fi

test -d kiểm tra nếu tập tin là một thư mục.


0

Câu trả lời được chấp nhận sẽ không trả về tiền tố của tệp với a. Để làm điều đó sử dụng

for entry in "$search_dir"/* "$search_dir"/.[!.]* "$search_dir"/..?*
do
  echo "$entry"
done

-1

Trên phiên bản Linux, tôi làm việc với (x86_64 GNU / Linux) sau đây hoạt động:

for entry in "$search_dir"/*
do
  echo "$entry"
done

-1: Điều này giống hệt với câu trả lời được chấp nhận ngoại trừ việc nó không trích dẫn các biến. Không trích dẫn $search_dircó nghĩa là nó bị hỏng nếu có một khoảng trắng trong tên thư mục. (Trong trường hợp này bạn có thể thoát khỏi việc không trích dẫn $entry, nhưng sẽ tốt hơn nếu trích dẫn nó.)
Soren Bjornstad

Bạn sai rồi. Giải pháp của tôi không giống nhau, ngay cả khi nó tương tự như câu trả lời được chấp nhận. Và nó KHÔNG sai, nó hoạt động. Đây là những gì tôi đã thử trên các hệ thống unix khác nhau, bao gồm cả Mac OS và nó hoạt động trên mọi hệ thống. Các tập tin với khoảng trống, ngay cả khi nó có khoảng trống hàng đầu trong tên, được hiển thị đúng. tìm kiếm_dir =. cho mục nhập trong $ search_dir / *; làm echo $ entry; đã hoàn thành
Andrushenko Alexander

Nếu bạn đọc bình luận của tôi gần hơn một chút, bạn sẽ thấy vấn đề với các khoảng trống trong tên thư mục , không phải tên tệp . Điều đó khác với vấn đề người hỏi gặp phải, nhưng điều đó vẫn có nghĩa là mã có thể bị phá vỡ ngoài ý muốn! Đây là một ví dụ về những gì không hoạt động : mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Vòng lặp chạy hai lần, một lần in xvà lần thứ hai y/*, có lẽ không phải là kết quả mong đợi.
Soren Bjornstad

Và tôi coi các biến không được trích dẫn không chính xác là sai một cách nguy hiểm, đó là cơ sở cho một downvote. Xem xét mã này để xóa mọi thư mục con của thư mục tìm kiếm trong khi để lại các tệp : search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Nếu cũng có một xthư mục trong thư mục hiện tại, nó sẽ bị xóa hoàn toàn, chỉ vì bạn không trích dẫn biến của mình!
Soren Bjornstad

Chà, bạn luôn lấy một mã được tạo cho một nhiệm vụ, thay đổi nhiệm vụ một chút và mã trở nên sai. Đối với câu hỏi yêu cầu giải pháp đề xuất của tôi hoạt động. Nhưng ... là một nhà phát triển tôi phải đồng ý với các lập luận của bạn: wenn chúng ta có thể làm cho một mã mạnh mẽ hơn và rộng rãi hơn, chúng ta nên làm điều đó. Vì vậy, tôi sẽ thêm các qoutes. Điều này sẽ làm cho câu trả lời của tôi giống hệt với câu trả lời được đưa ra ở trên, nhưng vì lợi ích của sự lộn xộn (có thể hữu ích cho ai đó) tôi cũng để lại câu trả lời.
Andrushenko Alexander

-1

Chỉ cần nhập lệnh đơn giản này:

ls -d */

Bạn có thể cung cấp một số lời giải thích @Michael Sisko
Vipul

ls (danh sách) -d (chỉ liệt kê các thư mục) * / (của bất kỳ mẫu nào, dấu gạch chéo ngược giới hạn danh sách chỉ các thư mục).
Michael Sisko
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.