ฉันจะหนีช่องว่างในรายการ bash loop ได้อย่างไร


121

ฉันมีสคริปต์ bash เชลล์ที่วนซ้ำผ่านไดเร็กทอรีชายน์ทั้งหมด (แต่ไม่ใช่ไฟล์) ของไดเร็กทอรีบางรายการ ปัญหาคือชื่อไดเร็กทอรีบางชื่อมีช่องว่าง

นี่คือเนื้อหาของไดเร็กทอรีทดสอบของฉัน:

$ls -F test
Baltimore/  Cherry Hill/  Edison/  New York City/  Philadelphia/  cities.txt

และรหัสที่วนซ้ำในไดเรกทอรี:

for f in `find test/* -type d`; do
  echo $f
done

นี่คือผลลัพธ์:

การทดสอบ / บัลติมอร์
การทดสอบ / เชอร์รี่
เนินเขา
การทดสอบ / เอดิสัน 
การทดสอบ / New
นิวยอร์ก
เมือง
การทดสอบ / เดลเฟีย

Cherry Hill และ New York City ถือเป็น 2 หรือ 3 รายการแยกกัน

ฉันลองอ้างชื่อไฟล์แล้วดังนี้:

for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
  echo $f
done

แต่ไม่มีประโยชน์

ต้องมีวิธีง่ายๆในการทำเช่นนี้


คำตอบด้านล่างดีมาก แต่เพื่อให้ซับซ้อนมากขึ้น - ฉันไม่ต้องการใช้ไดเรกทอรีที่อยู่ในไดเรกทอรีทดสอบของฉันเสมอไป บางครั้งฉันต้องการส่งในชื่อไดเร็กทอรีเป็นพารามิเตอร์บรรทัดคำสั่งแทน

ฉันรับคำแนะนำของ Charles ในการตั้งค่า IFS และได้รับสิ่งต่อไปนี้:

dirlist="${@}"
(
  [[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
  for d in $dirlist; do
    echo $d
  done
)

และใช้งานได้ดีเว้นแต่จะมีช่องว่างในอาร์กิวเมนต์บรรทัดคำสั่ง (แม้ว่าอาร์กิวเมนต์เหล่านั้นจะถูกยกมาก็ตาม) ตัวอย่างเช่นการเรียกใช้สคริปต์เช่นนี้test.sh "Cherry Hill" "New York City"สร้างผลลัพธ์ต่อไปนี้:

เชอร์รี่
เนินเขา
ใหม่
นิวยอร์ก
เมือง

re: แก้ไขlist="$@"ทิ้งรายการของค่าเดิมโดยสิ้นเชิงยุบเป็นสตริง โปรดปฏิบัติตามแนวทางปฏิบัติในคำตอบของฉันทุกประการ - งานดังกล่าวไม่ได้รับการสนับสนุนในทุกที่ในนั้น ถ้าคุณต้องการส่งผ่านรายการอาร์กิวเมนต์บรรทัดคำสั่งไปยังโปรแกรมคุณควรรวบรวมไว้ในอาร์เรย์และขยายอาร์เรย์นั้นโดยตรง
Charles Duffy

คำตอบ:


105

อันดับแรกอย่าทำอย่างนั้น แนวทางที่ดีที่สุดคือใช้find -execอย่างเหมาะสม:

# this is safe
find test -type d -exec echo '{}' +

แนวทางที่ปลอดภัยอื่น ๆ คือการใช้ NUL-terminated list แม้ว่าจะต้องการการสนับสนุนในการค้นหาของคุณ-print0:

# this is safe
while IFS= read -r -d '' n; do
  printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d -print0)

คุณยังสามารถเติมอาร์เรย์จากการค้นหาและส่งอาร์เรย์นั้นในภายหลัง:

# this is safe
declare -a myarray
while IFS= read -r -d '' n; do
  myarray+=( "$n" )
done < <(find test -mindepth 1 -type d -print0)
printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want

หากการค้นหาของคุณไม่รองรับ-print0ผลลัพธ์ของคุณจะไม่ปลอดภัย - ด้านล่างนี้จะไม่ทำงานตามที่ต้องการหากมีไฟล์ที่มีการขึ้นบรรทัดใหม่ในชื่อ (ซึ่งใช่เป็นกฎหมาย):

# this is unsafe
while IFS= read -r n; do
  printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d)

