الحفاظ الرائدة المساحة البيضاء أثناء القراءة >> كتابة سطر الملف عن طريق خط في باش
-
22-07-2019 - |
سؤال
وأنا أحاول أن يتكرر خلال دليل من ملفات نصية والجمع بينهما في وثيقة واحدة. هذا وتعمل كبرى، ولكن ملفات نصية تحتوي على مقتطفات الشفرة، وجميع من التنسيق بلدي هو الحصول على انهار على اليسار. تجريد كل المسافات الرائدة على الخط.
#!/bin/sh
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
echo "Processing $f file..."
echo "">$OUTPUT
cat $f | while read line; do
echo "$line">>$OUTPUT
done
echo >>$OUTPUT
echo >>$OUTPUT
done
وأنا المسلم مستجد باش، ولكن بعد البحث العالية والمنخفضة لم أتمكن من العثور على حل مناسب. يبدو BASH يكره المساحة البيضاء مما يؤدي بشكل عام.
المحلول
وبدلا من:
cat $f | while read line; do
echo "$line">>$OUTPUT
done
هل هذا:
cat $f >>$OUTPUT
(إذا كان هناك سبب ما عليك القيام به الأشياء سطرا سطرا انه سيكون من المناسب إدراج ذلك في السؤال).
نصائح أخرى
وكما أشار آخرون، وذلك باستخدام القط أو AWK بدلا من حلقة للقراءة الصدى هو وسيلة أفضل بكثير للقيام بذلك - يتجنب مشكلة تقليم بيضاء (واثنين آخرين لم تكن قد عثر عليها)، يعمل بشكل أسرع، وعلى الأقل مع القط، هو رمز ببساطة أكثر نظافة. ومع ذلك، أود أن أغتنم طعنة في الحصول على حلقة للقراءة صدى للعمل مباشرة.
أولا، مشكلة تقليم بيضاء: الأمر قراءة الديكورات تلقائيا الرئيسي وراء بيضاء. وهذا يمكن أن تكون ثابتة عن طريق تغيير تعريفه من المسافات عن طريق تعيين متغير IFS إلى فارغة. أيضا، قراءة يفترض أن مائل في نهاية السطر يعني السطر التالي هو استمرار، ويجب أن تقسم إلى جانب هذا واحد؛ لإصلاح هذا، استخدم فيها (الخام) علم -r. المشكلة الثالثة هنا هي أن العديد من تطبيقات صدى تفسير أحرف في السلسلة (على سبيل المثال، فإنها قد تتحول \ ن إلى السطر الفعلي)؛ لإصلاح هذا، استخدم printf بدلا من ذلك. وأخيرا، فقط كقاعدة النظافة البرمجة العامة، يجب عدم استخدام القط عندما لا تحتاج في الواقع إلى؛ استخدام مدخلات إعادة توجيه بدلا من ذلك. مع هذه التغييرات، وحلقة داخلية تبدو مثل هذا:
while IFS='' read -r line; do
printf "%s\n" "$line">>$OUTPUT
done <$f
و... هناك أيضا بضعة مشاكل أخرى مع النصي المحيطة: الخط الذي يحاول تحديد FILES كقائمة من الملفات. نسيج المتاحة لديها يقتبس من حوله، وهذا يعني أنه لم يحصل سعت إلى قائمة الفعلية للملفات . أفضل طريقة للقيام بذلك هو استخدام صفيف:
FILES=(../best-practices/*.textile)
...
for f in "${FILES[@]}"
(ويجب أن تكون كافة تواجدات $ و في مزدوجة نقلت في حالة أي من أسماء ومسافات أو أحرف مضحك أخرى فيها - يجب أن تفعله حقا هذا مع $ OUTPUT كذلك، على الرغم منذ ذلك انها تعرف في البرنامج النصي انها في الواقع آمنة لمغادرة خارج).
وأخيرا، هناك echo "">$OUTPUT
بالقرب من أعلى للملفات حلقة تنته أن يحدث لمحو ملف الإخراج كل مرة من خلال (أي في النهاية، فإنه يحتوي فقط على الملف. نسيج الماضي)؛ هذا يحتاج إلى أن انتقلت إلى قبل الحلقة. أنا لست متأكدا مما إذا كان القصد هنا لوضع سطر فارغ واحد في بداية الملف، أو ثلاثة أسطر فارغة بين الملفات (واحدة في بداية واثنان في النهاية)، لذلك لست متأكدا بالضبط ما والبديل المناسب هو. على أي حال، وهنا ما يمكن أن تصل إليه بعد تحديد كل هذه المشاكل:
#!/bin/sh
OUTPUT="../best_practices.textile"
FILES=(../best-practices/*.textile)
: >"$OUTPUT"
for f in "${FILES[@]}"
do
echo "Processing $f file..."
echo >>"$OUTPUT"
while IFS='' read -r line; do
printf "%s\n" "$line">>"$OUTPUT"
done <"$f"
echo >>"$OUTPUT"
echo >>"$OUTPUT"
done
وهذا هو وسيلة مكلفة أكثر مما ينبغي الجمع بين الملفات.
cat ../best-practices/*.textile > ../best_practices.textile
وإذا كنت ترغب في إضافة فارغة (السطر الجديد) إلى كل ملف كما لسلسلة، واستخدام AWK
awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile
وOR
awk 'FNR==1{print ""}{print}' file* > out.txt
وهذا يسمح لك لنثر أسطر جديدة بين كل ملف الإدخال كما فعلت في النص الأصلي:
for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT
لاحظ أن $FILES
هو غير المدرجة لهذا العمل (وإلا أسطر إضافية تظهر مرة واحدة فقط في نهاية كل المخرجات)، ولكن يجب أن تكون نقلت $f
لحماية المساحات في أسماء الملفات، إذا كانت موجودة.
والجواب الصحيح، المنظمة البحرية الدولية، وhref="https://stackoverflow.com/a/22539038/828885"> ، يرد أدناه:
while IFS= read line; do
check=${line:0:1}
done < file.txt
ملحوظة أنها سوف تأخذ الرعاية من الحالات التي يتم إيصاله المدخلات من أمر آخر، وليس فقط من الملف الفعلي.
لاحظ أنه يمكنك أيضا تبسيط إعادة توجيه كما هو مبين أدناه.
#!/bin/bash
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
echo "Processing $f file..."
{
echo
while IFS= read line; do
echo "$line"
done < $f
echo
echo;
} > $OUTPUT
done