it-swarm.asia

تنفيذ حلقات خلال `ls` في برنامج bash Shell النصي

هل يوجد لدى أحد القوالب برنامج نصي Shell لفعل شيء باستخدام ls للحصول على قائمة بأسماء الدليل والتكرار خلال كل منها والقيام بشيء ما؟

أخطط للقيام ls -1d */ للحصول على قائمة بأسماء الدليل.

87
Daniel A. White

تم التعديل لعدم استخدام ls حيث يمكن أن تفعل الكرة الأرضية ، كما اقترح @ shawn-j-goff وغيرهم.

فقط استخدم حلقة for..do..done:

for f in *; do
  echo "File -> $f"
done

يمكنك استبدال * بـ *.txt أو أي كرة أرضية أخرى تعرض قائمة (من الملفات أو الدلائل أو أي شيء في هذا الشأن) ، الأمر الذي ينشئ قائمة ، على سبيل المثال ، $(cat filelist.txt) أو يستبدلها فعليًا بقائمة.

داخل حلقة do ، يمكنك فقط الرجوع إلى متغير الحلقة مع بادئة تسجيل الدولار (هكذا $f في المثال أعلاه). يمكنك echo ذلك أو القيام بأي شيء آخر تريده.

على سبيل المثال ، لإعادة تسمية جميع ملفات .xml في الدليل الحالي إلى .txt:

for x in *.xml; do 
  t=$(echo $x | sed 's/\.xml$/.txt/'); 
  mv $x $t && echo "moved $x -> $t"
done

أو الأفضل من ذلك ، إذا كنت تستخدم Bash ، فيمكنك استخدام توسعات معلمات Bash بدلاً من وضع قشرة فرعية:

for x in *.xml; do 
  t=${x%.xml}.txt
  mv $x $t && echo "moved $x -> $t"
done
100
CoverosGene

استخدام إخراج lsللحصول على أسماء ملفات فكرة سيئة . يمكن أن يؤدي إلى البرامج النصية المعطلة وحتى الخطرة. وذلك لأن اسم الملف يمكن أن يحتوي على أي حرف ما عدا / و nullname__character ، ولا يستخدم lsأيًا من تلك الأحرف كمحددات ، لذلك إذا كان اسم الملف له مسافة أو سطر جديد ، فأنت سوف تحصل على نتائج غير متوقعة.

هناك طريقتان جيدتان للتكرار على الملفات. هنا ، لقد استخدمت ببساطة echoلإظهار القيام بشيء باستخدام اسم الملف ؛ يمكنك استخدام أي شيء ، على الرغم من.

الأول هو استخدام ميزات شل الأصلية المتلألئة.

for dir in */; do
  echo "$dir"
done

توسع Shell */ إلى وسيطات منفصلة تقرأها حلقة for؛ حتى إذا كان هناك مسافة أو سطر جديد أو أي حرف غريب آخر في اسم الملف ، فسوف ترى forكل اسم كامل كوحدة ذرية ؛ انها لا تحليل القائمة بأي شكل من الأشكال.

إذا كنت ترغب في الانتقال بشكل متكرر إلى الدلائل الفرعية ، فلن يحدث ذلك إلا إذا كان لدى Shell بعض ميزات التخطي الممتدة (مثل bashname __'s globstarname__. إذا لم يكن لدى Shell هذه الميزات أو إذا كنت تريد التأكد من عمل البرنامج النصي الخاص بك في مجموعة متنوعة من الأنظمة ، فإن الخيار التالي هو استخدام findname__.

find . -type d -exec echo '{}' \;

هنا ، سيتصل الأمر findechoويمررها وسيطة لاسم الملف. يفعل هذا مرة واحدة لكل ملف يجد. كما في المثال السابق ، لا يوجد تحليل لقائمة أسماء الملفات ؛ بدلاً من ذلك ، يتم إرسال fileneame بالكامل كوسيطة.

يبدو بناء جملة الوسيطة -exec مضحكا بعض الشيء. يأخذ findالوسيطة الأولى بعد -exec ويتعامل مع ذلك كبرنامج ليتم تشغيله ، وكل وسيطة لاحقة ، يستغرق الأمر وسيطة لتمريرها إلى هذا البرنامج. هناك نوعان من الوسيطات الخاصة التي يحتاج -exec إلى رؤيتها. أول واحد هو {}؛ يتم استبدال هذه الوسيطة باسم ملف تنشئه الأجزاء السابقة من findname__. والثاني هو ; ، والذي يتيح لـ findمعرفة أن هذه هي نهاية قائمة الوسائط التي سيتم تمريرها إلى البرنامج ؛ يحتاج هذا إلى findلأنه يمكنك المتابعة مع المزيد من الوسائط المخصصة لـ findوليس مخصصة للبرنامج exec'd. سبب \ هو أن Shell تتعامل أيضًا مع ; بشكل خاص - إنها تمثل نهاية الأمر ، لذلك نحن بحاجة إلى الهروب منه حتى تعطيه Shell كوسيطة إلى findبدلاً من استهلاكها لنفسها ؛ هناك طريقة أخرى لجعل شل لا تعاملها بشكل خاص ، وهي وضعها في علامات اقتباس: ';' تعمل تمامًا مثل \; لهذا الغرض.

64
Shawn J. Goff

بالنسبة للملفات التي تحتوي على مسافات ، سيتعين عليك التأكد من اقتباس المتغير مثل:

 for i in $(ls); do echo "$i"; done; 

أو يمكنك تغيير متغير بيئة فاصل حقل الإدخال (IFS):

 IFS=$'\n';for file in $(ls); do echo $i; done

أخيرًا ، بناءً على ما تفعله ، قد لا تحتاج حتى إلى:

 for i in *; do echo "$i"; done;
14
john

إذا كان لديك GNU Parallel http://www.gnu.org/software/parallel/ ، يمكنك القيام بذلك:

ls | parallel echo {} is in this dir

لإعادة تسمية الكل .txt إلى .xml:

ls *.txt | parallel mv {} {.}.xml

شاهد الفيديو التقديمي لـ GNU بالتوازي لمعرفة المزيد: http://www.youtube.com/watch؟v=OpaiGYxkSuQ

5
Ole Tange

فقط للإضافة إلى إجابة CoverosGene ، إليك طريقة لإدراج أسماء الدليل فقط:

for f in */; do
  echo "Directory -> $f"
done
4
n3o

لماذا لا تقم بتعيين IFS على سطر جديد ، ثم التقاط إخراج ls في صفيف؟ يجب أن يؤدي تعيين IFS إلى السطر الجديد إلى حل مشكلات الأحرف المضحكة في أسماء الملفات ؛ باستخدام ls يمكن أن يكون لطيفًا لأنه يحتوي على مرفق فرز مدمج.

(في الاختبار ، كنت أواجه مشكلة في تعيين IFS إلى \n لكن مع إعداده على مساحة عمل خلفية جديدة في السطر ، كما هو مقترح في مكان آخر هنا):

مثلا (بافتراض نمط البحث ls الذي تم تمريره في $1):

SAVEIFS=$IFS
IFS=$(echo -en "\n\b")

FILES=($(/bin/ls "$1"))

for AFILE in ${FILES[@]}
do
    ... do something with a file ...
done

IFS=$SAVEIFS

هذا مفيد بشكل خاص على OS X ، على سبيل المثال ، لالتقاط قائمة بالملفات التي تم فرزها حسب تاريخ الإنشاء (الأقدم إلى الأحدث) ، الأمر ls هو ls -t -U -r.

1
jetset