หากไม่มีใครจะใช้วิธีใดวิธีหนึ่งข้างต้นแนวทางที่สาม (มีประสิทธิภาพน้อยกว่าทั้งในแง่ของเวลาและการใช้หน่วยความจำเนื่องจากอ่านผลลัพธ์ทั้งหมดของกระบวนการย่อยก่อนทำการแยกคำ) คือการใช้IFSตัวแปรที่ไม่ ไม่มีอักขระเว้นวรรค ปิด globbing ( set -f) เพื่อป้องกันสตริงที่มีอักขระ glob เช่น[], *หรือ?จากการขยายตัว:

# this is unsafe (but less unsafe than it would be without the following precautions)
(
 IFS=$'\n' # split only on newlines
 set -f    # disable globbing
 for n in $(find test -mindepth 1 -type d); do
   printf '%q\n' "$n"
 done
)

สุดท้ายสำหรับกรณีพารามิเตอร์บรรทัดคำสั่งคุณควรใช้อาร์เรย์หากเชลล์ของคุณรองรับ (เช่น ksh, bash หรือ zsh):

# this is safe
for d in "$@"; do
  printf '%s\n' "$d"
done

จะรักษาการแยกจากกัน โปรดทราบว่าการอ้างอิง (และการใช้$@มากกว่า$*) เป็นสิ่งสำคัญ อาร์เรย์สามารถเติมข้อมูลด้วยวิธีอื่น ๆ เช่นนิพจน์ glob:

# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
  printf '%s\n' "$d"
done

1
ไม่รู้เกี่ยวกับรส '+' สำหรับ -exec หวาน
Johannes Schaub - litb

1
ดูเหมือนว่ามันสามารถทำได้เช่นกันเช่น xargs ใส่อาร์กิวเมนต์ที่ท้ายคำสั่งที่กำหนดเท่านั้น: / นั่นทำให้ฉันบางครั้ง
Johannes Schaub - litb

ฉันคิดว่า -exec [name] {} + เป็นส่วนขยาย GNU และ 4.4-BSD (อย่างน้อยมันก็ไม่ปรากฏใน Solaris 8 และฉันก็ไม่คิดว่ามันจะอยู่ใน AIX 4.3 เหมือนกัน) ฉันเดาว่าพวกเราที่เหลืออาจติดอยู่กับการต่อท่อไปยัง xargs ...
Michael Ratanapintha

2
ฉันไม่เคยเห็นไวยากรณ์ของ $ '\ n' มาก่อน ทำงานอย่างไร? (ฉันคิดว่า IFS = '\ n' หรือ IFS = "\ n" จะใช้งานได้ แต่ก็ทำไม่ได้)
MCS

1
@crosstalk แน่นอนใน Solaris 10 ฉันเพิ่งใช้มัน
นิค

26
find . -type d | while read file; do echo $file; done

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

find . -type d -print0 | xargs -0 echo 'The directory is: '

ไม่จำเป็นต้อง xargs ดู find -exec ... {} +
Charles Duffy

4
@Charles: สำหรับไฟล์จำนวนมาก xargs จะมีประสิทธิภาพมากกว่ามาก: มันจะสร้างกระบวนการเดียวเท่านั้น อ็อพชัน -exec จะแยกกระบวนการใหม่สำหรับแต่ละไฟล์ซึ่งอาจเป็นลำดับของขนาดที่ช้าลง
Adam Rosenfield

1
ฉันชอบ xargs มากกว่า สองคนนี้ดูเหมือนจะทำเหมือนกันทั้งคู่ในขณะที่ xargs มีตัวเลือกมากกว่าเช่นวิ่งคู่ขนาน
Johannes Schaub - litb

2
อดัมไม่มี '+' ตัวนั้นจะรวมชื่อไฟล์ให้ได้มากที่สุดแล้วจึงดำเนินการ แต่มันจะไม่มีฟังก์ชั่นที่เรียบร้อยเหมือนการทำงานแบบขนาน :)
Johannes Schaub - litb

