Bagaimana cara mendapatkan daftar file dalam direktori dalam skrip shell?

159

Saya mencoba untuk mendapatkan isi direktori menggunakan skrip shell.

Skrip saya adalah:

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

di mana $search_dirjalur relatif. Namun, $search_dirberisi banyak file dengan spasi putih dalam namanya. Dalam hal ini, skrip ini tidak berjalan seperti yang diharapkan.

Saya tahu saya bisa menggunakan for entry in *, tetapi itu hanya akan berfungsi untuk direktori saya saat ini.

Saya tahu saya dapat mengubah ke direktori itu, gunakan for entry in *kemudian ubah kembali, tetapi situasi khusus saya mencegah saya melakukan itu.

Saya memiliki dua jalur relatif $search_dirdan $work_dir, dan saya harus mengerjakan keduanya secara bersamaan, membacanya membuat / menghapus file di dalamnya dll.

Jadi apa yang harus saya lakukan sekarang?

PS: Saya menggunakan bash.

jrharshath
sumber

Jawaban:

274
for entry in "$search_dir"/*
do
  echo "$entry"
done
Ignacio Vazquez-Abrams
sumber
4
Bisakah Anda jelaskan mengapa for entry in "$search_dir/*"tidak berhasil? Mengapa kita perlu menempatkan di /*luar tanda kutip?
mrgloom
6
@ mrgloom: Karena Anda harus membiarkan shell glob wildcard.
Ignacio Vazquez-Abrams
tidak findakan lebih cepat?
Alexej Magura
4
Solusinya memberikan jalan penuh. Bagaimana jika saya hanya ingin daftar apa yang ada di direktori saat ini?
ThatsRightJack
1
@mrgloom jika Anda ingin melakukannya Anda dapat mencapainya denganfor entry in "${search_dir}/*"
funilrys
28

Jawaban lain di sini luar biasa dan menjawab pertanyaan Anda, tetapi ini adalah hasil google teratas untuk "bash dapatkan daftar file dalam direktori", (yang saya cari untuk menyimpan daftar file) jadi saya pikir saya akan memposting jawaban untuk masalah itu:

ls $search_path > filename.txt

Jika Anda hanya menginginkan jenis tertentu (mis. File .txt):

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

Perhatikan bahwa $ search_path adalah opsional; ls> filename.txt akan melakukan direktori saat ini.

mulai
sumber
Tidak perlu menggunakan grep untuk mendapatkan hanya file .txt: `ls $ search_path / *. Txt> filename.txt '. Tetapi yang lebih penting, seseorang tidak harus menggunakan output dari perintah ls untuk mengurai nama file.
Victor Zamanian
1
@ Viktoramanian, dapatkah Anda menjelaskan mengapa kami tidak harus menggunakan output lsuntuk mengurai nama file? Belum pernah mendengar ini sebelumnya.
samurai_jane
1
@samurai_jane Ada banyak tautan untuk diberikan terkait topik ini, tapi inilah satu hasil pencarian pertama: mywiki.wooledge.org/ParsingLs . Saya bahkan melihat pertanyaan di sini di SO mengklaim alasan untuk tidak menguraikan output ls adalah BS dan sangat rumit tentang hal itu. Tetapi jawaban / jawaban masih mengklaim itu adalah ide yang buruk. Silahkan lihat: unix.stackexchange.com/questions/128985/…
Victor Zamanian
26

Ini adalah cara untuk melakukannya di mana sintaksinya lebih mudah bagi saya untuk mengerti:

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

./adalah direktori aktif saat ini tetapi bisa diganti dengan jalur
*.txtapa saja mengembalikan apa pun.txt
Anda dapat memeriksa apa yang akan terdaftar dengan mudah dengan mengetikkan lsperintah langsung ke terminal.

Pada dasarnya, Anda membuat variabel yang yourfilenamesberisi semua yang dikembalikan oleh perintah daftar sebagai elemen yang terpisah, dan kemudian Anda mengulanginya. Loop membuat variabel sementara eachfileyang berisi elemen tunggal dari variabel yang dilaluinya, dalam hal ini nama file. Ini belum tentu lebih baik daripada jawaban yang lain, tetapi saya merasa intuitif karena saya sudah terbiasa dengan lsperintah dan sintaks for loop.

rrr
sumber
Ini berfungsi baik untuk skrip informal cepat atau satu baris, tetapi akan rusak jika nama file berisi baris baru, tidak seperti solusi berbasis global.
Soren Bjornstad
@SorenBjornstad terima kasih atas sarannya! Saya tidak tahu baris baru diizinkan dalam nama file - jenis file apa yang mungkin memilikinya? Seperti, apakah ini sesuatu yang biasa terjadi?
rrr
Baris baru dalam nama file jahat karena alasan ini dan sejauh yang saya tahu tidak ada alasan yang sah untuk menggunakannya. Saya sendiri belum pernah melihatnya di alam liar. Karena itu, sangat mungkin untuk membuat nama file dengan baris baru dengan jahat untuk mengeksploitasi ini. (Misalnya, bayangkan direktori yang berisi file A,, Bdan C. Anda membuat file yang dipanggil B\nCdan D, lalu memilih untuk menghapusnya. Perangkat lunak yang tidak menangani hak ini bisa berakhir dengan menghapus file yang sudah ada sebelumnya, B dan C bahkan jika Anda tidak memiliki izin untuk melakukan itu.)
Soren Bjornstad
19
for entry in "$search_dir"/* "$work_dir"/*
do
  if [ -f "$entry" ];then
    echo "$entry"
  fi
done
ghostdog74
sumber
11
find "${search_dir}" "${work_dir}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 -I {} echo "{}"
Noel Yap
sumber
1
Saya tahu ini cukup tua tetapi saya tidak bisa mendapatkan xargs -0 -i echo "{}"perintah terakhir , mau jelaskan sedikit? Khususnya apa yang -i echo "{}"dilakukan bagian itu? Saya juga membaca dari manhalaman yang -isudah usang sekarang dan kita harus menggunakan -Iinsted.
drkg4b
1
-ipengganti {}dengan arg.
Noel Yap
Terima kasih! Ini berguna, juga untuk yang berpikiran lambat seperti saya, saya pikir itu {}adalah string yang diganti dengan yang sesuai dengan findperintah.
drkg4b
3
mengapa Anda menggunakan xargs? secara default, findcetak apa yang ditemukannya ... Anda dapat menghapus semuanya -print0.
gniourf_gniourf
1
Melakukan itu tidak akan menangani entri file dengan spasi dengan baik.
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

Variasi

$ 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

Catatan:

  • . : folder saat ini
  • hapus -maxdepth 1untuk mencari secara rekursif
  • -type f: cari file, bukan direktori ( d)
  • -not -path '*/\.*' : jangan kembali .hidden_files
  • sed 's/^\.\///g': hapus yang diawali ./dari daftar hasil
