ปัญหาเกี่ยวกับช่องว่างในชื่อไฟล์


8

ฉันต้องการทำบางสิ่งซ้ำ ๆ ในรายการไฟล์ ไฟล์ในคำถามมีช่องว่างในชื่อของพวกเขา:

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)

ตอนนี้ฉันต้องการสร้างไฟล์แต่ละไฟล์เหล่านี้:

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

(ฉันใช้ echo และ pipe เพื่อเขียนคำสั่งหลายบรรทัด)

เมื่อฉันทำเช่นนั้นจะทำงานอย่างถูกต้องในไฟล์เหล่านั้นที่ไม่มีช่องว่างในชื่อของพวกเขา แต่คนอื่น ๆ ...

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

เปลี่ยน$(ls)ไป"$(ls)"หรือ$fileจะ"$file"ไม่ทำงาน ฉันจะทำอย่างไร

แก้ไข:

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

หลอกลวง! ในขณะที่ฉันยังใหม่อยู่ในการทุบตีฉันต้องการให้สิ่งต่าง ๆ เรียบง่ายที่สุดเท่าที่จะเป็นไปได้ดังนั้นจึงไม่มีอะไรที่จะพยายามหลีกเลี่ยงช่องว่างหรือใช้xargsหรือวิธีแก้ปัญหาด้วยread -rแม้ว่าพวกเขาจะแก้ปัญหาได้ก็ตาม

ตามที่บางคนถาม: ใช่การใช้สิ่งนี้แทนที่จะstat *เป็นเรื่องแปลก แต่ฉันแค่ต้องการหาวิธีทั่วไปในการใช้คำสั่งเดียวกันกับชื่อไฟล์เป็นกลุ่มใน bash โดยใช้ for for loop ดังนั้นstatสามารถยืนสำหรับgzip, หรือgpgrm


1
เกิดstat *อะไรขึ้น (;-)
Rmano


ฉันแค่ใช้สถิติเป็นตัวอย่าง :) ฉันต้องการรวบรวมชื่อไฟล์ด้วย ls แล้วใช้ผลลัพธ์ของ ls ใน bash loop
user258532

คำตอบ:


12

คำพูดที่หลากหลายจากสิ่งที่echo 'ซับซ้อน

คุณสามารถใช้:

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

แต่ยัง

stat -- * 

... และถ้าคุณต้องการรวบรวมไฟล์จากนั้นให้ใช้คำสั่ง (ทำไม?) คุณสามารถไปด้วย (แต่ต้องระวังด้วยไฟล์ที่มีบรรทัดใหม่ ... (1))

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

... และถ้าคุณต้องการไฟล์ที่ซ่อนเกินไปเพียงแค่ใช้* .*เป็นรูปแบบ แต่แล้วจำไว้ว่า.และ..จะอยู่ในชุด

เช่นกันที่คุณไม่ควรแยกlsเอาท์พุท


(1) แต่ถ้าคุณมีชื่อไฟล์ด้วยการขึ้นบรรทัดใหม่คุณสมควรได้รับมัน ... ;-)


"แล้วใช้คำสั่ง (ทำไม?)" -> stat ทำหน้าที่เป็นคำสั่งโดยพลการในขณะที่ฉันกำลังลองวิธีทุบตีลูปกับชื่อไฟล์ อาจเป็น gpg, gzip หรืออะไรก็ได้
258532

@ user258532 for f in *; do command "$f"; doneสิ่งที่คำสั่งการใช้งานเสมอ ไม่แยกวิเคราะห์lsแน่นอนไม่เคยทำในfor วงและทำไมต้องใช้echo?
terdon

echo: เพราะฉันชอบเขียนคำสั่งในหลายบรรทัด ... :)
user258532

2
@ user258532 หืม? ทำไมคุณต้องใช้เสียงสะท้อนเพื่อสิ่งนั้น? เพียงกด Enter แล้วขึ้นบรรทัดใหม่ หากคุณสิ้นสุดเส้นด้วยอ้างเปิดหรือบนหนึ่งdo, |, &&ฯลฯ คุณสามารถดำเนินการต่อในการขึ้นบรรทัดใหม่ อาจเป็นหรือใช้ heredocs ไม่มีเหตุผลในการใช้echoและอาจทำให้เกิดปัญหาได้
terdon

"แค่กด Enter และขึ้นบรรทัดใหม่ต่อไป" อุ๊ยตาย :-D ตอนนี้ IQ ของฉันต่ำกว่า 0 อย่างเป็นทางการ - คุณรู้ไหมฉันใหม่จริงๆที่จะทุบตีและทำอย่างนั้น แต่จริง ๆ แล้วฉันได้รับเงินของฉันกับ R (ชุดสถิติและภาษาสคริปต์)
258532

6

สังเกตด้านบน: คุณสามารถแยกยาว / คำสั่งที่ซับซ้อนมากกว่าหลายบรรทัดโดยการเพิ่มพื้นที่ตามด้วยเครื่องหมายและกดEnterทุกครั้งที่คุณต้องการที่จะเริ่มต้นการเขียนเป็นบรรทัดใหม่แทนฟอร์กหลายกระบวนการโดยใช้echo [...] | bash; นอกจากนี้คุณควรใส่$fileเครื่องหมายคำพูดคู่เพื่อป้องกันไม่ให้statแตกในกรณีที่ชื่อไฟล์มีช่องว่าง:

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

