আমি কীভাবে কোনও ফাইলগুলিতে স্ট্রিং প্রতিস্থাপন করতে পারি?


751

নির্দিষ্ট অনুসন্ধানের মানদণ্ডের ভিত্তিতে ফাইলগুলিতে স্ট্রিং প্রতিস্থাপন করা খুব সাধারণ কাজ। আমি কিভাবে করতে পারি

  • বর্তমান ডিরেক্টরিতে সমস্ত ফাইলের fooসাথে স্ট্রিং প্রতিস্থাপন করবেন bar?
  • সাব ডিরেক্টরি জন্য একই পুনরাবৃত্তভাবে কি না?
  • ফাইলের নামটি অন্য স্ট্রিংয়ের সাথে মেলে তবেই প্রতিস্থাপন করবেন?
  • স্ট্রিং নির্দিষ্ট প্রসঙ্গে পাওয়া গেলেই প্রতিস্থাপন করবেন?
  • স্ট্রিংটি যদি একটি নির্দিষ্ট লাইন সংখ্যায় থাকে তবে প্রতিস্থাপন করবেন?
  • একই প্রতিস্থাপনের সাথে একাধিক স্ট্রিং প্রতিস্থাপন করুন
  • বিভিন্ন প্রতিস্থাপনের সাথে একাধিক স্ট্রিং প্রতিস্থাপন করুন

2
এটি এই বিষয়ে একটি প্রমিত প্রশ্নোত্তর হওয়ার উদ্দেশ্যে করা হয়েছে (এই মেটা আলোচনাটি দেখুন ), দয়া করে নীচে আমার উত্তরটি সম্পাদনা করতে বা আপনার নিজের যুক্ত করতে নির্দ্বিধায়।
টেরডন

উত্তর:


1009

1. বর্তমান ডিরেক্টরিতে সমস্ত ফাইলগুলিতে একটি স্ট্রিংয়ের সমস্ত উপস্থিতি প্রতিস্থাপন:

এগুলি এমন ক্ষেত্রে হয় যেখানে আপনি জানেন যে ডিরেক্টরিতে কেবল নিয়মিত ফাইল রয়েছে এবং আপনি সমস্ত গোপনীয় ফাইলগুলি প্রক্রিয়া করতে চান। যদি এটি না হয় তবে অ্যাপ্রোচগুলি 2 এ ব্যবহার করুন।

sedএই উত্তরের সমস্ত সমাধান জিএনইউ ধরে নেয় sed। যদি ফ্রিবিএসডি বা ওএস / এক্স ব্যবহার করে তবে এর -iসাথে প্রতিস্থাপন করুন -i ''। এছাড়াও নোট করুন যে -iকোনও সংস্করণের সাথে স্যুইচ ব্যবহারের sedকিছু নির্দিষ্ট ফাইল সিস্টেম সুরক্ষা জড়িত রয়েছে এবং যে কোনও স্ক্রিপ্টে আপনি যে কোনও উপায়ে বিতরণ করার পরিকল্পনা করছেন তা অনিবার্য।

  • এই ডিরেক্টরিতে কেবল পুনরাবৃত্তিযোগ্য ফাইলগুলি:

    sed -i -- 's/foo/bar/g' *
    perl -i -pe 's/foo/bar/g' ./* 

    ( perlফাইলের নাম |বা স্পেসে শেষ হওয়াতে এটি ব্যর্থ হবে ) )।

  • এটি এবং সমস্ত উপ-ডিরেক্টরিতে পুনরাবৃত্ত, নিয়মিত ফাইল ( লুকানো রয়েছে সহ )

    find . -type f -exec sed -i 's/foo/bar/g' {} +

    যদি আপনি zsh ব্যবহার করেন:

    sed -i -- 's/foo/bar/g' **/*(D.)

    (তালিকাটি খুব বড় হলে ব্যর্থ হতে পারে, zargsপ্রায় কাজ করতে দেখুন )।

    বাশ নিয়মিত ফাইলগুলির জন্য সরাসরি যাচাই করতে পারে না, একটি লুপ প্রয়োজন (বিশ্বব্যাপী বিকল্পগুলি সেট করা বন্ধনীগুলি এড়ানোর জন্য):

    ( shopt -s globstar dotglob;
        for file in **; do
            if [[ -f $file ]] && [[ -w $file ]]; then
                sed -i -- 's/foo/bar/g' "$file"
            fi
        done
    )

    যখন ফাইলগুলি প্রকৃত ফাইল (-f) হয় তখন তারা নির্বাচিত হয় এবং সেগুলি (-w) হয়।