Victoria Stuart
sumber
0

Berikut cara lain untuk mendaftarkan file di dalam direktori (menggunakan alat yang berbeda, tidak seefisien beberapa jawaban lainnya).

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

echo *Keluarkan semua file dari direktori saat ini. The forLoop iterates atas setiap nama file dan mencetak ke stdout.

Selain itu, Jika mencari direktori di dalam direktori maka letakkan ini di dalam forloop:

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

test -d memeriksa apakah file tersebut adalah direktori.

SnoopDogg
sumber
0

Jawaban yang diterima tidak akan mengembalikan awalan file dengan a. Untuk melakukan itu gunakan

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

Pada versi Linux saya bekerja dengan (x86_64 GNU / Linux) bekerja sebagai berikut:

for entry in "$search_dir"/*
do
  echo "$entry"
done
Andrushenko Alexander
sumber
-1: Ini identik dengan jawaban yang diterima kecuali itu tidak mengutip variabel. Tidak mengutip $search_dirberarti rusak jika ada ruang dalam nama direktori. (Dalam hal ini Anda bisa lolos dengan tidak mengutip $entry, tetapi akan lebih baik untuk mengutipnya.)
Soren Bjornstad
Anda salah. Solusi saya tidak sama, bahkan ketika itu mirip dengan jawaban yang diterima. Dan itu BUKAN salah, itu berhasil. Inilah yang saya coba pada sistem unix yang berbeda, termasuk Mac OS, dan itu berhasil pada masing-masing sistem. File dengan kosong, bahkan ketika itu memiliki kosong dalam nama, ditampilkan dengan benar. search_dir =. untuk entri di $ search_dir / *; lakukan echo $ entry; selesai ./ aa aaa.txt ./add ./apm_tables.txt Anda dapat memilih solusi hanya ketika Anda dapat membuktikan bahwa solusi TIDAK BEKERJA atau menghasilkan hasil yang salah.
Andrushenko Alexander
Jika Anda membaca komentar saya sedikit lebih dekat, Anda akan melihat masalah dengan kosong pada nama direktori , bukan nama file . Itu berbeda dari masalah yang ditemui si penanya, tetapi itu tetap berarti kode tersebut dapat rusak secara tidak sengaja! Berikut ini adalah contoh dari apa yang tidak bekerja: mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; done. Pengulangan berjalan dua kali, sekali pencetakan xdan kedua kalinya y/*, yang mungkin bukan hasil yang diharapkan.
Soren Bjornstad
Dan saya menganggap variabel yang tidak dikutip dengan benar salah, yang merupakan alasan untuk downvote. Pertimbangkan kode ini untuk menghapus subdirektori dari direktori pencarian sementara meninggalkan file: search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; done. Jika ada juga xdirektori di direktori saat ini, itu dihapus, hanya karena Anda tidak mengutip variabel Anda!
Soren Bjornstad
Nah, Anda selalu mengambil kode yang dibuat untuk satu tugas, sedikit mengubah tugas dan kode menjadi salah. Untuk pertanyaan yang diajukan solusi yang saya usulkan bekerja. Tapi ... sebagai pengembang saya harus setuju dengan argumen Anda: jika kita bisa membuat kode lebih kuat dan umum, kita harus melakukannya. Jadi saya akan menambahkan qoutes. Ini akan membuat jawaban saya identik dengan jawaban yang diberikan di atas, tetapi demi diskusi (yang mungkin berguna bagi seseorang) saya meninggalkan jawabannya juga.
Andrushenko Alexander
-1

Cukup masukkan perintah sederhana ini:

ls -d */
Michael Sisko
sumber
Bisakah Anda memberikan penjelasan @ Michael Sisko
Vipul
ls (daftar) -d (daftar hanya direktori) * / (dari pola apa pun, backslash membatasi daftar hanya untuk direktori).
Michael Sisko