سؤال التكرار من خلال النتائج `ls` في البرنامج النصي bash shell


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

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


73
2017-08-28 17:03


الأصل




الأجوبة:


تم تعديله بعدم استخدام 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

77
2017-08-28 17:09



ماذا لو كان اسم الملف يحتوي على مساحة فيه؟ - Daniel A. White
لسوء الحظ ، كما قال دانيال ، فإن الكود الموجود في هذه الإجابة سوف ينكسر إذا كان أي من الملفات أو المجلدات يحتوي على مسافة أو سطر جديد في أسمائهم. يظهر سوء استخدام شائع جدا ل for الحلقات ، وكسر نموذجي لمحاولة تحليل ناتج ls. @ DanielA.White ، أنت ربما فكر في عدم قبول هذه الإجابة إذا لم تكن مفيدة (أو من المحتمل أن تكون مضللة) ، لأنك كما قلت ، أنت تتصرف في الأدلة. يجب أن توفر إجابة Shawn J. Goff حلاً أكثر قوة وعملية لمشكلتك. - slhck
-∞ لا يجب أن تحلل ls انتاج، لا ينبغي عليك قراءة المخرجات في for عقدةيجب عليك استخدام $() بدلا من `` و استخدام المزيد من الاسعار. أنا متأكد من أنCoverosGene تعني جيدًا ، ولكن هذا أمر فظيع. - l0b0
بديل أفضل إذا كنت تريد حقا أن تستخدم ls هو ls -1 | while read line; do stuff; done. على الأقل هذا لن يكسر المساحات البيضاء. - Emmanuel Joubaud
مع mv -v لا تحتاج echo "moved $x -> $t" - DmitrySandalov


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

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

الأول هو استخدام ميزات globeing الأصلي shell.

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

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

إذا كنت ترغب في الانتقال بشكل متكرر إلى أدلة فرعية ، فإن هذا لن يحدث إلا إذا كان لدى shell الخاص بك بعض ميزات globbing الممتدة (مثل bashالصورة globstar. إذا لم يكن لديك shell هذه الميزات ، أو إذا كنت تريد التأكد من أن البرنامج النصي الخاص بك يعمل على مجموعة متنوعة من الأنظمة ، فإن الخيار التالي هو استخدام find.

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

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

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


63
2018-04-29 22:16



يعد إجراء 1+ هذا بالتأكيد هو الطريق الذي يجب اتباعه عندما تحتاج إلى إنشاء قائمة ملفات واستخدامها في أمر. find -exec محدود فقط من خلال القدرة على تشغيل أوامر واحدة. مع الحلقة ، يمكنك الانبوب لمحتوى قلبك. - MaQleod
+1. أتمنى أن يستخدم المزيد من الناس find. هناك السحر الذي يمكن القيام به ، وحتى القيود المتصورة من -exec يمكن أن يعمل حولها. ال -print0 الخيار هو أيضا قيمة للاستخدام مع xargs. - ghoti


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

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

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

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

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

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

7
2017-08-28 17:26



استخدام لطيف من subshell في المثال الأول - Jeremy L
لماذا يحتاج IFS إلى $ بعد المهمة وقبل الشخصية الجديدة؟ - Andy Ibanez
يمكن أن تحتوي أسماء الملفات أيضًا على خطوط جديدة. كسر على \n غير كافية. من المستحسن أبدًا أن ننصح بتحليل مخرجات ls. - ghoti


إذا كان لديك جنو متوازي http://www.gnu.org/software/parallel/ مثبت يمكنك القيام بذلك:

ls | parallel echo {} is in this dir

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

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

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


5
2017-08-22 20:02





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

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

4
2018-02-11 14:36





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

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

مثلا (على افتراض المطلوب 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
2018-05-10 22:36



يمكن أن تحتوي أسماء الملفات أيضًا على خطوط جديدة ، وغالبًا ما يتم ذلك عندما يُسمح للمستخدمين بتسمية ملفاتهم الخاصة. كسر على \n غير كافية. تستخدم الحلول الصالحة الوحيدة لحلقة مع توسيع اسم المسار أو find. - ghoti
والطريقة الوحيدة الموثوقة لنقل قائمة أسماء الملفات هي فصلها بحرف NUL ، حيث أن هذا هو الوحيد الذي لا يحتويه بالتأكيد مسار الملف. - glglgl


هذه هي الطريقة التي أقوم بها ، ولكن ربما هناك طرق أكثر فعالية.

ls > filelist.txt

while read filename; do echo filename: "$filename"; done < filelist.txt

-3
2017-08-28 17:28



العصا على الأنابيب في مكان الملف:>ls | while read i; do echo filename: $i; done - Jeremy L
رائع. يجب أن أقول أنه يمكنك أيضًا استخدام $ EDITOR filelist.txt بين الأمرين. الكثير من الأشياء التي يمكنك القيام بها في محرر أسهل من سطر الأوامر. غير مناسب لهذا السؤال ، رغم ذلك. - TREE
الحل الخاص بك لا تعالج المشكلة على الإطلاق مع أسماء الملفات التي تحتوي على خطوط جديدة وأشياء أخرى خيالية. - glglgl