كيف يمكنني تنفيذ أي أمر بتحرير ملفه (الوسيطة) "في مكانه" باستخدام bash؟

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

  •  02-07-2019
  •  | 
  •  

سؤال

لدي ملف temp.txt، وأريد فرزه باستخدام ملف sort الأمر في باش.

أريد أن تحل النتائج التي تم فرزها محل الملف الأصلي.

هذا لا يعمل على سبيل المثال (أحصل على ملف فارغ):

sortx temp.txt > temp.txt

وهل يمكن عمل ذلك في سطر واحد دون اللجوء إلى النسخ إلى الملفات المؤقتة؟


يحرر:ال -o الخيار رائع جدًا لـ sort.إستعملت sort في سؤالي كمثال.أواجه نفس المشكلة مع الأوامر الأخرى:

uniq temp.txt > temp.txt.

هل هناك حل عام أفضل؟

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

المحلول

sort temp.txt -o temp.txt

نصائح أخرى

أ sort يحتاج إلى رؤية كل المدخلات قبل أن يتمكن من البدء في الإخراج.ولهذا السبب فإن sort يمكن للبرنامج أن يقدم بسهولة خيارًا لتعديل ملف في مكانه:

sort temp.txt -o temp.txt

على وجه التحديد، توثيق جنو sort يقول:

عادةً، يقرأ الفرز جميع المدخلات قبل فتح ملف الإخراج، بحيث يمكنك فرز الملف بأمان في مكانه باستخدام أوامر مثل sort -o F F و cat F | sort -o F.لكن، sort مع --merge (-m) يمكنه فتح ملف الإخراج قبل قراءة كل المدخلات، لذا فإن الأمر مثل cat F | sort -m -o F - G ليست آمنة حيث قد يبدأ النوع في الكتابة F قبل cat تم الانتهاء من قراءته.

بينما توثيق BSD sort يقول:

إذا كان ملف الإخراج هو أحد ملفات الإدخال، فقم بنسخه إلى ملف مؤقت قبل فرز وكتابة الإخراج إلى ملف الإخراج.

أوامر مثل uniq يمكنهم البدء في كتابة المخرجات قبل الانتهاء من قراءة المدخلات.لا تدعم هذه الأوامر عادةً التحرير الموضعي (وسيكون من الصعب عليها دعم هذه الميزة).

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

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

هنا، يقرأ جزء Perl الإخراج الكامل من uniq ثابت $_ ثم يقوم بالكتابة فوق الملف الأصلي بهذه البيانات.يمكنك أن تفعل الشيء نفسه في لغة البرمجة النصية التي تختارها، وربما حتى في Bash.لكن لاحظ أنه سيحتاج إلى ذاكرة كافية لتخزين الملف بأكمله، وهذا غير مستحسن عند العمل مع الملفات الكبيرة.

فيما يلي نهج أكثر عمومية، يعمل مع UNIQ، وفرز، وما إلى ذلك.

{ rm file && uniq > file; } < file

تعليق توبو على الإسفنج يضمن أن يكون إجابة في حد ذاته.

على الاقتباس من com.moreutils الصفحة الرئيسية:

ربما تكون أداة الأغراض العامة الأكثر استخدامًا حتى الآن هي الإسفنجة (1)، والتي تتيح لك القيام بأشياء مثل هذه:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

لكن، sponge يعاني من نفس المشكلة تعليقات ستيف جيسوب هنا. إذا كان أي من الأوامر في خط الأنابيب من قبل sponge إذا فشلت، فسيتم كتابة الملف الأصلي.

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

اه أوه، my-important-file ذهب.

تفضل، سطر واحد:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

من الناحية الفنية، لا يوجد نسخ إلى ملف مؤقت، ويجب أن يكون الأمر "mv" فوريًا.

انا احب ال sort file -o file الإجابة ولكن لا تريد كتابة نفس اسم الملف مرتين.

باستخدام باش توسيع التاريخ:

$ sort file -o !#^

يمسك الوسيط الأول للسطر الحالي عند الضغط عليه يدخل.

نوع فريد في المكان:

$ sort -u -o file !#$

الاستيلاء على الوسيط الأخير في السطر الحالي.

وقد ذكر كثيرون خيار.هنا هو جزء صفحة الرجل.

من صفحة الرجل:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

قد يكون هذا مقيدًا للغاية للذاكرة، ولكن يمكنك استخدام awk لتخزين البيانات المتوسطة في الذاكرة، ثم كتابتها مرة أخرى.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

بديل ل sponge مع أكثر شيوعا sed:

sed -ni r<(command file) file

يعمل لأي أمر (sort, uniq, tac, ، ...) ويستخدم المشهور جدًا sed-i خيار (تحرير الملفات في المكان).

تحذير: يحاول command file أولاً لأن تحرير الملفات في مكانها ليس آمنًا بطبيعته.


توضيح

أولا، أنت تقول sed عدم طباعة الأسطر (الأصلية) (-n خيار)، وبمساعدة sedr يأمر و bashاستبدال العملية, ، المحتوى الذي تم إنشاؤه بواسطة <(command file) سيتم حفظ الإخراج في المكان.


جعل الأمور أسهل

يمكنك لف هذا الحل في دالة:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

مثال

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file

استخدم الحجة --output= أو -o

لقد قمت للتو بالتجربة على FreeBSD:

sort temp.txt -otemp.txt

لإضافة uniq القدرة، ما هي الجوانب السلبية ل:

sort inputfile | uniq | sort -o inputfile

اقرأ عن المحرر غير التفاعلي، ex.

إذا كنت تصر على استخدام sort في البرنامج، يجب عليك استخدام ملف وسيط - لا أعتقد ذلك sort لديه خيار للفرز في الذاكرة.ستفشل أي خدعة أخرى باستخدام stdin/stdout ما لم تتمكن من ضمان أن حجم المخزن المؤقت لـ stdin الخاص بالفرز كبير بما يكفي ليناسب الملف بأكمله.

يحرر:عار علي. sort temp.txt -o temp.txt يعمل بشكل ممتاز.

حل آخر:

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