اگر آن رشته یک متغیر است، این میتواند (و باید)خیلی به سادگی با بسط پارامتر انجام بشود. انشعاب کردن به یک ابزار خارجی برای دستکاری رشتهها به افراط آهسته و غیر ضروری است.
var='some string'; search=some; rep=another # Bash در var=${var//"$search"/$rep} # POSIX تابع # usage: string_rep SEARCH REPL STRING # تعویض میکند REPL را با STRING در SEARCH تمام نمونههای string_rep() { # مقدار دهی آغازین in=$3 unset out # نباید تهی باشد SEARCH [ "$1" ] || return while true; do # نباشد "$in" دیگر شامل SEARCH انقطاع حلقه در صورتیکه case "$in" in *"$1"*) : ;; *) break;; esac # "$out" در REP بعلاوه SEARCH تا رسیدن به اولین نمونه "$in" پیوست کردن هر چیز از out=$out${in%%"$1"*}$2 # "$in" و خود این نمونه از SEARCH حذف هرچیز تا رسیدن به اولین نمونه in=${in#*"$1"} done # و چاپ out باقی مانده است به "$in" در SEARCH ضمیمه هر آنچه بعد از آخرین نمونه printf '%s%s\n' "$out" "$in" } var=$(string_rep "$search" "$rep" "$var")توجه: POSIX روشی برای موضعی ساختن متغیرها ندارد. هر چند، اکثر پوستهها (حتی dash و busybox) دارند. اگر پوسته شما از آن پشتیبانی میکند از موضعی ساختن متغیرها آسوده خاطر باشید. حتی اگر پشتیبانی نمیکند، اگر تابع را با var=$(string_rep ...) فراخوانی کنید، تابع یک پوسته فرعی اجرا خواهد نمود و هر تخصیصی که انجام دهد سماجت نخواهد کرد.
در مثال bash، نقلقولهای اطراف "$search" از رفتار شدن با محتوای متغیر به عنوان یک الگوی پوسته (glob نیز نامیده شده)، پیشگیری میکنند. البته، در صورتیکه انطباق الگو مورد نظر است، نقلقولها را ضمیمه نکنید. به هرحال، اگر "$rep" نقلقولی شده بود، با نقلقولها به عنوان کاراکترهای لفظی رفتار میگردید.
بسط پارامترهایی مانند این مورد با تفصیل بیشتر در پرسش و پاسخ شماره ۱۰۰ بحث گردیدهاند.
اگر یک فایل یا جریان باشد، مسائل یک مقدار کمی ماهرانه میشوند. ابزارهای استاندارد معتبر برای این مورد sed یا AWK (برای جریانها)، و ed (برای فایلها) میباشند.
البته، خودتان میتوانید در bash از طریق ترکیب روش قبلی با پرسش و پاسخ شماره ۱ آن را انجام بدهید:
search=foo; rep=bar while IFS= read -r line; do printf '%s\n' "${line//"$search"/$rep}" done < <(some_command) some_command | while IFS= read -r line; do printf '%s\n' "${line//"$search"/$rep}" done
اگر میخواهید پردازشی بیش از فقط یک جستجو-تعویض ساده انجام بدهید، شاید این بهترین گزینه باشد. توجه نمایید که مثال اخیر حلقه را در یک پوسته فرعی اجرا میکند. برای اطلاعات بیشتر در باره آن پرسش و پاسخ شماره ۲۴ را ببینید.
البته، یک گزینه دیگر sed خواهد بود:
# تعویض میکند "replace" را با "some_command" در خروجی "search" تمام نمونههای some_command | sed 's/search/replace/g'
sed عبارتهای منظم را به کار میبرد. بر خلاف bash، به منظور رفتار کردن بامقادیر به عنوان رشتههای لفظی "search" و "replace" شدیداً باید پوشش یافته میشدند. این کار بسیار غیر عملی است، و کوشش برای انجام آن کُد شما را به شدت مستعد باگها خواهد نمود. تعبیه متغیرهای پوسته در sed هرگز ایده مناسبی نیست.
به هر حال، ممکن است توجه نمایید که حلقه bash فوق برای مجموعههای بزرگ اطلاعات خیلی کُند است. بنابراین چطور مورد سریعتری پیدا کنیم، که بتواند رشتههای لفظی را تعویض کند؟ خوب، میتوانید AWK را به کار ببرید. تابع زیر تمام نمونههای STR با REP را تعویض میکند، با خواندن از stdin و نوشتن در stdout.
# usage: gsub_literal STR REP # مینویسد stdout را میخواند و در stdin .تعویض میکند REP را با STR تمام نمونههای gsub_literal() { # نمیتواند تهی باشد STR [[ $1 ]] || return # و مانند آن باز داشته شود '\n' از بسط awk ها پوشانده شود تا '\' لازم است رشته با awk -v str="${1//\\/\\\\}" -v rep="${2//\\/\\\\}" ' # به دست آوردن طول رشته جستجو BEGIN { len = length(str); } { # تهی کردن رشته خروجی out = ""; # ادامه حلقه مادامیکه رشته جستجو در سطر وجود دارد while (i = index($0, str)) { # پیوست کردن هر چیز تا رشته جستجو، و رشته جایگزین string out = out substr($0, 1, i-1) rep; # حذف کردن همه چیز تا اولین نمونه جستجو و خود نمونه از سطر $0 = substr($0, i + len); } # پیوست کردن هر آنچه باقی مانده out = out $0; print out; } ' } some_command | gsub_literal "$search" "$rep" # خلاصه شده به عنوان یک سطر: some_command | awk -v s="${search//\\/\\\\}" -v r="${rep//\\/\\\\}" 'BEGIN {l=length(s)} {o="";while (i=index($0, s)) {o=o substr($0,1,i-1) r; $0=substr($0,i+l)} print o $0}'
تعویض رشتهها (آخرین ویرایش 2013-04-15 19:58:23 توسط geirha)