วิธีรับรายการไฟล์ในไดเรกทอรีในเชลล์สคริปต์


159

ฉันพยายามรับเนื้อหาของไดเรกทอรีโดยใช้เชลล์สคริปต์

สคริปต์ของฉันคือ:

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

โดยที่$search_dirเป็นเส้นทางสัมพัทธ์ อย่างไรก็ตาม$search_dirมีไฟล์จำนวนมากที่มีช่องว่างในชื่อ ในกรณีนี้สคริปต์นี้ไม่ทำงานตามที่คาดไว้

ฉันรู้ว่าฉันสามารถใช้งานfor entry in *ได้ แต่มันจะใช้ได้เฉพาะกับไดเรกทอรีปัจจุบันของฉันเท่านั้น

ฉันรู้ว่าฉันสามารถเปลี่ยนไปยังไดเรกทอรีfor entry in *นั้นใช้แล้วเปลี่ยนกลับ แต่สถานการณ์เฉพาะของฉันทำให้ฉันไม่สามารถทำได้

ฉันมีสองเส้นทางญาติ$search_dirและ$work_dirและฉันต้องทำงานทั้งสองพร้อมกันอ่านพวกเขาสร้าง / ลบไฟล์ในพวกเขา ฯลฯ

แล้วฉันจะทำอย่างไรดี

PS: ฉันใช้ทุบตี

คำตอบ:


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