২. কেবলমাত্র যদি ফাইলের নামটি অন্য স্ট্রিংয়ের সাথে মেলে বা একটি নির্দিষ্ট এক্সটেনশন থাকে / নির্দিষ্ট ধরণের হয় তবেই প্রতিস্থাপন করুন:

  • এই ডিরেক্টরিতে কেবল পুনরাবৃত্তিযোগ্য ফাইলগুলি:

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
  • এটি এবং সমস্ত উপ-ডিরেক্টরিতে পুনরাবৃত্ত, নিয়মিত ফাইল

    find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +

    আপনি যদি ব্যাশ ব্যবহার করছেন (ধনুর্বন্ধনীগুলি বিশ্বব্যাপী বিকল্পগুলি সেট করা এড়ানোর জন্য):

    ( shopt -s globstar dotglob
        sed -i -- 's/foo/bar/g' **baz*
        sed -i -- 's/foo/bar/g' **.baz
    )

    যদি আপনি zsh ব্যবহার করেন:

    sed -i -- 's/foo/bar/g' **/*baz*(D.)
    sed -i -- 's/foo/bar/g' **/*.baz(D.)

    --তোলে বলতে sedযে আর পতাকার কমান্ড লাইনে দেওয়া হবে। শুরু হওয়া ফাইল নামের বিরুদ্ধে রক্ষা করতে এটি দরকারী -

  • যদি কোনও ফাইল নির্দিষ্ট ধরণের থাকে, উদাহরণস্বরূপ, এক্সিকিউটেবল ( man findআরও বিকল্পের জন্য দেখুন):

    find . -type f -executable -exec sed -i 's/foo/bar/g' {} +

    zsh:

    sed -i -- 's/foo/bar/g' **/*(D*)

3. স্ট্রিংটি নির্দিষ্ট প্রসঙ্গে পাওয়া গেলে কেবল প্রতিস্থাপন করুন

  • প্রতিস্থাপন fooসঙ্গে barথাকলে তবেই হয় bazএকই লাইনে পরবর্তী:

    sed -i 's/foo\(.*baz\)/bar\1/' file

    ইন sed, \( \)প্রথম বন্ধনীর মধ্যে যা আছে তা সংরক্ষণ করে এবং তারপরে আপনি এটি অ্যাক্সেস করতে পারেন \1। এই থিমের বিভিন্ন প্রকরণ রয়েছে, এই জাতীয় নিয়মিত অভিব্যক্তি সম্পর্কে আরও জানতে, এখানে দেখুন

  • প্রতিস্থাপন fooসঙ্গে barশুধুমাত্র যদি fooইনপুট ফাইলের 3d কলাম (ক্ষেত্র) পাওয়া যায় (হোয়াইটস্পেস বিভাজিত ক্ষেত্র অভিমানী):

    gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file

    ( gawk4.1.0 বা আরও নতুন প্রয়োজন)।

  • ভিন্ন ক্ষেত্রের জন্য কেবল আগ্রহের ক্ষেত্রের সংখ্যা $Nকোথায় ব্যবহার করুন N। ভিন্ন ক্ষেত্রের বিভাজনের জন্য ( :এই উদাহরণে) ব্যবহার করুন:

    gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file

    ব্যবহার করে আরেকটি সমাধান perl:

    perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@F\n"' foo 

    দ্রষ্টব্য: awkএবং উভয় perlসমাধানই ফাইলের ব্যবধানকে প্রভাবিত করবে (শীর্ষস্থানীয় এবং পিছনের ফাঁকা স্থানগুলি সরিয়ে ফেলবে, এবং ফাঁকাগুলির ক্রমগুলিকে সেই লাইনে থাকা একটি লাইনের অক্ষরে রূপান্তর করবে)। একটি ভিন্ন ক্ষেত্রের জন্য, ব্যবহার $F[N-1]যেখানে Nক্ষেত্র সংখ্যা যদি আপনি চান এবং একটি ভিন্ন ক্ষেত্র বিভাজক ব্যবহারের জন্য (হয় $"=":"সেট আউটপুট ক্ষেত্র বিভাজক থেকে :):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • প্রতিস্থাপন fooসঙ্গে barশুধুমাত্র 4 র্থ লাইনে:

    sed -i '4s/foo/bar/g' file
    gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
    perl -i -pe 's/foo/bar/g if $.==4' file

4. একাধিক প্রতিস্থাপন অপারেশন: বিভিন্ন স্ট্রিং সঙ্গে প্রতিস্থাপন

  • আপনি sedআদেশগুলি একত্রিত করতে পারেন :

    sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file

    সচেতন, যাতে বিষয়গুলো (হতে sed 's/foo/bar/g; s/bar/baz/g'প্রতিস্থাপন করে দেবে fooসঙ্গেbaz )।

  • বা পার্ল কমান্ড

    perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
  • আপনার যদি প্রচুর পরিমাণে নিদর্শন থাকে তবে sedস্ক্রিপ্ট ফাইলে আপনার নিদর্শন এবং তাদের প্রতিস্থাপনগুলি সংরক্ষণ করা আরও সহজ :

    #! /usr/bin/sed -f
    s/foo/bar/g
    s/baz/zab/g
  • অথবা, যদি আপনার উপরের জন্য খুব বেশি প্যাটার্ন জোড়া সম্ভব হয় তবে আপনি কোনও ফাইল থেকে প্যাটার্ন জোড়গুলি পড়তে পারেন (দুটি লাইনের সাথে পৃথক পৃথক নিদর্শন, $ প্যাটার্ন এবং $ প্রতিস্থাপন):

    while read -r pattern replacement; do   
        sed -i "s/$pattern/$replacement/" file
    done < patterns.txt
  • নিদর্শনগুলি এবং বৃহত ডেটা ফাইলগুলির দীর্ঘ তালিকাগুলির জন্য এটি বেশ ধীর হবে তাই আপনি প্যাটার্নগুলি পড়তে এবং sedতার পরিবর্তে এগুলি থেকে একটি স্ক্রিপ্ট তৈরি করতে চাইতে পারেন । নিম্নলিখিতটি ধরে নিয়েছে একটি <স্পেস> ডিলিমিটার ম্যাচে <স্পেস> প্রতিস্থাপন জোড়গুলির একটি তালিকা পৃথক করে ফাইলটিতে patterns.txt: প্রতি-লাইনটি প্রতি-লাইনে ঘটে :

    sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <patterns.txt |
    sed -f- ./editfile >outfile

    উপরের ফর্ম্যাটটি মূলত স্বেচ্ছাসেবী এবং উদাহরণস্বরূপ, কোনও ম্যাচ বা প্রতিস্থাপনের মধ্যে একটি <স্পেস> এর জন্য অনুমতি দেয় না । পদ্ধতিটি যদিও খুব সাধারণ: মূলত, আপনি যদি কোনও আউটপুট স্ট্রিম তৈরি করতে পারেন যা কোনও স্ক্রিপ্টের মতো লাগে তবে আপনি সেই স্ট্রিমটিকে স্ক্রিপ্ট হিসাবে স্ক্রিপ্ট ফাইল হিসাবে উল্লেখ করে উত্স করতে পারেনsedsedsed- stdin।

  • আপনি অনুরূপ ফ্যাশনে একাধিক স্ক্রিপ্ট একত্রিত করতে এবং একত্রীকরণ করতে পারেন:

    SOME_PIPELINE |
    sed -e'#some expression script'  \
        -f./script_file -f-          \
        -e'#more inline expressions' \
    ./actual_edit_file >./outfile

    একটি পসিক্স sedসমস্ত স্ক্রিপ্টগুলি কমান্ড-লাইনে প্রদর্শিত ক্রমের সাথে একত্রে সংযুক্ত করে। এগুলির কোনওটিরই শেষের দরকার নেই\n কোনওটিরই ewline এ ।

  • grep একইভাবে কাজ করতে পারেন:

    sed -e'#generate a pattern list' <in |
    grep -f- ./grepped_file
  • নিদর্শন হিসাবে স্থির-স্ট্রিংয়ের সাথে কাজ করার সময়, নিয়মিত এক্সপ্রেশন মেটাচ্যাকার্টারগুলি এড়ানো ভাল অনুশীলন । আপনি এটি বরং সহজেই এটি করতে পারেন:

    sed 's/[]$&^*\./[]/\\&/g
         s| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|
    ' <patterns.txt |
    sed -f- ./editfile >outfile

5. একাধিক প্রতিস্থাপন অপারেশন: একই স্ট্রিং সঙ্গে একাধিক নিদর্শন প্রতিস্থাপন

  • কোন প্রতিস্থাপন foo, barবা bazসঙ্গেfoobar

    sed -Ei 's/foo|bar|baz/foobar/g' file
  • অথবা

    perl -i -pe 's/foo|bar|baz/foobar/g' file

2
@ স্টাফেন চ্যাজেলাস সম্পাদনার জন্য ধন্যবাদ, এটি বেশ কয়েকটি জিনিস স্থির করেছে। তবে দয়া করে বাশের সাথে প্রাসঙ্গিক তথ্য মুছে ফেলবেন না। সবাই ব্যবহার করে না zsh। সমস্ত উপায়ে zshতথ্য যোগ করুন তবে ব্যাশ স্টাফগুলি সরানোর কোনও কারণ নেই। এছাড়াও, আমি জানি যে পাঠ্য প্রক্রিয়াজাতকরণের জন্য শেলটি ব্যবহার করা আদর্শ নয় তবে এমন ক্ষেত্রে রয়েছে যেখানে এটির প্রয়োজন হয়। আমি আমার মূল স্ক্রিপ্টের আরও ভাল সংস্করণে সম্পাদনা করেছি sedযা শেল লুপটি পার্স করার পরিবর্তে একটি স্ক্রিপ্ট তৈরি করবে । উদাহরণস্বরূপ আপনার কাছে কয়েক শতাধিক প্যাটার্ন থাকলে এটি কার্যকর হতে পারে।
টেরডন

2
@ ইটারডন, আপনার বাশটি ভুল। 4.3 এর আগে ব্যাশ নামার সময় সিঙ্কলিঙ্কগুলি অনুসরণ করবে। এছাড়াও (.)গ্যাশব্বিং কোয়ালিফায়ারের জন্য বাশের কোনও সমমান নেই তাই এখানে ব্যবহার করা যাবে না। (আপনি কিছু মিস করছেন - পাশাপাশি)। লুপটির জন্য ভুল (নিখোঁজ -আর) এবং এর অর্থ ফাইলগুলিতে বেশ কয়েকটি পাস করা এবং সেড স্ক্রিপ্টের সাহায্যে কোনও লাভ নেই।
স্টাফেন চেজেলাস

7
@terdon বিকল্প কমান্ডের --পরে sed -iএবং তার আগে কী বোঝায়?
গিফিক

5
@ জিোক এটি পজিক্স জিনিস। এটি বিকল্পগুলির সমাপ্তি নির্দেশ করে এবং শুরু করে আর্গুমেন্টগুলি পাস করতে দেয় -। এটি ব্যবহার করে এটি নিশ্চিত করে যে কমান্ডগুলি এই জাতীয় নামের মতো ফাইলগুলিতে কাজ করবে -foo। এটি ছাড়া, -fএকটি বিকল্প হিসাবে পার্স করা হবে।
টেরডন

1
গিট সংগ্রহস্থলগুলিতে কয়েকটি পুনরাবৃত্ত কমান্ড কার্যকর করতে খুব সাবধান হন। উদাহরণস্বরূপ, এই উত্তরের বিভাগ 1 এ প্রদত্ত সমাধানগুলি প্রকৃতপক্ষে কোনও .gitডিরেক্টরিতে অভ্যন্তরীণ গিট ফাইলগুলিকে সংশোধন করবে এবং আপনার চেকআউটটি আসলে বিশৃঙ্খলা করবে। নাম দ্বারা নির্দিষ্ট ডিরেক্টরিতে / পরিচালনা করা ভাল।
পিস্তোস

75

একটি ভাল পিএল acement লিনাক্স টুল rpl , যেটি আদতে, ডেবিয়ান প্রকল্পের জন্য লেখা হয়েছিল তাই এটি পাওয়া যায় apt-get install rplকোন ডেবিয়ান উদ্ভূত ডিস্ট্রো, এবং অন্যদের জন্য হতে পারে, কিন্তু অন্যথায় আপনি ডাউনলোড করতে পারেন tar.gzফাইল SourgeForge

ব্যবহারের সহজ উদাহরণ:

 $ rpl old_string new_string test.txt

দ্রষ্টব্য যে স্ট্রিংয়ের ফাঁকা স্থান থাকলে এটি উদ্ধৃতি চিহ্নগুলিতে আবদ্ধ হওয়া উচিত। ডিফল্টরূপে rplবড় হাতের অক্ষরের যত্ন নিন তবে সম্পূর্ণ শব্দের নয় , তবে আপনি বিকল্পগুলি -i(কেস উপেক্ষা করুন ) এবং -w(পুরো শব্দ) দিয়ে এই ডিফল্টগুলি পরিবর্তন করতে পারেন । আপনি একাধিক ফাইল নির্দিষ্ট করতে পারেন :

 $ rpl -i -w "old string" "new string" test.txt test2.txt

এমনকি অনুসন্ধানে পুনরুক্তি ( ) অনুসন্ধান করতে এমনকি অনুসন্ধান করতে এক্সটেনশানগুলি ( -x) উল্লেখ করুন :-R

 $ rpl -x .html -x .txt -R old_string new_string test*

আপনি ইন্টারেক্টিভ মোডে অনুসন্ধান / প্রতিস্থাপন করতে পারেন-p (প্রম্পট) বিকল্পের সাহায্যে :

আউটপুটটি ফাইলের / স্ট্রিংয়ের সংখ্যা এবং অনুসন্ধানের ধরণের সংখ্যা (সংবেদনশীল, পুরো / আংশিক শব্দের ক্ষেত্রে) দেখায়, তবে এটি -q( সাইলেন্ট মোড ) বিকল্পের সাথে নিস্তব্ধ হতে পারে , বা আরও বেশি ভার্বোজের সাথে তালিকার লাইন সংখ্যার তালিকা করে প্রতিটি ফাইল এবং ডিরেক্টরি এর সাথে মেলে -v( ভার্বোস মোড ) বিকল্পের ।

মনে রাখার মতো অন্যান্য বিকল্পগুলি হ'ল -e(সম্মান স্ক্যাপগুলি) যা অনুমতি দেয় regular expressions, তাই আপনি ট্যাবগুলি ( \t), নতুন লাইন ( \n) ইত্যাদিও অনুসন্ধান করতে পারেন । এমনকি আপনি ব্যবহার করতে পারেন -fথেকে অনুমতি বাধ্য (অবশ্যই ব্যবহারকারী লিখন অনুমতি শুধুমাত্র যখন,) এবং -dপরিমার্জন times` সংরক্ষণ)।

অবশেষে, আপনি যদি নিশ্চিত হন যে কোনটি ঠিক করবে কিনা, -s( সিমুলেট মোড ) ব্যবহার করুন ।


2
মতামত এবং সরলতার চেয়ে অনেক ভাল। আমি কেবল ইচ্ছুক এটি ফাইলের নামগুলিতে অভিনয় করার অনুমতি দেয় এবং তারপরে এটি যথাযথ হবে।
Kzqai

1
আমি -s (সিমুলেট মোড) :-)
erm3nda

25

একাধিক ফাইলের মাধ্যমে কীভাবে অনুসন্ধান করবেন এবং প্রতিস্থাপন করবেন তা পরামর্শ দেয়:

আপনি ফাইন্ড এবং সিড ব্যবহার করতে পারেন তবে আমি দেখতে পেয়েছি যে পার্লের এই ছোট লাইনটি দুর্দান্তভাবে কাজ করে।

perl -pi -w -e 's/search/replace/g;' *.php
  • - এর অর্থ নিম্নলিখিত কোডের লাইনটি কার্যকর করা।
  • -i মানে জায়গাটিতে সম্পাদনা করা
  • -আমরা সাবধানবাণী লিখি
  • - ইনপুট ফাইলের উপর লুপ, স্ক্রিপ্ট প্রয়োগ করার পরে প্রতিটি লাইন মুদ্রণ।

আমার সেরা ফলাফলগুলি পার্ল এবং গ্রেপ ব্যবহার করে এসেছে (সেই ফাইলটির সন্ধানের এক্সপ্রেশন রয়েছে তা নিশ্চিত করতে)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

13

আপনি এক্স মোডে ভিম ব্যবহার করতে পারেন:

বর্তমান ডিরেক্টরিতে সমস্ত ফাইলের স্ট্রিং ALF কে BRA এর সাথে প্রতিস্থাপন করবেন?

for CHA in *
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

সাব ডিরেক্টরি জন্য একই পুনরাবৃত্তভাবে কি না?

find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';'

ফাইলের নামটি অন্য স্ট্রিংয়ের সাথে মেলে তবেই প্রতিস্থাপন করবেন?

for CHA in *.txt
do
  ex -sc '%s/ALF/BRA/g' -cx "$CHA"
done

স্ট্রিং নির্দিষ্ট প্রসঙ্গে পাওয়া গেলেই প্রতিস্থাপন করবেন?

ex -sc 'g/DEL/s/ALF/BRA/g' -cx file

স্ট্রিংটি যদি একটি নির্দিষ্ট লাইন সংখ্যায় থাকে তবে প্রতিস্থাপন করবেন?

ex -sc '2s/ALF/BRA/g' -cx file

একই প্রতিস্থাপনের সাথে একাধিক স্ট্রিং প্রতিস্থাপন করুন

ex -sc '%s/\vALF|ECH/BRA/g' -cx file

বিভিন্ন প্রতিস্থাপনের সাথে একাধিক স্ট্রিং প্রতিস্থাপন করুন

ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file

13

আমি এটি ব্যবহার করেছি:

grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
  1. এতে থাকা সমস্ত ফাইলের তালিকা দিন old_string

  2. ফাঁকা স্থানের ফলাফলের সাথে নিউলাইনটি প্রতিস্থাপন করুন (যাতে ফাইলগুলির তালিকা দেওয়া যায় sed

  3. sedপুরানো স্ট্রিংটিকে নতুনের সাথে প্রতিস্থাপন করতে সেই ফাইলগুলিতে চালান ।

আপডেট: উপরের ফলাফলটি হোয়াইটস্পেসযুক্ত ফাইলের নামগুলিতে ব্যর্থ হবে। পরিবর্তে, ব্যবহার করুন:

grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'


মনে রাখবেন যে যদি আপনার ফাইলের নামের কোনওটিতে ফাঁকা স্থান, ট্যাব বা নতুনলাইন থাকে তবে এটি ব্যর্থ হবে। ব্যবহার grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'এটি স্বেচ্ছাসেবী ফাইল নামের সাথে ডিল করতে হবে।
টেরডন

ধন্যবাদ বন্ধুরা. আপডেট যুক্ত করা হয়েছে এবং পুরাতন কোডটি রেখে দিয়েছে কারণ এটি একটি আকর্ষণীয় ক্যাভিয়েট যা এই আচরণ সম্পর্কে অজ্ঞাত কারো পক্ষে কার্যকর হতে পারে।
o_o_o--

6

ব্যবহারকারীর দৃষ্টিকোণ থেকে, একটি দুর্দান্ত এবং সাধারণ ইউনিক্স সরঞ্জাম যা কাজটি পুরোপুরি করে qsubst। উদাহরণ স্বরূপ,

% qsubst foo bar *.c *.h

আমার সমস্ত সি ফাইলের fooসাথে প্রতিস্থাপন করা হবে bar। একটি দুর্দান্ত বৈশিষ্ট্য হ'ল qsubstএকটি ক্যোয়ারী-রিপ্লেসটি করবে , অর্থাত এটি আমার প্রতিটি উপস্থিতি প্রদর্শন করবে fooএবং জিজ্ঞাসা করবে যে আমি এটি প্রতিস্থাপন করতে চাই কি না। [আপনি নিঃশর্তভাবে (কোনও জিজ্ঞাসা না করা) -goবিকল্পের সাথে প্রতিস্থাপন করতে পারেন , এবং অন্যান্য বিকল্প রয়েছে, উদাহরণস্বরূপ, -wযদি আপনি কেবল প্রতিস্থাপন করতে চানfoo এটি পুরো শব্দটি ব্যবহার করে ।]

এটি কীভাবে পাওয়া যায়: qsubstডার মাউস (ম্যাকগিল থেকে) আবিষ্কার করেছিলেন এবং 198. আগস্টে কম.ইউনিক্স.সোর্স 11 (7) এ পোস্ট করেছিলেন Updated আপডেট সংস্করণ বিদ্যমান। উদাহরণস্বরূপ, নেটবিএসডি সংস্করণটি qsubst.c,v 1.8 2004/11/01আমার ম্যাকের উপর সংকলন করে এবং পুরোপুরি চলে।


2

আমার এমন কিছু দরকার ছিল যা একটি শুকনো রান বিকল্প সরবরাহ করবে এবং একটি গ্লোব নিয়ে পুনরাবৃত্তভাবে কাজ করবে, এবং এটি করার চেষ্টা করার পরে awkএবং sedআমি ছেড়ে দিয়েছিলাম এবং পরিবর্তে অজগরে এটি করেছি।

স্ক্রিপ্ট যাও recursively সমস্ত একটি উল্লিখিত glob প্যাটার্ন (যেমন মিলে ফাইল অনুসন্ধান করে --glob="*.html"একটি Regex জন্য) এবং প্রতিস্থাপন Regex সঙ্গে প্রতিস্থাপন:

find_replace.py [--dir=my_folder] \
    --search-regex=<search_regex> \
    --replace-regex=<replace_regex> \
    --glob=[glob_pattern] \
    --dry-run

প্রতিটি দীর্ঘ বিকল্প যেমন --search-regexএকটি সংক্ষিপ্ত বিকল্প আছে, যেমন -s-hসমস্ত বিকল্প দেখতে চালাতে ।

উদাহরণস্বরূপ, এটি থেকে সমস্ত তারিখ টুসকি হবে 2017-12-31থেকে 31-12-2017:

python replace.py --glob=myfile.txt \
    --search-regex="(\d{4})-(\d{2})-(\d{2})" \
    --replace-regex="\3-\2-\1" \
    --dry-run --verbose
import os
import fnmatch
import sys
import shutil
import re

import argparse

def find_replace(cfg):
    search_pattern = re.compile(cfg.search_regex)

    if cfg.dry_run:
        print('THIS IS A DRY RUN -- NO FILES WILL BE CHANGED!')

    for path, dirs, files in os.walk(os.path.abspath(cfg.dir)):
        for filename in fnmatch.filter(files, cfg.glob):

            if cfg.print_parent_folder:
                pardir = os.path.normpath(os.path.join(path, '..'))
                pardir = os.path.split(pardir)[-1]
                print('[%s]' % pardir)
            filepath = os.path.join(path, filename)

            # backup original file
            if cfg.create_backup:
                backup_path = filepath + '.bak'

                while os.path.exists(backup_path):
                    backup_path += '.bak'
                print('DBG: creating backup', backup_path)
                shutil.copyfile(filepath, backup_path)

            with open(filepath) as f:
                old_text = f.read()

            all_matches = search_pattern.findall(old_text)

            if all_matches:

                print('Found {} matches in file {}'.format(len(all_matches), filename))

                new_text = search_pattern.sub(cfg.replace_regex, old_text)

                if not cfg.dry_run:
                    with open(filepath, "w") as f:
                        print('DBG: replacing in file', filepath)
                        f.write(new_text)
                else:
                    for idx, matches in enumerate(all_matches):
                        print("Match #{}: {}".format(idx, matches))

                    print("NEW TEXT:\n{}".format(new_text))

            elif cfg.verbose:
                print('File {} does not contain search regex "{}"'.format(filename, cfg.search_regex))


if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='''DESCRIPTION:
    Find and replace recursively from the given folder using regular expressions''',
                                     formatter_class=argparse.RawDescriptionHelpFormatter,
                                     epilog='''USAGE:
    {0} -d [my_folder] -s <search_regex> -r <replace_regex> -g [glob_pattern]

    '''.format(os.path.basename(sys.argv[0])))

    parser.add_argument('--dir', '-d',
                        help='folder to search in; by default current folder',
                        default='.')

    parser.add_argument('--search-regex', '-s',
                        help='search regex',
                        required=True)

    parser.add_argument('--replace-regex', '-r',
                        help='replacement regex',
                        required=True)

    parser.add_argument('--glob', '-g',
                        help='glob pattern, i.e. *.html',
                        default="*.*")

    parser.add_argument('--dry-run', '-dr',
                        action='store_true',
                        help="don't replace anything just show what is going to be done",
                        default=False)

    parser.add_argument('--create-backup', '-b',
                        action='store_true',
                        help='Create backup files',
                        default=False)

    parser.add_argument('--verbose', '-v',
                        action='store_true',
                        help="Show files which don't match the search regex",
                        default=False)

    parser.add_argument('--print-parent-folder', '-p',
                        action='store_true',
                        help="Show the parent info for debug",
                        default=False)

    config = parser.parse_args(sys.argv[1:])

    find_replace(config)

Here স্ক্রিপ্টের একটি আপডেট সংস্করণ যা অনুসন্ধানের পদগুলি এবং বিভিন্ন বর্ণের প্রতিস্থাপনকে হাইলাইট করে।


1
আমি বুঝতে পারি না আপনি কেন এই জটিল কিছু তৈরি করবেন। পুনরাবৃত্তির জন্য, বাশের (বা আপনার শেলের সমতুল্য) globstarবিকল্প এবং **গ্লোবস বা ব্যবহার করুন find। একটি শুকনো রান জন্য, শুধু ব্যবহার করুন sed। আপনি যদি -iবিকল্পটি ব্যবহার না করেন তবে এটি কোনও পরিবর্তন করবে না। ব্যাকআপ ব্যবহারের জন্য sed -i.bak(বা perl -i .bak); মেলে না এমন ফাইলগুলির জন্য, ব্যবহার করুন grep PATTERN file || echo file। এবং কেন পৃথিবীতে অজগরটি শেলটি না দেওয়ার পরিবর্তে গ্লোবটি প্রসারিত করবে? কেন কেবল ন্যায়বিচারের script.py --glob=foo*পরিবর্তে script.py foo*?
টেরডন

1
আমার কেন খুব সহজ: (1) সর্বোপরি, ডিবাগ করা সহজ; (২) সহায়ক জনগোষ্ঠীর সাথে কেবলমাত্র একটি একক ভাল নথিভুক্ত সরঞ্জাম ব্যবহার করা (৩) না জানা sedএবং awkভাল না করা এবং তাদের আয়ত্তে অতিরিক্ত সময় ব্যয় করতে ইচ্ছুক না হওয়া, (৪) পাঠযোগ্যতা, (৫) এই সমাধানটি নন-পিক্সিক সিস্টেমগুলিতেও কাজ করবে (এমনটি নয় যে আমার এটি প্রয়োজন তবে অন্য কারও কাছে হতে পারে)।
সিসিপিজ্জা

1

ripgrep (কমান্ডের নাম rg) একটি grepসরঞ্জাম, তবে অনুসন্ধান এবং প্রতিস্থাপনকে সমর্থন করে।

$ cat ip.txt
dark blue and light blue
light orange
blue sky
$ # by default, line number is displayed if output destination is stdout
$ # by default, only lines that matched the given pattern is displayed
$ # 'blue' is search pattern and -r 'red' is replacement string
$ rg 'blue' -r 'red' ip.txt
1:dark red and light red
3:red sky

$ # --passthru option is useful to print all lines, whether or not it matched
$ # -N will disable line number prefix
$ # this command is similar to: sed 's/blue/red/g' ip.txt
$ rg --passthru -N 'blue' -r 'red' ip.txt
dark red and light red
light orange
red sky


rg ইন-প্লেস বিকল্পটি সমর্থন করে না, তাই আপনাকে এটি নিজেই করতে হবে

$ # -N isn't needed here as output destination is a file
$ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
$ cat ip.txt
dark red and light red
light orange
red sky


নিয়মিত এক্সপ্রেশন সিনট্যাক্স এবং বৈশিষ্ট্যগুলির জন্য মরিচা রেজেক্স ডকুমেন্টেশন দেখুন । -Pসুইচ সক্রিয় হবে PCRE2 গন্ধ। rgডিফল্টরূপে ইউনিকোড সমর্থন করে।

$ # non-greedy quantifier is supported
$ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
Xrk sand band cue combat

$ # unicode support
$ echo 'fox:αλεπού,eagle:αετός' | rg '\p{L}+' -r '($0)'
(fox):(αλεπού),(eagle):(αετός)

$ # set operator example, remove all punctuation characters except . ! and ?
$ para='"Hi", there! How *are* you? All fine here.'
$ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
Hi there! How are you? All fine here.

$ # use -P if you need even more advanced features
$ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|\w+' -r '[$0]'
[car] bat [cod] map


পছন্দ করুন grep, -Fবিকল্পটি স্থির স্ট্রিংগুলি মেলাতে অনুমতি দেবে, একটি সহজ বিকল্প যা আমি মনে করি sedএটিও কার্যকর করা উচিত।

$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -F '[4]*' -r '2'
2.3/26
foo
5.3-29


আরেকটি সহজ বিকল্প -Uযা মাল্টলাইন মিলাকে সক্ষম করে

$ # (?s) flag will allow . to match newline characters as well
$ printf '42\nHi there\nHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
42
Hi  Day


rg ডস-স্টাইল ফাইলগুলিও পরিচালনা করতে পারে

$ # same as: sed -E 's/\w+(\r?)$/123\1/'
$ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r '123'
hi 123
good 123


এর আরেকটি সুবিধা rgহ'ল এটির চেয়ে দ্রুত গতি হতে পারেsed

$ # for small files, initial processing time of rg is a large component
$ time echo 'aba' | sed 's/a/b/g' > f1
real    0m0.002s
$ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
real    0m0.007s

$ # for larger files, rg is likely to be faster
$ # 6.2M sample ASCII file
$ wget https://norvig.com/big.txt    
$ time LC_ALL=C sed 's/\bcat\b/dog/g' big.txt > f1
real    0m0.060s
$ time rg --passthru '\bcat\b' -r 'dog' big.txt > f2
real    0m0.048s
$ diff -s f1 f2
Files f1 and f2 are identical

$ time LC_ALL=C sed -E 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1
real    0m0.725s
$ time rg --no-pcre2-unicode --passthru -wP '(\w+)(\s+\1)+' -r '$1' big.txt > f2
real    0m0.093s
$ diff -s f1 f2
Files f1 and f2 are identical
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.