Bash বা zshell মত শেল ব্যবহার করে, কিভাবে আমি একটি recursive 'খুঁজে পেতে এবং প্রতিস্থাপন করতে পারেন? অন্য কথায়, আমি এই ডিরেক্টরি এবং এর সাবডিরেক্টরির সমস্ত ফাইলগুলিতে 'বার' দিয়ে 'foo' এর প্রতিটি ঘটনার প্রতিস্থাপন করতে চাই।
Bash বা zshell মত শেল ব্যবহার করে, কিভাবে আমি একটি recursive 'খুঁজে পেতে এবং প্রতিস্থাপন করতে পারেন? অন্য কথায়, আমি এই ডিরেক্টরি এবং এর সাবডিরেক্টরির সমস্ত ফাইলগুলিতে 'বার' দিয়ে 'foo' এর প্রতিটি ঘটনার প্রতিস্থাপন করতে চাই।
উত্তর:
এই কমান্ডটি এটি করবে (ম্যাক ওএস এক্স লায়ন এবং কুবুন্টু লিনাক্স উভয় ক্ষেত্রেই পরীক্ষিত)।
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
এখানে কিভাবে এটা কাজ করে:
find . -type f -name '*.txt'
বর্তমান ডিরেক্টরি (খুঁজে পাওয়া যায় নি) .
) এবং নীচে, সব নিয়মিত ফাইল ( -type f
) যার নাম শেষ .txt
|
পরবর্তী কমান্ডের যে কমান্ডের আউটপুট (ফাইলের নামের একটি তালিকা) পাস করে xargs
ঐ ফাইল নাম সংগ্রহ এবং এক এক তাদের এক হাতে sed
sed -i '' -e 's/foo/bar/g'
মানে "ব্যাকআপ ব্যতীত ফাইলটি সম্পাদনা করুন, এবং নিম্নলিখিত বিকল্পটি তৈরি করুন ( s/foo/bar
) লাইন প্রতি একাধিক বার ( /g
) "(দেখুন man sed
) লাইন 4 এ 'ব্যাকআপ ছাড়া' অংশটি আমার জন্য ঠিক আছে, যেহেতু আমি যে ফাইলগুলি পরিবর্তন করছি তা যেকোনোভাবে সংস্করণ নিয়ন্ত্রণের অধীনে রয়েছে, তাই ভুল হলে আমি সহজেই পূর্বাবস্থায় ফেরাতে পারি।
-print0
বিকল্প। আপনার কমান্ড তাদের নামের মধ্যে স্পেস ইত্যাদি ফাইলগুলিতে ব্যর্থ হবে।
find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +
GNU সঙ্গে এই সব কি হবে।
sed: can't read : No such file or directory
যখন আমি রান find . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'
, কিন্তু find . -name '*.md' -print0
অনেক ফাইল একটি তালিকা দেয়।
-i
এবং ''
''
পরে sed -i
কি হয় ''
ভূমিকা?
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
এই মুছে ফেলা হয় xargs
নির্ভরতা।
sed
, তাই অধিকাংশ সিস্টেমে ব্যর্থ হবে। গনুহ sed
আপনি মধ্যে কোন স্থান করা প্রয়োজন -i
এবং ''
।
আপনি যদি জিট ব্যবহার করেন তবে আপনি এটি করতে পারেন:
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
শুধুমাত্র ফাইল নাম তালিকা। -z
প্রতিটি ফলাফলের পরে একটি নিল বাইট প্রিন্ট।
আমি এটি শেষ করতে পেরেছি কারণ কোনও প্রকল্পের কিছু ফাইল ফাইলটির শেষে একটি নতুন লাইন ছিল না, এবং এটি অন্য কোনও পরিবর্তন না করেও একটি নতুন লাইন যুক্ত করেছে। (ফাইলগুলি শেষ পর্যন্ত একটি নতুন লাইন থাকা উচিত কিনা তা নিয়ে কোনও মন্তব্য নেই।)
find ... -print0 | xargs -0 sed ...
সমাধানগুলি অনেক বেশি সময় নেয় না তবে এমন ফাইলগুলিতে নতুন লাইন যুক্ত করে যা ইতিমধ্যে একটি না থাকে, যা একটি গিট রেপোর ভিতরে কাজ করার সময় বিরক্তিকর। git grep
তুলনা করে দ্রুত আলো হয়।
এখানে আমার zsh / perl ফাংশন আমি এই জন্য ব্যবহার করি:
change () {
from=$1
shift
to=$1
shift
for file in $*
do
perl -i.bak -p -e "s{$from}{$to}g;" $file
echo "Changing $from to $to in $file"
done
}
এবং আমি এটা ব্যবহার করে চালানো হবে
$ change foo bar **/*.java
(উদাহরণ স্বরূপ)
চেষ্টা করুন:
sed -i 's/foo/bar/g' $(find . -type f)
উবুন্টু 1২.04 এ পরীক্ষিত।
সাবডিরেক্টরি নাম এবং / অথবা ফাইলের নামগুলিতে স্পেস থাকে তবে এই কমান্ডটি কাজ করবে না, তবে যদি আপনি তাদের এই কমান্ডটি ব্যবহার না করেন তবে এটি কাজ করবে না।
এটি সাধারণত ডিরেক্টরি নাম এবং ফাইলের নামগুলিতে স্পেস ব্যবহার করার একটি খারাপ অভ্যাস।
http://linuxcommand.org/lc3_lts0020.php
"ফাইল নাম সম্পর্কে গুরুত্বপূর্ণ তথ্য" দেখুন
আমি এখন এই শেল স্ক্রিপ্টটি ব্যবহার করি, যা অন্য উত্তরগুলি থেকে এবং ওয়েব অনুসন্ধান থেকে আমি যা শিখেছি তা সমন্বয় করে। আমি এটি একটি ফাইল বলা change
আমার একটি ফোল্ডারে $PATH
এবং কি chmod +x change
।
#!/bin/bash
function err_echo {
>&2 echo "$1"
}
function usage {
err_echo "usage:"
err_echo ' change old new foo.txt'
err_echo ' change old new foo.txt *.html'
err_echo ' change old new **\*.txt'
exit 1
}
[ $# -eq 0 ] && err_echo "No args given" && usage
old_val=$1
shift
new_val=$1
shift
files=$* # the rest of the arguments
[ -z "$old_val" ] && err_echo "No old value given" && usage
[ -z "$new_val" ] && err_echo "No new value given" && usage
[ -z "$files" ] && err_echo "No filenames given" && usage
for file in $files; do
sed -i '' -e "s/$old_val/$new_val/g" $file
done
আমার ব্যবহার ক্ষেত্রে আমি প্রতিস্থাপন চেয়েছিলেন foo:/Drive_Letter
সঙ্গে foo:/bar/baz/xyz
আমার ক্ষেত্রে আমি নিম্নলিখিত কোড দিয়ে এটি করতে সক্ষম হয়েছিলাম।
আমি ফাইলের প্রচুর ছিল যেখানে একই ডিরেক্টরি অবস্থান ছিল।
find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'
সাহায্য যে আশা করি।
উবুন্টু এবং সেন্টোসের উপর নিম্নোক্ত কমান্ডটি কাজ করে। যাইহোক, ওএস এক্স এর অধীনে আমি ত্রুটি পেয়েছি:
find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;
sed: 1: "./Root": অবৈধ কমান্ড কোড।
যখন আমি xargs এর মাধ্যমে প্যারামিগুলিকে পাস করার চেষ্টা করি তখন এটি কোন ত্রুটি ছাড়াই কাজ করে:
find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'
-i
থেকে -i ''
আপনি পরিবর্তিত যে আসলে তুলনায় আরো প্রাসঙ্গিক -exec
থেকে -print0 | xargs -0
। BTW, আপনি সম্ভবত প্রয়োজন হবে না -e
।