4
คุณช่วยอธิบายfor entry in "$search_dir/*"ได้ไหมว่าทำไมไม่ทำงาน ทำไมเราต้องวาง/*นอกเหนือราคา
mrgloom

6
@mrgloom: เพราะคุณต้องปล่อยให้เชลล์ทำให้สัญลักษณ์ตัวแทนดูกลมกลืน
Ignacio Vazquez-Abrams

จะไม่findเร็วขึ้นหรือ
Alexej Magura

4
การแก้ปัญหาให้เส้นทางเต็ม ถ้าฉันต้องการแสดงรายการสิ่งที่อยู่ในไดเรกทอรีปัจจุบัน
ThatsRightJack

1
@mrgloom หากคุณต้องการทำเช่นนั้นคุณสามารถบรรลุด้วยfor entry in "${search_dir}/*"
funilrys

28

คำตอบอื่น ๆ ที่นี่ยอดเยี่ยมและตอบคำถามของคุณ แต่นี่คือผลการค้นหาอันดับต้น ๆ ของ google สำหรับ "bash get list ของไฟล์ในไดเรกทอรี" (ซึ่งฉันกำลังมองหาที่จะบันทึกรายการไฟล์) ดังนั้นฉันคิดว่าฉันจะโพสต์ ตอบปัญหานั้น:

ls $search_path > filename.txt

หากคุณต้องการเพียงบางประเภท (เช่นไฟล์. txt ใด ๆ ):

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

โปรดทราบว่า $ search_path เป็นตัวเลือก ls> filename.txt จะทำไดเรกทอรีปัจจุบัน


ไม่จำเป็นต้องใช้ grep เพื่อรับเฉพาะไฟล์. txt: `ls $ search_path / *. txt> filename.txt ' แต่ที่สำคัญกว่านั้นไม่ควรใช้เอาต์พุตของคำสั่ง ls เพื่อแยกวิเคราะห์ชื่อไฟล์
Victor Zamanian

1
@VictorZamanian คุณช่วยอธิบายได้ไหมว่าทำไมเราไม่ควรใช้lsชื่อไฟล์ในการแยกวิเคราะห์ชื่อไฟล์? ไม่เคยได้ยินเรื่องนี้มาก่อน
samurai_jane

1
@samurai_jane มีจำนวนมากของการเชื่อมโยงเพื่อให้การเกี่ยวกับเรื่องนี้ แต่นี่เป็นผลการค้นหาที่คนแรก: mywiki.wooledge.org/ParsingLs ฉันยังเห็นคำถามที่นี่เพื่ออ้างเหตุผลที่ไม่แยกวิเคราะห์ผลลัพธ์ของ ls คือ BS และอธิบายอย่างละเอียดเกี่ยวกับเรื่องนี้ แต่คำตอบ / คำตอบก็ยังอ้างว่าเป็นความคิดที่ไม่ดี ดูได้ที่: unix.stackexchange.com/questions/128985/ …
Victor Zamanian

26

นี่เป็นวิธีที่จะทำให้ไวยากรณ์เข้าใจง่ายขึ้นสำหรับฉัน:

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

./เป็นไดเรกทอรีการทำงานปัจจุบัน แต่สามารถถูกแทนที่ด้วยพา ธ ใด ๆ
*.txtส่งคืน Anything.txt
คุณสามารถตรวจสอบสิ่งที่จะแสดงรายการได้อย่างง่ายดายโดยพิมพ์lsคำสั่งลงในเทอร์มินัล

โดยพื้นฐานแล้วคุณสร้างตัวแปรyourfilenamesที่มีทุกสิ่งที่คำสั่ง list ส่งกลับเป็นองค์ประกอบแยกจากกันและจากนั้นคุณวนซ้ำมัน การวนซ้ำสร้างตัวแปรชั่วคราวeachfileที่มีองค์ประกอบเดียวของตัวแปรที่วนลูปผ่านในกรณีนี้ชื่อไฟล์ นี่ไม่จำเป็นต้องดีกว่าคำตอบอื่น ๆ แต่ฉันคิดว่ามันใช้งานง่ายเพราะฉันคุ้นเคยกับlsคำสั่งและไวยากรณ์ลูปแล้ว


วิธีนี้ใช้ได้สำหรับสคริปต์ที่รวดเร็วและไม่เป็นทางการหรือซับเส้นเดียว แต่จะแตกถ้าชื่อไฟล์มีการขึ้นบรรทัดใหม่ซึ่งแตกต่างจากโซลูชั่นแบบกลม
Soren Bjornstad

@SorenBjornstad ขอบคุณสำหรับคำแนะนำ! ฉันไม่ทราบว่าอนุญาตให้ขึ้นบรรทัดใหม่ในชื่อไฟล์ - ไฟล์ประเภทใดที่อาจมีพวกเขา ชอบนี่เป็นสิ่งที่เกิดขึ้นทั่วไปใช่ไหม
rrr

การขึ้นบรรทัดใหม่ในชื่อไฟล์นั้นชั่วร้ายด้วยเหตุนี้และเท่าที่ฉันรู้ไม่มีเหตุผลอันสมควรที่จะใช้พวกเขา ฉันไม่เคยเห็นใครอยู่ในป่า ที่กล่าวมาเป็นไปได้โดยสิ้นเชิงที่จะสร้างชื่อไฟล์โดยมีจุดขึ้นบรรทัดใหม่ในลักษณะที่เป็นการเจาะช่องโหว่นี้ (ตัวอย่างเช่นจินตนาการไดเรกทอรีที่มีไฟล์A, BและC. คุณสร้างไฟล์ที่เรียกว่าB\nCและDจากนั้นเลือกที่จะลบพวกเขา. ซอฟแวร์ที่ไม่ได้จัดการกับสิทธินี้จะจบลงด้วยการลบไฟล์นิยาย B และ C แทนแม้ว่าคุณจะไม่ได้มี การอนุญาตให้ทำเช่นนั้น)
Soren Bjornstad


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

1
ฉันรู้ว่ามันค่อนข้างเก่า แต่ฉันไม่สามารถxargs -0 -i echo "{}"รับคำสั่งสุดท้ายได้โปรดอธิบายฉันหน่อยได้ไหม? โดยเฉพาะอย่างยิ่ง-i echo "{}"ส่วนที่ทำคืออะไร? ฉันอ่านจากmanหน้าเว็บที่-iเลิกใช้แล้วและเราควรใช้-Iinsted
drkg4b

1
-iทดแทน{}ด้วย arg
Noel Yap

ขอบคุณ! สิ่งนี้มีประโยชน์เช่นกันสำหรับคนที่ใจช้าเช่นฉันฉันคิดว่า{}สตริงนั้นจะถูกแทนที่ด้วยfindคำสั่งที่ตรงกัน
drkg4b

3
ทำไมคุณใช้xargs? โดยค่าเริ่มต้นfindพิมพ์สิ่งที่มันพบว่า ... -print0คุณสามารถลบทุกอย่างจาก
gniourf_gniourf

1
การทำเช่นนั้นจะไม่จัดการรายการไฟล์ด้วยช่องว่างได้ดี
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

รูปแบบ

$ 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

หมายเหตุ:

  • . : โฟลเดอร์ปัจจุบัน
  • ลบ-maxdepth 1เพื่อค้นหาซ้ำ
  • -type f: ค้นหาไฟล์ไม่ใช่ไดเรกทอรี ( d)
  • -not -path '*/\.*' : อย่ากลับมา .hidden_files
  • sed 's/^\.\///g': ลบส่วนที่เพิ่มเติม./จากรายการผลลัพธ์

0

ต่อไปนี้เป็นอีกวิธีในการแสดงรายการไฟล์ภายในไดเรกทอรี (ใช้เครื่องมือที่แตกต่างกันไม่มีประสิทธิภาพเท่ากับคำตอบอื่น ๆ )

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

echo *ส่งออกไฟล์ทั้งหมดของไดเรกทอรีปัจจุบัน foriterates ห่วงแต่ละชื่อไฟล์และพิมพ์ที่ stdout

นอกจากนี้หากค้นหาไดเรกทอรีภายในไดเรกทอรีให้วางสิ่งนี้ไว้ในforลูป:

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

