linux - التكرار من خلال محتوى ملف في باش




bash loops (8)

كيف يمكنني تكرار كل سطر من ملف نصي باستخدام 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 output output:

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 ([email protected]) (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

Peter: هذا يمكن أن يعمل من أجلك

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

هذا سيعيد الناتج

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

إحدى الطرق للقيام بذلك هي:

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

بشكل استثنائي ، إذا كان نص الحلقة قد يقرأ من الإدخال القياسي ، فيمكنك فتح الملف باستخدام واصف ملف مختلف:

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

هنا ، 10 هو مجرد رقم عشوائي (يختلف عن 0 ، 1 ، 2).



بعض الأشياء القليلة التي لا تغطيها إجابات أخرى:

القراءة من ملف محدد

# ':' 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 ... command ... | while read -r line; do ... command ... | while read -r line; do ... لأن حلقة while تعمل هنا في shell الحالي بدلاً من subshell كما في حالة الأخير. راجع المنشور المرتبط لا يتم تذكر المتغير المعدل داخل حلقة ما .

القراءة من مدخلات محددة فارغة ، على سبيل المثال 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

قراءة ملف كامل في صفيف (إصدارات 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

الوظائف ذات الصلة:


هذا ليس أفضل من الإجابات الأخرى ، ولكنه طريقة أخرى لإنجاز المهمة في ملف بدون مسافات (انظر التعليقات). أجد أنني في كثير من الأحيان بحاجة إلى أحرف واحدة للتنقل عبر القوائم في ملفات نصية دون الخطوة الإضافية لاستخدام ملفات البرامج النصية المنفصلة.

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

أو إذا كنت تنوي استخدام هذا كمحرر دفق (تعلم sed) يمكنك تفريغ الإخراج إلى ملف آخر على النحو التالي.

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

هذا فقط يخبر shell لتقسيم على خطوط جديدة فقط ، لا مسافات ، ثم إرجاع البيئة مرة أخرى إلى ما كان عليه سابقا. في هذه المرحلة ، قد ترغب في وضع كل ذلك في نص برمجي بدلاً من الضغط عليه في سطر واحد ، على الرغم من ذلك.

حظا سعيدا!


هنا هو مثال حياتي الحقيقي كيفية تكرار خطوط من إخراج برنامج آخر ، والتحقق من وجود سلاسل فرعية ، وإسقاط علامات اقتباس مزدوجة من المتغير ، واستخدام هذا المتغير خارج الحلقة. أعتقد أن الكثير يسأل هذه الأسئلة عاجلاً أم آجلاً.

##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

متغيرات Declare خارج الحلقة ، تعيين قيمة واستخدامها خارج الحلقة يتطلب القيام به <<< "$ (...)" بناء الجملة. يجب تشغيل التطبيق في سياق وحدة التحكم الحالية. علامات الاقتباس حول الأمر يحتفظ بخطوط جديدة من دفق الإخراج.

تقوم حلقة التكرار للصفوف الفرعية بقراءة اسم الزوج = القيمة ، تقسيم جزء الجانب الأيمن من آخر = حرف ، تسقط أول اقتباس ، تسقط آخر اقتباس ، لدينا قيمة نظيفة لاستخدامها في مكان آخر.


#!/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

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




io