قم بتغطية بطانة واحدة لإضافتها إلى الملف

StackOverflow https://stackoverflow.com/questions/54365

  •  09-06-2019
  •  | 
  •  

سؤال

هذا على الأرجح حل معقد.

أنا أبحث عن عامل تشغيل بسيط مثل ">>"، لكن للتعليق المسبق.

وأخشى أنه غير موجود.سأضطر إلى القيام بشيء مثل

 mv myfile tmp
 cat myheader tmp > myfile

أي شيء أكثر ذكاء؟

هل كانت مفيدة؟

المحلول

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

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

والآن، وبعد كل ذلك، كان الجواب:


إنشاء واصف ملف آخر للملف (exec 3<> yourfile) ومن ثم الكتابة إلى ذلك (>&3) يبدو أنه يتغلب على معضلة القراءة/الكتابة في نفس الملف.يعمل بالنسبة لي على ملفات 600K مع awk.لكن تجربة نفس الخدعة باستخدام "cat" تفشل.

تمرير الإضافة كمتغير إلى awk (-v TEXT="$text") يتغلب على مشكلة الاقتباسات الحرفية التي تمنع القيام بهذه الخدعة باستخدام 'sed'.

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

نصائح أخرى

لا يزال هذا يستخدم ملفًا مؤقتًا، ولكنه على الأقل موجود في سطر واحد:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

ائتمان: سحق:قم بإضافة نص / أسطر إلى ملف

echo '0a
your text here
.
w' | ed some_file

إد هو المحرر القياسي! http://www.gnu.org/fun/jokes/ed.msg.html

جون مي:طريقتك ليست مضمونة للعمل، ومن المحتمل أن تفشل إذا قمت بإضافة أكثر من 4096 بايت من الأشياء (على الأقل هذا ما يحدث مع gnu awk، لكنني أفترض أن التطبيقات الأخرى سيكون لها قيود مماثلة).لن يفشل في هذه الحالة فحسب، بل سيدخل في حلقة لا نهاية لها حيث سيقرأ مخرجاته الخاصة، مما يجعل الملف ينمو حتى يتم ملء كل المساحة المتوفرة.

جربه بنفسك:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(تحذير:قتله بعد فترة من الوقت أو أنه سوف يملأ نظام الملفات)

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

هذا غير ممكن بدون ملف مؤقت، ولكن إليك خطًا واحدًا

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

يمكنك استخدام أدوات أخرى مثل ed أو perl للقيام بذلك بدون ملفات مؤقتة.

ومن الجدير بالذكر أنه في كثير من الأحيان يكون فكره جيده لإنشاء الملف المؤقت بأمان باستخدام أداة مساعدة مثل com.mktemp, ، على الأقل إذا كان سيتم تنفيذ البرنامج النصي بامتيازات الجذر.يمكنك على سبيل المثال القيام بما يلي (مرة أخرى في bash):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )

إذا كنت بحاجة إلى ذلك على أجهزة الكمبيوتر التي تتحكم فيها، فقم بتثبيت الحزمة "moreutils" واستخدم "sponge".ثم يمكنك القيام بما يلي:

cat header myfile | sponge myfile

باستخدام bash heredoc يمكنك تجنب الحاجة إلى ملف tmp:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

يعمل هذا لأنه يتم تقييم $(cat myfile) عند تقييم البرنامج النصي bash، قبل تنفيذ القطة مع إعادة التوجيه.

على افتراض أن الملف الذي تريد تحريره هو my.txt

$cat my.txt    
this is the regular file

والملف الذي تريد إضافته مسبقًا هو header

$ cat header
this is the header

تأكد من وجود سطر فارغ نهائي في ملف الرأس.
الآن يمكنك إضافة ذلك مسبقًا

$cat header <(cat my.txt) > my.txt

ينتهي بك الأمر مع

$ cat my.txt
this is the header
this is the regular file

بقدر ما أعرف، هذا يعمل فقط في "باش".

عندما تبدأ في محاولة القيام بأشياء تصبح صعبة في برنامج شل النصي، أود أن أقترح بشدة النظر في إعادة كتابة البرنامج النصي بلغة برمجة نصية "مناسبة" (Python/Perl/Ruby/etc)