test -d ตรวจสอบว่าไฟล์เป็นไดเรกทอรี


0

คำตอบที่ยอมรับจะไม่ส่งคืนคำนำหน้าไฟล์ด้วย เมื่อต้องการทำเช่นนั้นใช้

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

-1

ในรุ่น Linux ฉันทำงานด้วย (x86_64 GNU / Linux) ดังต่อไปนี้:

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

-1: นี่เป็นเหมือนคำตอบที่ยอมรับยกเว้นว่ามันไม่ได้อ้างอิงตัวแปร การไม่อ้าง$search_dirถึงหมายความว่ามันจะแตกหากมีช่องว่างในชื่อไดเรกทอรี (ในกรณีนี้คุณสามารถออกไปได้โดยไม่ต้องพูด$entryแต่จะเป็นการดีกว่าถ้าจะพูด)
Soren Bjornstad

คุณผิด. โซลูชันของฉันไม่เหมือนกันแม้ว่าจะคล้ายกับคำตอบที่ยอมรับ และมันก็ไม่ผิดก็ใช้งานได้ นี่คือสิ่งที่ฉันลองในระบบยูนิกซ์ที่แตกต่างกันรวมถึง Mac OS และมันใช้ได้กับทุกระบบ ไฟล์ที่มีช่องว่างแม้ว่าจะมีช่องว่างนำหน้าอยู่ในชื่อก็จะแสดงขึ้นอย่างถูกต้อง search_dir = สำหรับรายการใน $ search_dir / *; ทำ echo $ entry; เสร็จแล้ว. / aa aaa.txt ./add ./apm_tables.txt คุณสามารถลงคะแนนโซลูชั่นเมื่อคุณสามารถพิสูจน์ได้ว่าการแก้ปัญหาไม่ทำงานหรือก่อให้เกิดผลลัพธ์ที่ผิด
Andrushenko Alexander

หากคุณอ่านความคิดเห็นของฉันใกล้ชิดคุณจะเห็นว่าปัญหาเกิดจากช่องว่างในชื่อไดเรกทอรีไม่ใช่ชื่อไฟล์ มันแตกต่างจากปัญหาที่ผู้ถามใช้ แต่ก็ยังหมายความว่ารหัสสามารถทำลายโดยไม่ได้ตั้งใจ! mkdir "x y"; touch "x y/1.txt"; search_dir="x y"; for entry in $search_dir/*; do echo $entry; doneนี่คือตัวอย่างของสิ่งที่ไม่ได้ทำงาน: การวนซ้ำจะทำงานสองครั้งเมื่อพิมพ์xและครั้งที่สองy/*ซึ่งไม่น่าจะเป็นผลที่คาดหวัง
Soren

และฉันคิดว่าตัวแปรที่ไม่ได้ยกมานั้นมีความผิดอย่างผิด ๆ ซึ่งเป็นเหตุให้เกิด downvote พิจารณารหัสนี้เพื่อลบไดเรกทอรีย่อยของไดเรกทอรีค้นหาใด ๆ search_dir="x y"; for entry in $search_dir/*; do if [ -d "$entry" ]; then rm -rf "$entry"; fi; doneขณะที่ออกจากไฟล์: หากยังมีxไดเรกทอรีอยู่ในไดเรกทอรีปัจจุบันก็จะถูกลบทิ้งอย่างรวดเร็วเพียงเพราะคุณไม่ได้อ้างอิงตัวแปรของคุณ!
Soren

ดีคุณจะใช้รหัสที่สร้างขึ้นสำหรับงานหนึ่งเปลี่ยนงานเล็กน้อยและรหัสผิด สำหรับคำถามที่ถามวิธีแก้ปัญหาที่เสนอของฉันทำงาน แต่ ... ในฐานะนักพัฒนาฉันต้องเห็นด้วยกับข้อโต้แย้งของคุณ: wenn เราสามารถสร้างรหัสที่มีประสิทธิภาพมากขึ้นและโดยทั่วไปเราควรทำมัน ดังนั้นฉันจะเพิ่ม qoutes สิ่งนี้จะทำให้คำตอบของฉันเหมือนกับคำตอบที่ได้กล่าวไว้ข้างต้น แต่เพื่อประโยชน์ของดิสคัสชั่น (ซึ่งอาจเป็นประโยชน์สำหรับใครบางคน) ฉันก็ทิ้งคำตอบไว้เช่นกัน
Andrushenko Alexander

-1

เพียงแค่ป้อนคำสั่งง่ายๆนี้:

ls -d */

คุณช่วยอธิบายได้ไหม @Michael Sisko
Vipul

ls (list) -d (ลิสต์ไดเร็กทอรีเท่านั้น) * / (ของรูปแบบใด ๆ , แบ็กสแลช จำกัด รายการไว้ที่ไดเร็กทอรีเท่านั้น)
Michael Sisko
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.