วนเนื้อหาของไฟล์ใน Bash


1387

ฉันจะวนซ้ำแต่ละบรรทัดของไฟล์ข้อความด้วยBash ได้อย่างไร

ด้วยสคริปต์นี้:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

ฉันได้รับผลลัพธ์นี้บนหน้าจอ:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(ต่อมาฉันต้องการทำสิ่งที่ซับซ้อน$pมากกว่าเพียงแค่เอาท์พุทไปยังหน้าจอ)


ตัวแปรสภาพแวดล้อมSHELLคือ (จาก env):

SHELL=/bin/bash

/bin/bash --version เอาท์พุท:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/version เอาท์พุท:

Linux version 2.6.18.2-34-default (geeko@buildhost) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

ไฟล์ peptides.txt ประกอบด้วย:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL

19
โอ้ฉันเห็นหลายสิ่งเกิดขึ้นที่นี่: ความคิดเห็นทั้งหมดถูกลบและคำถามถูกเปิดใหม่ สำหรับการอ้างอิงเท่านั้นคำตอบที่ยอมรับได้ในอ่านบรรทัดไฟล์โดยบรรทัดที่กำหนดค่าให้กับตัวแปรแก้ไขปัญหาในแบบที่เป็นที่ยอมรับและควรเป็นที่ต้องการมากกว่าคำตอบที่ยอมรับได้ที่นี่
fedorqui 'ดังนั้นหยุดทำอันตราย'

คำตอบ:


2090

วิธีหนึ่งในการทำคือ:

while read p; do
  echo "$p"
done <peptides.txt

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

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt

พิเศษถ้าตัวลูปอาจอ่านจากอินพุตมาตรฐานคุณสามารถเปิดไฟล์โดยใช้ file descriptor อื่น:

while read -u 10 p; do
  ...
done 10<peptides.txt

ที่นี่ 10 เป็นเพียงตัวเลขใดก็ได้ (แตกต่างจาก 0, 1, 2)


7
ฉันจะตีความบรรทัดสุดท้ายได้อย่างไร ไฟล์ peptides.txt ถูกเปลี่ยนเส้นทางไปยังอินพุตมาตรฐานและไปยังบล็อก while ทั้งหมดหรือไม่?
Peter Mortensen

11
"Slurp peptides.txt ลงไปในส่วนนี้ในขณะที่วนรอบดังนั้นคำสั่ง 'read' จึงมีบางสิ่งที่ต้องใช้" วิธี "cat" ของฉันคล้ายกันโดยการส่งเอาต์พุตของคำสั่งไปยังบล็อกแบบบล็อกเพื่อการบริโภคโดย 'read' ด้วยเช่นกันเฉพาะมันจะเปิดตัวโปรแกรมอื่นเพื่อทำงานให้เสร็จ
Warren Young

8
วิธีนี้ดูเหมือนจะข้ามบรรทัดสุดท้ายของไฟล์
xastor

5
พูดสองบรรทัด !! echo "$ p" และไฟล์ .. เชื่อฉันสิมันจะกัดคุณถ้าคุณทำไม่ได้ !!! ฉันรู้ว่า! lol
Mike Q

5
ทั้งสองเวอร์ชันไม่สามารถอ่านบรรทัดสุดท้ายหากไม่ได้ขึ้นบรรทัดใหม่ ใช้เสมอwhile read p || [[ -n $p ]]; do ...
dawg

447
cat peptides.txt | while read line 
do
   # do something with $line here
done

และตัวแปรหนึ่งซับ:

cat peptides.txt | while read line; do something_with_$line_here; done

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

คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดยทำสิ่งต่อไปนี้:

cat peptides.txt | while read line || [[ -n $line ]];
do
   # do something with $line here
done

68
โดยทั่วไปหากคุณใช้ "cat" มีเพียงอาร์กิวเมนต์เดียวคุณกำลังทำอะไรผิดพลาด (หรือไม่ดี)
JesperE

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

74
มีอีกปัญหาที่ร้ายแรงกว่านี้: เนื่องจากห่วงในขณะที่เป็นส่วนหนึ่งของไปป์นั้นจะทำงานใน subshell และด้วยเหตุนี้ตัวแปรใด ๆ ที่ตั้งอยู่ภายในลูปจะหายไปเมื่อออกจาก (ดูbash-hackers.org/wiki/doku php / mirroring / bashfaq / 024 ) สิ่งนี้อาจน่ารำคาญมาก (ขึ้นอยู่กับสิ่งที่คุณพยายามทำในลูป)
Gordon Davisson