أما بالنسبة لإضافة سطر إلى ملف، فلا يمكن القيام بذلك عبر الأنابيب، كما هو الحال عندما تفعل أي شيء مثل cat blah.txt | grep something > blah.txt, ، فإنه يؤدي إلى إفراغ الملف عن غير قصد.هناك أمر فائدة صغير يسمى sponge يمكنك تثبيت (يمكنك القيام بذلك cat blah.txt | grep something | sponge blah.txt ويقوم بتخزين محتويات الملف مؤقتًا، ثم يكتبها في الملف).إنه مشابه للملف المؤقت ولكن ليس عليك القيام بذلك بشكل صريح.لكنني أود أن أقول إن هذا مطلب "أسوأ" من متطلبات بيرل على سبيل المثال.

قد تكون هناك طريقة للقيام بذلك عبر awk، أو ما شابه ذلك، ولكن إذا كان عليك استخدام برنامج shell-script، فأعتقد أن الملف المؤقت هو الطريقة الأسهل (/ فقط؟).

يحرر:هذا مكسور.يرى سلوك غريب عند إعداد ملف باستخدام القطة والمحملة

الحل البديل لمشكلة الكتابة الفوقية هو استخدام tee:

cat header main | tee main > /dev/null

كما يقترح دانيال فيلكوف، استخدم نقطة الإنطلاق.
بالنسبة لي، هذا حل ذكي وبسيط:

{ echo foo; cat bar; } | tee bar > /dev/null

الذي أستخدمه.يتيح لك هذا تحديد الترتيب والأحرف الإضافية وما إلى ذلك بالطريقة التي تريدها:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

ملاحظة:فقط أنها لا تعمل إذا كانت الملفات تحتوي على نص بخط مائل عكسي، لأنه يتم تفسيره على أنه أحرف هروب

في الغالب من أجل المتعة/لعبة الجولف، ولكن

ex -c '0r myheader|x' myfile

سوف يقوم بالمهمة، ولا توجد خطوط أنابيب أو عمليات إعادة توجيه.بالطبع، vi/ex ليس مخصصًا للاستخدام غير التفاعلي، لذلك سيومض vi لفترة وجيزة.

لماذا لا تستخدم الأمر ed ببساطة (كما اقترحه fluffle هنا)؟

يقرأ ed الملف بأكمله في الذاكرة ويقوم تلقائيًا بتحرير الملف في مكانه!

لذا، إذا لم يكن ملفك كبيرًا جدًا ...

# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed

prepend() {
   printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}

echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile

هناك حل آخر يتمثل في استخدام مقابض الملفات المفتوحة كما اقترح Jürgen Hötzel في إعادة توجيه الإخراج من sed 's/c/d/' myFile إلى myFile

echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt

كل هذا يمكن وضعه في سطر واحد بالطبع.

متغير في حل cb0 لـ "لا يوجد ملف مؤقت" لإرفاق النص الثابت مسبقًا:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

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

ملحوظة:أحب هذا الحل.ومع ذلك، في جهاز Mac الخاص بي، يتم فقدان الملف الأصلي (اعتقد أنه لا ينبغي ذلك ولكنه يحدث).يمكن إصلاح ذلك عن طريق كتابة الحل الخاص بك على النحو التالي:صدى "النص إلى إعداد" | Cat - file_to_be_modified | cat> tmp_file ؛mv tmp_file file_to_be_modified

وإليكم ما اكتشفته:

echo -e "header \n$(cat file)" >file
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile

تحذير:يحتاج هذا إلى المزيد من العمل لتلبية احتياجات OP.