ปัญหาคือการ$(ls)ขยายไปยังรายการชื่อไฟล์ที่มีช่องว่างและจะเกิดขึ้นเช่น"$(ls)"กัน

แม้การแก้ปัญหานี้วิธีนี้จะยังคงแบ่งชื่อไฟล์ที่มีเครื่องหมายแบ็กสแลชและชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ (ตามที่ terdon ชี้)

วิธีแก้ปัญหาสำหรับปัญหาทั้งสองคือไปป์เอาท์พุตของfindการwhileวนซ้ำที่ทำงานread -rเพื่อให้แต่ละการวนซ้ำread -rจะเก็บfindเอาต์พุตหนึ่งบรรทัดลงใน$file:

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

1
และไฟล์ที่ซ่อนอยู่? :)
AB

5
ที่จะยังคงล้มเหลวในชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ lsเพียงแค่ทำไม่ได้แยก เคย
terdon

4
@ user258532 ไม่ไม่ อย่างจริงจังเพียงแค่ไม่ได้แยก lsมีวิธีที่ดีกว่าและมีประสิทธิภาพมากขึ้น คุณอาจต้องการอ่านสิ่งนี้: ทำไม * ไม่ * แจง `ls '? สำหรับรายละเอียดเพิ่มเติม
terdon

1
ปัญหาที่นี่ไม่lsได้เป็นfor- forซ้ำมากกว่า (IFS คั่น) คำที่กำหนดหลังจากคำหลักin`
เกล็นแจ็

1
@glennjackman ดีใช่มันรวมกันของและfor จะดีเช่น lsfor f in *
terdon

3

ใช้ไฟล์เก่าดีfindใช้ได้กับไฟล์ที่ซ่อนอยู่บรรทัดใหม่และช่องว่าง

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

หรืออื่น ๆ แทน stat

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

1

ในฐานะที่เป็นคน R ฉันได้พบวิธีแก้ปัญหาใน 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]
 )
)
}

ฉันรู้ว่ามันบ้า ฉันหวังว่าผลลัพธ์ของlsการแยกจะง่ายกว่า ... R สามารถจัดการกับช่องว่างได้เนื่องจาก dir () ส่งคืนค่าอักขระที่เสนอราคา สิ่งใดระหว่างเครื่องหมายคำพูดนั้นเป็นชื่อไฟล์ที่ถูกต้องพร้อมช่องว่าง


3
อย่ารำคาญ (แต่ +1 ด้วยความพยายาม! :) เพียงใช้for f in *กด Enter และดำเนินการต่อในบรรทัดใหม่: doกด Enter อีกครั้งstat "$f"และป้อนอีกครั้งแล้วdoneป้อน นั่นเป็นคำสั่งที่แบ่งออกเป็น 4 บรรทัดและจะไม่แยกชื่อไฟล์ใด ๆ
terdon

1

ฉันพบปัญหาช่องว่างอื่น ๆ สำหรับลูปดังนั้นคำสั่ง (imo ที่แข็งแกร่งกว่า) ต่อไปนี้คือสิ่งที่ฉันใช้โดยทั่วไป นอกจากนี้ยังเหมาะอย่างยิ่งในท่อ

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

คุณสามารถรวมสิ่งนี้เข้ากับgrepหรือใช้findแทน:

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

คำตอบแรกของคุณล้มเหลวในชื่อไฟล์ที่มีเครื่องหมายทับขวาหรือช่องว่างนำหน้าหรือต่อท้าย คำตอบที่สองของคุณล้มเหลวโดยสิ้นเชิงเว้นแต่คุณจะเพิ่ม-Eตัวเลือกให้grepโดยที่จะไม่รับรู้+ในการแสดงออกปกติ ถึงอย่างนั้นก็แกว่งและพลาดกับคำถามนี้เนื่องจากคุณเอาชื่อไฟล์ที่มีช่องว่างgrep นอกจากนี้ยังลบชื่อไฟล์ที่มีตัวเลข (ตัวเลข) และเครื่องหมายวรรคตอน (เช่นวงเล็บ) ตามตัวอย่างในคำถาม และนั่นก็ไม่ได้พูดถึงชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่หรือเริ่มต้นด้วย-(เส้นประ)
สกอตต์

-1

แต่คุณสามารถเปลี่ยนชื่อไฟล์แทนช่องว่างด้วยอักขระอื่นเช่นขีดล่างดังนั้นคุณจึงกำจัดปัญหานี้ได้:

ในการทำเช่นนั้นให้เรียกใช้คำสั่งได้อย่างง่ายดาย:

for file in * ; do mv "$f" "${f// /_}" ; done

-2

คำตอบนี้จะแก้ปัญหาการแยกวิเคราะห์lsและดูแล backspaces และบรรทัดใหม่

ลองสิ่งนี้มันจะแก้ปัญหาของคุณโดยใช้ Internal Field Separator IFS

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

แต่คุณสามารถแก้ปัญหาได้โดยไม่จำเป็นต้องแยกวิเคราะห์เอาต์พุตโดยใช้

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

1
ไม่ทำงานสำหรับไฟล์ที่ซ่อน
AB

2
OP ไม่ขอไฟล์ที่ซ่อน
Maythux

1
ฉันไม่เห็นสาเหตุที่คุณต้องแก้ไขIFSที่นี่: การอ้างอิงตัวแปรควรเพียงพอที่จะป้องกันการแยกคำใช่หรือไม่
ขับเหล็ก

ls สำหรับการแยก
Maythux

สำหรับ downvote คืออะไร !!!
Maythux
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.