2
โปรดทราบว่าหากคุณต้องการทำอะไรกับชื่อไฟล์คุณจะต้องอ้างถึง เช่น:find . -type d | while read file; do ls "$file"; done
David Moles

23

นี่คือวิธีง่ายๆที่จัดการแท็บและ / หรือช่องว่างในชื่อไฟล์ หากคุณต้องจัดการกับอักขระแปลก ๆ อื่น ๆ ในชื่อไฟล์เช่นบรรทัดใหม่ให้เลือกคำตอบอื่น

ไดเร็กทอรีทดสอบ

ls -F test
Baltimore/  Cherry Hill/  Edison/  New York City/  Philadelphia/  cities.txt

รหัสที่จะเข้าไปในไดเรกทอรี

find test -type d | while read f ; do
  echo "$f"
done

ชื่อไฟล์ต้องถูกยกมา ( "$f") หากใช้เป็นอาร์กิวเมนต์ หากไม่มีเครื่องหมายคำพูดช่องว่างจะทำหน้าที่เป็นตัวคั่นอาร์กิวเมนต์และอาร์กิวเมนต์หลายรายการจะถูกกำหนดให้กับคำสั่งที่เรียกใช้

และผลลัพธ์:

test/Baltimore
test/Cherry Hill
test/Edison
test/New York City
test/Philadelphia

ขอบคุณสิ่งนี้ใช้ได้กับนามแฝงที่ฉันสร้างขึ้นเพื่อแสดงว่าแต่ละไดเร็กทอรีในโฟลเดอร์ปัจจุบันใช้พื้นที่เท่าใดมันสำลัก dirs บางส่วนที่มีช่องว่างในชาติก่อน สิ่งนี้ใช้ได้ใน zsh แต่คำตอบอื่น ๆ บางคำตอบไม่ได้:alias duc='ls -d * | while read D; do du -sh "$D"; done;'
Ted Naleid

2
หากคุณใช้ zsh คุณสามารถทำได้:alias duc='du -sh *(/)'
cbliard

@cbliard นี่ยังคงเป็นบั๊กกี้ ลองเรียกใช้ด้วยชื่อไฟล์โดยใช้ลำดับแท็บหรือเว้นวรรคหลาย ๆ ช่อง คุณจะทราบว่ามันเปลี่ยนสิ่งเหล่านั้นให้เป็นช่องว่างเดียวเนื่องจากคุณไม่ได้อ้างถึงในเสียงสะท้อนของคุณ แล้วก็มีกรณีของชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ ...
Charles Duffy

@CharlesDuffy ฉันลองใช้ลำดับแท็บและช่องว่างหลายช่อง ใช้ได้กับคำพูด ฉันลองขึ้นบรรทัดใหม่แล้วก็ไม่ได้ผลเลย ฉันอัปเดตคำตอบตามนั้น ขอขอบคุณที่ชี้ให้เห็น
cbliard

1
@cbliard Right - การเพิ่มคำพูดให้กับคำสั่ง echo คือสิ่งที่ฉันได้รับ สำหรับการขึ้นบรรทัดใหม่คุณสามารถทำให้การทำงานว่าด้วยการใช้ค้นหาและ-print0 IFS='' read -r -d '' f
Charles Duffy

7

นี่เป็นเรื่องยุ่งยากอย่างมากใน Unix มาตรฐานและโซลูชันส่วนใหญ่มักใช้การขึ้นบรรทัดใหม่หรืออักขระอื่น ๆ อย่างไรก็ตามหากคุณใช้ชุดเครื่องมือ GNU คุณสามารถใช้ประโยชน์จากfindตัวเลือก-print0และใช้xargsกับตัวเลือกที่เกี่ยวข้อง-0(ลบ - ศูนย์) มีอักขระสองตัวที่ไม่สามารถปรากฏในชื่อไฟล์ธรรมดาได้ นั่นคือเครื่องหมายทับและ NUL '\ 0' เห็นได้ชัดว่าเครื่องหมายทับปรากฏในชื่อพา ธ ดังนั้นวิธีแก้ปัญหา GNU ของการใช้ NUL '\ 0' เพื่อทำเครื่องหมายท้ายชื่อจึงมีความแยบยลและไม่น่าเชื่อถือ