يجب أن تكون هناك طريقة لجعل منهج @shixilun يعمل على الرغم من مخاوفه.يجب أن يكون هناك أمر bash للهروب من المسافة البيضاء عند قراءة ملف في سلسلة بديلة sed (على سبيل المثال.استبدل أحرف السطر الجديد بـ " ".أوامر شل vis و cat يمكنه التعامل مع الأحرف غير القابلة للطباعة، ولكن ليس المسافات البيضاء، لذلك لن يحل هذا مشكلة OP:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

يفشل بسبب الأسطر الجديدة الأولية في البرنامج النصي البديل، والتي يجب أن تكون مسبوقة بحرف استمرار السطر () وربما متبوعًا بـ &، لإبقاء Shell وsed سعيدين، مثل هذه الإجابة SO

sed لديه حد لحجم يبلغ 40 كيلو بايت لأوامر البحث والاستبدال غير العامة (لا يوجد زائدة /g بعد النمط) لذلك من المحتمل أن يتجنب مشاكل تجاوز المخزن المؤقت المخيفة التي حذر منها مجهول.

sed -i -e "1s/^/new first line\n/" old_file.txt

مع $(الأمر) يمكنك كتابة مخرجات الأمر في متغير.لذلك قمت بذلك في ثلاثة أوامر في سطر واحد وبدون ملف مؤقت.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile

إذا كان لديك ملف كبير (بضع مئات من الكيلوبايت في حالتي) ويمكنك الوصول إلى لغة بايثون، فهذا أسرع بكثير من cat حلول الأنابيب:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

الحل مع printf:

new_line='the line you want to add'
target_file='/file you/want to/write to'

printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"

يمكنك أيضًا القيام بما يلي:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

ولكن في هذه الحالة عليك التأكد من عدم وجود أي شيء % في أي مكان، بما في ذلك محتويات الملف الهدف، حيث يمكن تفسير ذلك وإفساد نتائجك.

يمكنك استخدام سطر أوامر بيرل:

perl -i -0777 -pe 's/^/my_header/' tmp

حيث -سأقوم بإنشاء بديل مضمّن للملف وسيقوم -0777 بتشويش الملف بأكمله وجعل ^ مطابقة فقط في البداية.-pe سوف يطبع كافة الأسطر

أو إذا كان my_header ملفًا:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

حيث سيسمح /e بتقييم الكود في الاستبدال.

current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

حيث "my_file" هو الملف الذي سيتم إضافة "my_string" إليه في البداية.

أنا تروق @فلفل إد يقترب الأفضل.بعد كل شيء، فإن مفاتيح تبديل سطر الأوامر لأي أداة مقابل أوامر المحرر المكتوبة هي في الأساس نفس الشيء هنا؛عدم رؤية حل المحرر النصي "النظافة" أقل أو غير ذلك.

وهنا بلدي بطانة واحدة إلحاق .git/hooks/prepare-commit-msg لإدراج في الريبو .gitmessage ملف لارتكاب الرسائل:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

مثال .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

انا افعل 1r بدلاً من 0r, لأن ذلك سيترك سطرًا فارغًا جاهزًا للكتابة أعلى الملف من القالب الأصلي.لا تضع سطراً فارغاً فوق رسالتك .gitmessage ثم سينتهي بك الأمر مع سطرين فارغين. -s يمنع إخراج معلومات التشخيص من ed.

فيما يتعلق بالمرور بهذا ، أنا اكتشف أنه من الجيد أيضًا أن يكون لدى هواة vim:

[core]
        editor = vim -c ':normal gg'

المتغيرات، فتو؟

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list

أعتقد أن هذا هو أنظف نسخة من ed:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

كوظيفة:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile

إذا كنت تقوم بالبرمجة النصية باستخدام BASH، في الواقع، يمكنك فقط إصدار ما يلي:

cat - yourfile  /tmp/out && mv /tmp/out yourfile

هذا في الواقع موجود في المثال المعقد الذي نشرته بنفسك في سؤالك.

IMHO لا يوجد حل Shell (ولن يكون أبدًا) من شأنه أن يعمل بشكل متسق وموثوق مهما كان حجم الملفين myheader و myfile.والسبب هو أنك إذا كنت تريد القيام بذلك دون التكرار إلى ملف مؤقت (وبدون السماح للصدفة بالتكرار بصمت إلى ملف مؤقت، على سبيل المثال.من خلال بنيات مثل exec 3<>myfile, ، الأنابيب إلى tee, ، إلخ.

الحل "الحقيقي" الذي تبحث عنه يحتاج إلى التلاعب بنظام الملفات، وبالتالي فهو غير متوفر في مساحة المستخدمين وسيعتمد على النظام الأساسي:أنت تطلب تعديل مؤشر نظام الملفات المستخدم بواسطة myfile إلى القيمة الحالية لمؤشر نظام الملفات لـ myheader واستبداله في نظام الملفات EOF ل myheader مع رابط متسلسل إلى عنوان نظام الملفات الحالي المشار إليه myfile.هذا ليس بالأمر التافه ومن الواضح أنه لا يمكن القيام به بواسطة مستخدم غير متميز، وربما ليس بواسطة المستخدم المتميز أيضًا ...العب مع inodes، وما إلى ذلك.

يمكنك تزييف هذا بشكل أو بآخر باستخدام أجهزة الحلقة.انظر على سبيل المثال هذا الموضوع SO.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top