25
ฉันใช้ "cat file |" เป็นจุดเริ่มต้นของคำสั่งมากมายเพราะฉันมักจะสร้างต้นแบบด้วย "head file |"
mat kelcey

62
สิ่งนี้อาจไม่ได้มีประสิทธิภาพ แต่สามารถอ่านได้มากกว่าคำตอบอื่น ๆ
Savage Reader

144

ตัวเลือก 1a:ขณะที่วนซ้ำ: ทีละบรรทัด: การเปลี่ยนเส้นทางอินพุต

#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do 
    echo $p
done < $filename

ตัวเลือก 1b:ขณะที่วนซ้ำ: ทีละบรรทัด:
เปิดไฟล์อ่านจากไฟล์ descriptor (ในกรณีนี้ file descriptor # 4)

#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
    echo $p
done

สำหรับตัวเลือก 1b: จำเป็นต้องปิดไฟล์ descriptor อีกครั้งหรือไม่? เช่นห่วงอาจเป็นวงใน
Peter Mortensen

3
ตัวอธิบายไฟล์จะถูกล้างข้อมูลด้วยกระบวนการออก การปิดอย่างชัดเจนสามารถทำได้เพื่อนำหมายเลข fd มาใช้ซ้ำ หากต้องการปิด fd ให้ใช้ exec อื่นด้วย & - ไวยากรณ์เช่นนี้: exec 4 <& -
Stan Graves

1
ขอบคุณสำหรับตัวเลือก 2 ฉันพบปัญหามากมายกับตัวเลือกที่ 1 เพราะฉันต้องการอ่านจาก stdin ภายในลูป; ในกรณีเช่นนี้ตัวเลือกที่ 1 จะไม่ทำงาน
masgo

4
คุณควรจะชี้ให้เห็นได้อย่างชัดเจนมากขึ้นว่าตัวเลือกที่ 2 เป็นกำลังใจอย่างมาก @masgo Option 1b ควรทำงานในกรณีนั้นและสามารถรวมกับไวยากรณ์การเปลี่ยนเส้นทางอินพุตจาก Option 1a โดยแทนที่done < $filenameด้วยdone 4<$filename(ซึ่งมีประโยชน์หากคุณต้องการอ่านชื่อไฟล์จากพารามิเตอร์คำสั่งซึ่งในกรณีนี้คุณสามารถแทนที่$filenameด้วย$1)
Egor Hans

ฉันจำเป็นต้องวนซ้ำเนื้อหาไฟล์เช่นในtail -n +2 myfile.txt | grep 'somepattern' | cut -f3ขณะที่ใช้คำสั่ง ssh ภายในลูป (สิ้นเปลือง stdin); ตัวเลือกที่ 2 ที่นี่ดูเหมือนจะเป็นวิธีเดียวหรือไม่
user5359531

85

นี่ไม่ใช่คำตอบที่ดีกว่าคำตอบอื่น ๆ แต่เป็นอีกวิธีหนึ่งในการทำให้งานเสร็จในไฟล์ที่ไม่มีช่องว่าง (ดูความคิดเห็น) ฉันพบว่าฉันมักจะต้องมีหนึ่ง liners เพื่อขุดผ่านรายการในไฟล์ข้อความโดยไม่ต้องใช้ขั้นตอนพิเศษในการใช้ไฟล์สคริปต์แยกต่างหาก

for word in $(cat peptides.txt); do echo $word; done

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

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done

หรือถ้าคุณตั้งใจจะใช้สิ่งนี้เช่นโปรแกรมแก้ไขกระแสข้อมูล (เรียนรู้) คุณสามารถถ่ายโอนข้อมูลไปยังไฟล์อื่นดังนี้

for word in $(cat peptides.txt); do cmd_a.sh $word; cmd_b.py $word; done > outfile.txt

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

OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

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

ขอให้โชคดี!


6
bash $ (<peptides.txt) อาจจะดูดีกว่า แต่ก็ยังผิดที่ Joao พูดถูกต้องคุณกำลังทำการตรรกะการแทนที่คำสั่งที่เว้นวรรคหรือบรรทัดใหม่เป็นสิ่งเดียวกัน หากบรรทัดมีช่องว่างภายในลูปจะดำเนินการสองครั้งขึ้นไปสำหรับหนึ่งบรรทัด ดังนั้นรหัสของคุณควรอ่านอย่างถูกต้อง: สำหรับ word ใน $ (<peptides.txt); ทำ .... ถ้าคุณรู้ว่าไม่มีช่องว่างแล้วเส้นเท่ากับคำและคุณโอเค
maxpolk

2
@ JoaoCosta, maxpolk: ข้อดีที่ฉันไม่ได้พิจารณา ฉันได้แก้ไขโพสต์ต้นฉบับเพื่อให้สะท้อนถึงพวกเขา ขอบคุณ!
อันยิ่งใหญ่

2
การใช้forทำให้โทเค็นอินพุต / บรรทัดอยู่ภายใต้การขยายของเชลล์ซึ่งโดยทั่วไปไม่พึงประสงค์ ลองสิ่งนี้: for l in $(echo '* b c'); do echo "[$l]"; done- ดังที่คุณเห็น*- ถึงแม้จะเป็นตัวอักษรที่ยกมาแต่เดิม- ขยายไปยังไฟล์ในไดเรกทอรีปัจจุบัน
mklement0

2
@dblanchard: ตัวอย่างสุดท้ายโดยใช้ $ IFS ควรละเว้นช่องว่าง คุณลองเวอร์ชั่นนั้นแล้วหรือยัง?
อันยิ่งใหญ่

4
วิธีที่คำสั่งนี้มีความซับซ้อนมากขึ้นเนื่องจากปัญหาสำคัญได้รับการแก้ไขแสดงได้ดีว่าทำไมการใช้forเพื่อย้ำบรรทัดไฟล์เป็นความคิดที่ไม่ดี นอกจากนี้ส่วนขยายที่กล่าวถึงโดย @ mklement0 (แม้ว่าอาจจะสามารถหลีกเลี่ยงได้โดยการนำเครื่องหมายคำพูดที่ใช้ Escape ซึ่งทำให้สิ่งต่าง ๆ มีความซับซ้อนมากขึ้นและอ่านได้น้อยลง)
Egor Hans

69

อีกสองสามสิ่งที่ไม่ได้รับคำตอบอื่น ๆ :

อ่านจากไฟล์ที่มีการคั่น

# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
  # process the fields
  # if the line has less than three fields, the missing fields will be set to an empty string
  # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt

อ่านจากเอาต์พุตของคำสั่งอื่นโดยใช้การทดแทนกระบวนการ

while read -r line; do
  # process the line
done < <(command ...)

วิธีนี้ดีกว่าcommand ... | while read -r line; do ...เพราะขณะที่ลูปที่นี่ทำงานในเชลล์ปัจจุบันแทนที่จะเป็นเชลล์ย่อยเช่นเดียวกับในกรณีหลัง ดูโพสต์ที่เกี่ยวข้องกับตัวแปรการปรับเปลี่ยนภายในห่วงขณะไม่ได้จำได้

เช่นการอ่านจากอินพุตที่คั่นด้วย null ตัวอย่างเช่น find ... -print0

while read -r -d '' line; do
  # logic
  # use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)

การอ่านที่เกี่ยวข้อง: BashFAQ / 020 - ฉันจะค้นหาและจัดการชื่อไฟล์ที่มีการขึ้นบรรทัดใหม่ช่องว่างหรือทั้งสองอย่างปลอดภัยได้อย่างไร

อ่านจากไฟล์มากกว่าหนึ่งไฟล์ในแต่ละครั้ง

while read -u 3 -r line1 && read -u 4 -r line2; do
  # process the lines
  # note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt

ตามคำตอบของ @ chepner ที่นี่ :

-uเป็นนามสกุลทุบตี เพื่อความเข้ากันได้ของ POSIX การโทรแต่ละครั้งจะมีหน้าตาคล้ายread -r X <&3กัน

อ่านไฟล์ทั้งหมดลงในอาร์เรย์ (เวอร์ชั่น Bash ก่อนหน้านี้ถึง 4)

while read -r line; do
    my_array+=("$line")
done < my_file

หากไฟล์ลงท้ายด้วยบรรทัดที่ไม่สมบูรณ์ (บรรทัดใหม่หายไปในตอนท้าย) ดังนั้น:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

การอ่านไฟล์ทั้งหมดในอาร์เรย์ (Bash เวอร์ชั่น 4x และใหม่กว่า)

readarray -t my_array < my_file

หรือ

mapfile -t my_array < my_file

และจากนั้น

for line in "${my_array[@]}"; do
  # process the lines
done

กระทู้ที่เกี่ยวข้อง:


โปรดทราบว่าแทนที่จะcommand < input_filename.txtทำเช่นนั้นได้ตลอดเวลาinput_generating_command | commandหรือcommand < <(input_generating_command)
masterxilo

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

45

ใช้สักครู่ห่วงเช่นนี้:

while IFS= read -r line; do
   echo "$line"
done <file

หมายเหตุ:

  1. หากคุณไม่ได้ตั้งค่าIFSอย่างถูกต้องคุณจะสูญเสียการเยื้อง

  2. คุณควรใช้ตัวเลือก -r กับการอ่านเกือบทุกครั้ง

  3. อย่าอ่านบรรทัดด้วย for


2
ทำไมต้อง-rเลือก
David C. Rankin

2
@ DavidC.Rankin ตัวเลือก -r ป้องกันการตีความทับขวา Note #2คือการเชื่อมโยงซึ่งจะมีการอธิบายในรายละเอียด ...
Jahid

รวมสิ่งนี้กับตัวเลือก "read -u" ในคำตอบอื่นแล้วมันสมบูรณ์แบบ
Florin Andrei

@FlorinAndrei: ตัวอย่างข้างต้นไม่ต้องการ-uตัวเลือกคุณกำลังพูดถึงตัวอย่างอื่นด้วย-uหรือไม่
Jahid

มองผ่านลิงก์ของคุณและรู้สึกประหลาดใจที่ไม่มีคำตอบที่เชื่อมโยงลิงก์ของคุณในหมายเหตุ 2 หน้านั้นมีทุกสิ่งที่คุณจำเป็นต้องรู้เกี่ยวกับเรื่องนั้น หรือคำตอบแบบลิงก์เท่านั้นเป็นกำลังใจหรืออะไร?
Egor Hans

14

สมมติว่าคุณมีไฟล์นี้:

$ cat /tmp/test.txt
Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR

มีองค์ประกอบสี่อย่างที่จะเปลี่ยนความหมายของเอาต์พุตไฟล์ที่อ่านโดยโซลูชัน Bash จำนวนมาก:

  1. บรรทัดว่าง 4
  2. ช่องว่างนำหน้าหรือต่อท้ายบนสองบรรทัด
  3. การรักษาความหมายของแต่ละบรรทัด (เช่นแต่ละบรรทัดเป็นบันทึก);
  4. บรรทัด 6 ไม่สิ้นสุดด้วย CR

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

นี่คือวิธีการที่อาจเปลี่ยนแปลงไฟล์ (เปรียบเทียบกับสิ่งที่catส่งคืน):

1) สูญเสียบรรทัดสุดท้ายและช่องว่างนำหน้าและต่อท้าย:

$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'

(ถ้าคุณทำwhile IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txtแทนคุณรักษาช่องว่างนำหน้าและต่อท้าย แต่ยังคงสูญเสียบรรทัดสุดท้ายหากยังไม่สิ้นสุดด้วย CR)

2) การใช้การทดแทนกระบวนการด้วยcatจะอ่านไฟล์ทั้งหมดในหนึ่งอึกและสูญเสียความหมายของแต่ละบรรทัด:

$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR'

(ถ้าคุณลบออก"จาก$(cat /tmp/test.txt)คุณอ่านคำไฟล์โดยคำมากกว่าหนึ่งอึกนอกจากนี้ยังอาจไม่ได้ตั้งใจ ... )


วิธีที่แข็งแกร่งที่สุดและง่ายที่สุดในการอ่านไฟล์แบบบรรทัดต่อบรรทัดและรักษาระยะห่างทั้งหมดคือ:

$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'    Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space    '
'Line 6 has no ending CR'

หากคุณต้องการตัดส่วนนำและพื้นที่ซื้อขายออกให้นำชิ้นIFS=ส่วนออก:

$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'

(ไฟล์ข้อความที่ไม่มีการยุติ\nในขณะที่เป็นเรื่องปกติจะถือว่าใช้งานไม่ได้ภายใต้ POSIX หากคุณสามารถเชื่อใจการติดตามที่\nคุณไม่ต้องการ|| [[ -n $line ]]ในwhileลูป)

เพิ่มเติมที่คำถามที่พบบ่อยทุบตี


13

หากคุณไม่ต้องการให้ตัวละครขึ้นบรรทัดใหม่ของคุณใช้ -

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

จากนั้นเรียกใช้สคริปต์ด้วยชื่อไฟล์เป็นพารามิเตอร์


4
#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done

7
คำตอบนี้ต้องการ caveats ที่กล่าวถึงในคำตอบของ mightypileและสามารถล้มเหลวได้ไม่ดีหากบรรทัดใด ๆ มีเชลล์อักขระเมตาอักขระ (เนื่องจากไม่มีเครื่องหมายอัญประกาศ "$ x")
Toby Speight

7
ฉันแปลกใจจริง ๆ ที่ผู้คนยังไม่ได้ตามปกติอย่าอ่านบรรทัดด้วย ...
Egor Hans

3

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

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

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

การจับคู่แบบวนรอบสำหรับสตริงย่อยจากนั้นอ่านชื่อ =คู่ค่าแยกส่วนด้านขวาของอักขระ=ตัวสุดท้ายปล่อยอัญประกาศแรกปล่อยอัญประกาศเดี่ยวสุดท้ายเรามีค่าใหม่ทั้งหมดที่จะใช้ที่อื่น


3
ในขณะที่คำตอบนั้นถูกต้องฉันเข้าใจว่ามันจบลงที่นี่ได้อย่างไร วิธีการที่สำคัญเหมือนกันกับคำตอบอื่น ๆ ที่เสนอ นอกจากนี้มันจะจมน้ำตายในตัวอย่าง FPS ของคุณ
Egor Hans

0

นี่จะมาช้ามาก แต่ด้วยความคิดที่ว่าอาจช่วยใครบางคนฉันกำลังเพิ่มคำตอบ นอกจากนี้อาจไม่ใช่วิธีที่ดีที่สุด headคำสั่งสามารถใช้กับ-nอาร์กิวเมนต์เพื่ออ่านn บรรทัดจากจุดเริ่มต้นของไฟล์และtailคำสั่งเช่นเดียวกันสามารถใช้ในการอ่านจากด้านล่าง ตอนนี้เพื่อดึงข้อมูลบรรทัดที่nจากไฟล์เรามุ่งหน้าไปยังบรรทัดที่ nไปป์ข้อมูลเพื่อตัดเฉพาะ 1 บรรทัดจากข้อมูลที่ถูกไพพ์

   TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 `
   echo $TOTAL_LINES       # To validate total lines in the file

   for (( i=1 ; i <= $TOTAL_LINES; i++ ))
   do
      LINE=`head -n$i $USER_FILE | tail -n1`
      echo $LINE
   done

1
อย่าทำอย่างนี้ วนลูปกับหมายเลขบรรทัดและการดึงข้อมูลแต่ละบรรทัดของแต่ละบุคคลโดยวิธีการsedหรือhead+ tailเป็นอย่างเหลือเชื่อที่ไม่มีประสิทธิภาพและแน่นอน begs คำถามที่ว่าทำไมคุณไม่เพียงแค่ใช้อย่างใดอย่างหนึ่งของการแก้ปัญหาอื่น ๆ ที่นี่ หากคุณต้องการทราบหมายเลขบรรทัดให้เพิ่มตัวนับในwhile read -rลูปของคุณหรือใช้nl -baเพื่อเพิ่มคำนำหน้าหมายเลขบรรทัดในแต่ละบรรทัดก่อนลูป
tripleee

-1

@ Peter: สิ่งนี้สามารถช่วยคุณได้

echo "Start!";for p in $(cat ./pep); do
echo $p
done

นี้จะส่งกลับผลลัพธ์

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL


3
คำตอบนี้คือการเอาชนะหลักการทั้งหมดที่กำหนดโดยคำตอบที่ดีข้างต้น!
codeforester

3
โปรดลบคำตอบนี้
dawg

3
ตอนนี้พวกคุณอย่าพูดเกินจริง คำตอบไม่ดี แต่ดูเหมือนว่าจะทำงานอย่างน้อยสำหรับกรณีใช้งานง่าย ตราบใดที่มีให้การเป็นคำตอบที่ไม่ดีไม่ได้มีอยู่
Egor Hans

3
@EgorHans ฉันไม่เห็นด้วยอย่างยิ่ง: จุดของคำตอบคือการสอนคนวิธีการเขียนซอฟต์แวร์ การสอนผู้คนให้ทำสิ่งต่าง ๆ ในแบบที่คุณรู้ว่าเป็นอันตรายต่อพวกเขาและผู้ที่ใช้ซอฟต์แวร์ของพวกเขา (การแนะนำบั๊ก / พฤติกรรมที่ไม่คาดคิด / ฯลฯ ) กำลังทำร้ายผู้อื่นอย่างรู้เท่าทัน คำตอบที่รู้กันว่าเป็นอันตรายนั้นไม่มี "สิทธิ์ที่จะมีอยู่" ในแหล่งข้อมูลการสอนที่ได้รับการดูแลเป็นอย่างดี (และการทำให้เป็นจริงนั้นคือสิ่งที่เราคนที่ออกเสียงและตั้งค่าสถานะควรทำที่นี่)
Charles Duffy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.