4

ทำไมไม่ใส่

IFS='\n'

หน้าคำสั่ง for? ซึ่งจะเปลี่ยนตัวคั่นฟิลด์จาก <Space> <Tab> <Newline> เป็นแค่ <Newline>


4
find . -print0|while read -d $'\0' file; do echo "$file"; done

1
-d $'\0'เหมือนกับ-d ''- เนื่องจาก bash ใช้สตริงที่สิ้นสุดด้วย NUL อักขระตัวแรกของสตริงว่างคือ NUL และด้วยเหตุผลเดียวกัน NUL จึงไม่สามารถแสดงภายในสตริง C ได้เลย
Charles Duffy

4

ฉันใช้

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
for f in $( find "$1" -type d ! -path "$1" )
do
  echo $f
done
IFS=$SAVEIFS

มันจะไม่เพียงพอหรือ?
ไอเดียนำมาจากhttp://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html


เคล็ดลับที่ดี: มีประโยชน์มากสำหรับตัวเลือกสำหรับ osascript บรรทัดคำสั่ง (OS X AppleScript) โดยที่ช่องว่างจะแบ่งอาร์กิวเมนต์ออกเป็นหลายพารามิเตอร์โดยมีเพียงพารามิเตอร์เดียวเท่านั้น
ทิม

ไม่มันไม่เพียงพอ มันไม่มีประสิทธิภาพ (เนื่องจากการใช้งานโดยไม่จำเป็น$(echo ...)) ไม่จัดการชื่อไฟล์ที่มีนิพจน์ glob อย่างถูกต้องไม่จัดการชื่อไฟล์ที่มี$'\b'อักขระ $ '\ n' อย่างถูกต้องและยิ่งไปกว่านั้นยังแปลงช่องว่างหลาย ๆ ช่องให้เป็นอักขระช่องว่างเดียวบน ด้านเอาต์พุตเนื่องจากการอ้างอิงไม่ถูกต้อง
Charles Duffy

4

อย่าจัดเก็บรายการเป็นสตริง จัดเก็บเป็นอาร์เรย์เพื่อหลีกเลี่ยงความสับสนในตัวคั่นทั้งหมดนี้ นี่คือตัวอย่างสคริปต์ที่จะทำงานในไดเร็กทอรีย่อยทั้งหมดของการทดสอบหรือรายการที่ให้มาในบรรทัดคำสั่ง:

#!/bin/bash
if [ $# -eq 0 ]; then
        # if no args supplies, build a list of subdirs of test/
        dirlist=() # start with empty list
        for f in test/*; do # for each item in test/ ...
                if [ -d "$f" ]; then # if it's a subdir...
                        dirlist=("${dirlist[@]}" "$f") # add it to the list
                fi
        done
else
        # if args were supplied, copy the list of args into dirlist
        dirlist=("$@")
fi
# now loop through dirlist, operating on each one
for dir in "${dirlist[@]}"; do
        printf "Directory: %s\n" "$dir"
done

ทีนี้มาลองใช้ในไดเร็กทอรีทดสอบที่มีเส้นโค้งหรือสองเส้นเข้า:

$ ls -F test
Baltimore/
Cherry Hill/
Edison/
New York City/
Philadelphia/
this is a dirname with quotes, lfs, escapes: "\''?'?\e\n\d/
this is a file, not a directory
$ ./test.sh 
Directory: test/Baltimore
Directory: test/Cherry Hill
Directory: test/Edison
Directory: test/New York City
Directory: test/Philadelphia
Directory: test/this is a dirname with quotes, lfs, escapes: "\''
'
\e\n\d
$ ./test.sh "Cherry Hill" "New York City"
Directory: Cherry Hill
Directory: New York City

1
มองย้อนกลับไปเกี่ยวกับเรื่องนี้ - มีจริงเป็นวิธีการแก้ปัญหาด้วยการดวลจุดโทษ POSIX: คุณสามารถนำมาใช้อาร์เรย์ผนวกกับมันด้วย"$@" set -- "$@" "$f"
Charles Duffy

4

คุณสามารถใช้ IFS (ตัวคั่นฟิลด์ภายใน) ชั่วคราวโดยใช้:

OLD_IFS=$IFS     # Stores Default IFS
IFS=$'\n'        # Set it to line break
for f in `find test/* -type d`; do
    echo $f
done

$IFS=$OLD_IFS


กรุณาให้คำอธิบาย
Steve K

IFS ระบุว่าสัญลักษณ์ตัวคั่นคืออะไรดังนั้นชื่อไฟล์ที่มีช่องว่างจะไม่ถูกตัดทอน
amazingthere

$ IFS = $ OLD_IFS ในตอนท้ายควรเป็น: IFS = $ OLD_IFS
Michel Donais

3

ps ถ้าเป็นเรื่องเกี่ยวกับช่องว่างในอินพุตเท่านั้นคำพูดคู่บางคำก็ทำงานได้อย่างราบรื่นสำหรับฉัน ...

read artist;

find "/mnt/2tb_USB_hard_disc/p_music/$artist" -type f -name *.mp3 -exec mpg123 '{}' \;

2

หากต้องการเพิ่มสิ่งที่โจนาธานกล่าว: ใช้-print0ตัวเลือกเพื่อfindร่วมกับสิ่งxargsต่อไปนี้:

find test/* -type d -print0 | xargs -0 command

ที่จะดำเนินการคำสั่งcommandด้วยอาร์กิวเมนต์ที่เหมาะสม ไดเร็กทอรีที่มีช่องว่างจะถูกยกมาอย่างถูกต้อง (กล่าวคือจะถูกส่งผ่านเป็นอาร์กิวเมนต์เดียว)


1
#!/bin/bash

dirtys=()

for folder in *
do    
 if [ -d "$folder" ]; then    
    dirtys=("${dirtys[@]}" "$folder")    
 fi    
done    

for dir in "${dirtys[@]}"    
do    
   for file in "$dir"/\*.mov   # <== *.mov
   do    
       #dir_e=`echo "$dir" | sed 's/[[:space:]]/\\\ /g'`   -- This line will replace each space into '\ '   
       out=`echo "$file" | sed 's/\(.*\)\/\(.*\)/\2/'`     # These two line code can be written in one line using multiple sed commands.    
       out=`echo "$out" | sed 's/[[:space:]]/_/g'`    
       #echo "ffmpeg -i $out_e -sameq -vcodec msmpeg4v2 -acodec pcm_u8 $dir_e/${out/%mov/avi}"    
       `ffmpeg -i "$file" -sameq -vcodec msmpeg4v2 -acodec pcm_u8 "$dir"/${out/%mov/avi}`    
   done    
done

โค้ดด้านบนจะแปลงไฟล์. mov เป็น. avi ไฟล์. mov อยู่คนละโฟลเดอร์และชื่อโฟลเดอร์ก็มีช่องว่างด้วยเช่นกัน สคริปต์ด้านบนของฉันจะแปลงไฟล์. mov เป็นไฟล์. avi ในโฟลเดอร์เดียวกัน ฉันไม่รู้ว่ามันช่วยคุณได้หรือเปล่า

กรณี:

[sony@localhost shell_tutorial]$ ls
Chapter 01 - Introduction  Chapter 02 - Your First Shell Script
[sony@localhost shell_tutorial]$ cd Chapter\ 01\ -\ Introduction/
[sony@localhost Chapter 01 - Introduction]$ ls
0101 - About this Course.mov   0102 - Course Structure.mov
[sony@localhost Chapter 01 - Introduction]$ ./above_script
 ... successfully executed.
[sony@localhost Chapter 01 - Introduction]$ ls
0101_-_About_this_Course.avi  0102_-_Course_Structure.avi
0101 - About this Course.mov  0102 - Course Structure.mov
[sony@localhost Chapter 01 - Introduction]$ CHEERS!

ไชโย!


echo "$name" | ...ไม่ทำงานถ้าnameเป็น-nและวิธีการทำงานกับชื่อที่มีลำดับแบ็กสแลช - เอสเคปขึ้นอยู่กับการนำไปใช้งานของคุณ - POSIX ทำให้พฤติกรรมechoในกรณีนั้นไม่ได้กำหนดไว้อย่างชัดเจน (ในขณะที่ XSI ที่ขยาย POSIX ทำให้การขยายลำดับแบ็กสแลช - เอสเคปพฤติกรรมที่กำหนดมาตรฐาน และระบบ GNU - รวมถึง bash - โดยไม่POSIXLY_CORRECT=1ทำลายมาตรฐาน POSIX โดยการใช้งาน-e(ในขณะที่ข้อมูลจำเพาะต้องecho -eพิมพ์-eบนเอาต์พุต) printf '%s\n' "$name" | ...ปลอดภัยกว่า
Charles Duffy

1

ต้องจัดการกับช่องว่างในชื่อพา ธ ด้วย สิ่งที่ฉันทำในที่สุดคือการใช้การเรียกซ้ำและfor item in /path/*:

function recursedir {
    local item
    for item in "${1%/}"/*
    do
        if [ -d "$item" ]
        then
            recursedir "$item"
        else
            command
        fi
    done
}

1
อย่าใช้functionคำสำคัญ - ทำให้รหัสของคุณไม่เข้ากันกับ POSIX sh แต่ไม่มีจุดประสงค์อื่นที่เป็นประโยชน์ คุณสามารถกำหนดฟังก์ชันด้วยrecursedir() {การเพิ่มสอง parens และลบคำสำคัญของฟังก์ชันและสิ่งนี้จะเข้ากันได้กับเชลล์ที่เข้ากันได้กับ POSIX ทั้งหมด
Charles Duffy

1

แปลงรายการไฟล์เป็นอาร์เรย์ Bash สิ่งนี้ใช้วิธีการของ Matt McClure ในการส่งคืนอาร์เรย์จากฟังก์ชัน Bash: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html ผลลัพธ์คือวิธี เพื่อแปลงอินพุตแบบหลายบรรทัดเป็นอาร์เรย์ Bash

#!/bin/bash

# This is the command where we want to convert the output to an array.
# Output is: fileSize fileNameIncludingPath
multiLineCommand="find . -mindepth 1 -printf '%s %p\\n'"

# This eval converts the multi-line output of multiLineCommand to a
# Bash array. To convert stdin, remove: < <(eval "$multiLineCommand" )
eval "declare -a myArray=`( arr=(); while read -r line; do arr[${#arr[@]}]="$line"; done; declare -p arr | sed -e 's/^declare -a arr=//' ) < <(eval "$multiLineCommand" )`"

for f in "${myArray[@]}"
do
   echo "Element: $f"
done

วิธีนี้ดูเหมือนจะใช้งานได้แม้ว่าจะมีอักขระที่ไม่เหมาะสมและเป็นวิธีทั่วไปในการแปลงอินพุตใด ๆ เป็นอาร์เรย์ Bash ข้อเสียคือถ้าอินพุตยาวคุณอาจเกินขีด จำกัด ขนาดบรรทัดคำสั่งของ Bash หรือใช้หน่วยความจำจำนวนมาก

แนวทางที่ลูปที่ทำงานในรายการในที่สุดก็มีรายการที่ถูกส่งไปมีข้อเสียตรงที่การอ่าน stdin ไม่ใช่เรื่องง่าย (เช่นการขออินพุตจากผู้ใช้) และลูปเป็นกระบวนการใหม่ดังนั้นคุณอาจสงสัยว่าทำไมตัวแปร คุณตั้งค่าภายในลูปจะไม่สามารถใช้งานได้หลังจากการวนซ้ำเสร็จสิ้น

ฉันไม่ชอบการตั้งค่า IFS ด้วยมันอาจทำให้โค้ดอื่น ๆ ยุ่งเหยิงได้


หากคุณใช้IFS='' readในบรรทัดเดียวกันการตั้งค่า IFS จะแสดงสำหรับคำสั่ง read เท่านั้นและไม่ได้หลีกเลี่ยง ไม่มีเหตุผลที่จะไม่ชอบการตั้งค่า IFS ด้วยวิธีนี้
Charles Duffy

1

ฉันเห็นคำตอบที่ซับซ้อนมากเกินไป ฉันไม่ต้องการส่งเอาท์พุตของยูทิลิตี find หรือเขียน loop เพราะ find มีตัวเลือก "exec" สำหรับสิ่งนี้

ปัญหาของฉันคือฉันต้องการย้ายไฟล์ทั้งหมดที่มีนามสกุล dbf ไปยังโฟลเดอร์ปัจจุบันและบางไฟล์มีพื้นที่สีขาว

ฉันจัดการมัน:

 find . -name \*.dbf -print0 -exec mv '{}'  . ';'

ดูง่ายมากสำหรับฉัน


0

เพิ่งพบว่ามีความคล้ายคลึงกันระหว่างคำถามของฉันกับของคุณ หากคุณต้องการส่งผ่านอาร์กิวเมนต์เป็นคำสั่ง

test.sh "Cherry Hill" "New York City"

เพื่อพิมพ์ออกมาตามลำดับ

for SOME_ARG in "$@"
do
    echo "$SOME_ARG";
done;

สังเกตว่า $ @ ล้อมรอบด้วยเครื่องหมายคำพูดคู่หมายเหตุบางส่วนที่นี่


0

ฉันต้องการแนวคิดเดียวกันในการบีบอัดไดเรกทอรีหรือไฟล์หลาย ๆ ไฟล์ตามลำดับจากโฟลเดอร์หนึ่ง ๆ ฉันได้แก้ไขโดยใช้ awk เพื่อแยกวิเคราะห์รายการจาก ls และเพื่อหลีกเลี่ยงปัญหาพื้นที่ว่างในชื่อ

source="/xxx/xxx"
dest="/yyy/yyy"

n_max=`ls . | wc -l`

echo "Loop over items..."
i=1
while [ $i -le $n_max ];do
item=`ls . | awk 'NR=='$i'' `
echo "File selected for compression: $item"
tar -cvzf $dest/"$item".tar.gz "$item"
i=$(( i + 1 ))
done
echo "Done!!!"

คุณคิดอย่างไร?


ฉันคิดว่าสิ่งนี้จะทำงานไม่ถูกต้องหากชื่อไฟล์มีบรรทัดใหม่อยู่ในนั้น บางทีคุณควรลอง
user000001


-3

สำหรับฉันสิ่งนี้ได้ผลและมันค่อนข้าง "สะอาด":

for f in "$(find ./test -type d)" ; do
  echo "$f"
done

4
แต่ที่แย่กว่านี้คือ เครื่องหมายคำพูดคู่รอบ ๆ การค้นหาทำให้ชื่อพา ธ ทั้งหมดเชื่อมต่อกันเป็นสตริงเดียว เปลี่ยนเสียงสะท้อนเป็นlsเพื่อดูปัญหา
NVRAM

-4

เพิ่งมีปัญหาเรื่องตัวแปรง่ายๆ ... แปลงไฟล์. flv ที่พิมพ์เป็น. mp3 (หาว)

for file in read `find . *.flv`; do ffmpeg -i ${file} -acodec copy ${file}.mp3;done

ค้นหาไฟล์แฟลชของผู้ใช้ Macintosh ซ้ำ ๆ และเปลี่ยนเป็นไฟล์เสียง (คัดลอกไม่มีการแปลงรหัส) ... มันเหมือนกับในขณะที่ด้านบนสังเกตว่าการอ่านแทนที่จะเป็นเพียง 'for file in ' จะหายไป


2
คำreadหลังinคืออีกหนึ่งคำในรายการที่คุณกำลังทำซ้ำ สิ่งที่คุณโพสต์เป็นเวอร์ชันที่เสียหายเล็กน้อยของสิ่งที่ผู้ถามมีซึ่งไม่ได้ผล คุณอาจตั้งใจจะโพสต์สิ่งที่แตกต่างออกไป แต่ก็อาจมีคำตอบอื่น ๆ ครอบคลุมอยู่ที่นี่
Gilles 'SO- หยุดชั่ว'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.