به جای $10 از ${10} استفاده کنید. این روش در BASH و KornShell کار میکند، اما در پیادهسازیهای قدیمی BourneShell کار نمیکند. یک روش دیگر برای دستیابی به پارامترهای مکانی بعد از $9 استفاده از for است، به عنوان مثال, برای به دست آوردن آخرین پارامتر:
# Bourne
for last
do
: # هیچ
done
echo "last argument is: $last"
برای به دست آوردن یک شناسه با عدد، میتوانیم از شمارشگر استفاده کنیم:
# Bourne
n=12 # این شماره شناسهای میباشد که مورد نظر است
i=1
for arg
do
if test $i -eq $n
then
argn=$arg
break
fi
i=`expr $i + 1`
done
echo "argument number $n is: $argn"
مزیت این روش ازبین نرفتن شناسهها است. اگر مشکلی نیست، فرمان shift پارامترهای مکانی قبلی را دور میاندازد:
shift 11
echo "the 12th argument is: $1"
به علاوه، bash و ksh93 با مجموعه پارامترهای مکانی به عنوان یک آرایه رفتار میکنند،و شما میتوانید از ترکیب دستوری بسط پارامتر برای آدرس دادن به عناصر آن از راههای مختلف استفاده کنید :
# Bash, ksh93
for x in "${@:(-2)}" # روی دو پارامتر آخر تکرار میکند
for y in "${@:2}" # روی تمام پارامترها تکرار میشود $2 با شروع از
# استفاده کنیم مفید باشد shift که شاید برای موقعی که نمیخواهیم از
اگرچه دستیابی مستقیم به هر پارامتر مکانی به این روش امکانپذیر است، به ندرت لازم میشود. جایگزین رایج آن استفاده از getopts برای پردازش گزینهها(به عنوان مثال، "-l" یا "-o filename")، و سپس به کار بردن حلقه for یا while برای پردازش تمام شناسهها یکی پس از دیگری است. یک توضیح از چگونگی پردازش شناسههای خط فرمان در پرسش و پاسخ شماره 35 در دسترس میباشد، و یک توضیح دیگر در http://www.shelldorado.com/goodcoding/cmdargs.html یافت میشود.
پرسش و پاسخ 25 (آخرین ویرایش 2009-12-30 18:09:05 توسط MatthiasPopp)
در اکثر پوستهها، هر یک از فرمانهای یک لوله در پوسته فرعی جداگانهای اجرا میگردد. نه در پوسته کاری:
# فعال شده کار میکند lastpipe نگارش 4.2 با bash یا ksh88/ksh93 فقط در
# در سایر پوستهها صفر را چاپ میکند
linecnt=0
printf '%s\n' foo bar | while read -r line
do
linecnt=$((linecnt+1))
done
echo "total number of lines: $linecnt"
دلیل این رفتار تعجبآور پنهانی، آنطور که در بالا وصف شد، آنست که هر پوسته فرعی یک محیط و زمینه متغیر جدیدی برقرار میکند. حلقه while فوق در پوسته فرعی جدیدی با نسخه خودش از متغیر linecnt اجرا میشود، که از پوسته والدش که آنرا با مقدار اولیه '0' ایجاد نموده اخذ کرده است. سپس این نسخه برای شمارش به کار میرود . وقتی حلقه while خاتمه مییابد، نسخه پوسته فرعی دور انداخته میشود، و مقدار اصلی متغیر linecnt پوسته والد(که مقدارش تغییر نکرده) در فرمان echo به کار میرود.
پوستههای متفاوت در این وضعیت رفتار متفاوتی نشان میدهند:
پوسته Bourne موقعی که ورودی یا خروجی هر چه(حلقهها، case و غیره) به غیر از یک فرمان ساده , با استفاده از عملگر تغییر مسیر(< و >) یا لوله، تغییر مسیر داده میشود، یک پوسته فرعی ایجاد میکند.
BASH فقط اگر حلقه قسمتی از لوله باشد، یک پردازش جدید ایجاد میکند.
پوسته Korn فقط اگر حلقه قسمتی از یک لوله باشد، اما آخرین بخش آن نباشد پوسته فرعی ایجاد میکند. مثال read در بالا به طور واقعی در ksh88 و ksh93 کار میکند! (اما در mksh خیر)
POSIX رفتار bash را تصریح میکند، اما به عنوان یک توسعه اجازه میدهد هر قسمت یا تمام لوله بدون پوسته فرعی اجرا شود(به این ترتیب رفتار شل Korn را به خوبی مجاز میکند).
موارد بیشتر معیوب :
# Bash 4
# بدون حلقه نیز مشکل رخ میدهد
printf '%s\n' foo bar | mapfile -t line
printf 'total number of lines: %s\n' "${#line[@]}" # صفر را چاپ میکند
f() {
if [[ -t 0 ]]; then
echo "$1"
else
read -r var
fi
};
f 'hello' | f
echo "$var" # هیچ چاپ نمیکند
دوباره، در هر دو حالت لوله باعث میشود read یا بعضی فرمانها مشمول اجرا در پوسته فرعی شوند، بنابراین اثر آن هرگز در پردازش والد مشاهده نمیشود.
باید تأکید بشود که موضوع مختص حلقهها نیست. خصوصیت عمومی تمام لولهها میباشد، گرچه حلقه "while/read" میتواند مثال استانداردی ملاحظه شود که وقتی افراد توضیحات صفحات man یا help دستور داخلی read را میخوانند و توجه داده میشوند که این دستور داده را از stdin میپذیرد، بارها و بارها ظاهر میگردد. آنها ممکن است به یاد بیاورند که داده تغییر مسیر یافته به درون دستور مرکب در سرتاسر آن دستور در دسترس است، اما درک نمیکنند چراتمام جایگزینی پردازشها و تغییر مسیرهای ظریفی که در سرتاسر محلهایی مانند FAQ #1 به کار میاندازند، ضروری هستند. طبعاً آنها به قرار دادن مواردی به طور مستقیم در لولهها اقدام میکنند و ازعواقب آن سردرگم میشوند.
# POSIX
while read -r line; do linecnt=$(($linecnt+1)); done < file
echo $linecnt
متأسفانه، این مورد در پوسته Bourne کار نمیکند، sh(1) موروثی پوسته Bourne را برای راه عبور از آن ملاحظه کنید.
استفاده از گروهبندی دستورات و انجام همه چیز در پوسته فرعی:
# POSIX
linecnt=0
cat /etc/passwd | {
while read -r line ; do
linecnt=$((linecnt+1))
done
echo "total number of lines: $linecnt"
}
این در واقع وضعیت پوسته فرعی را تغییر نمیدهد، اما اگر در مابقی کُد شما، چیزی از پوسته فرعی مورد نیاز نیست، آنوقت انهدام محیط موقت محلی پس از تمام شدن کار شما با آن، دقیقاً میتواند موردی باشد که به هر حال شما لازم دارید.استفاده از جایگزینی پردازش(فقط Bash):
# Bash
while read -r line; do
((linecnt++))
done < <(grep PATH /etc/profile)
echo "total number of lines: $linecnt"
این مورد در اصل مشابه workaround اول در بالا میباشد. ما بازهم فایلی را تغییر مسیر میدهیم، فقط در اینجا فایل یک لوله با نام (fifo) موقت است که توسط جایگزینی پردازش ما برای انتقال خروجی grep ایجاد گردیده است. استفاده از لوله با نام:
# POSIX
mkfifo mypipe
grep PATH /etc/profile > mypipe &
while read -r line;do
linecnt=$(($linecnt+1))
done < mypipe
echo "total number of lines: $linecnt"استفاده از کمک پردازش(ksh و حتی pdksh و bash نسخه ۴ و oksh و mksh..):
# ksh
grep PATH /etc/profile |&
while read -r -p line; do
linecnt=$((linecnt+1))
done
echo "total number of lines: $linecnt"استفاده از HereString(فقط Bash):
read -ra words <<< 'hi ho hum'
printf 'total number of words: %d' "${#words[@]}"
عملگر <<< مختص bash (نگارش 2.05b و بعد) میباشد، به هرحال یک روش بسیار پاکیزه و سودمند برای تعیین یک رشته کوچک لفظی برای ورودی فرمان است.
# Bash
declare -i linecnt
while read -r; do
((linecnt++))
done <<EOF
hi
ho
hum
EOF
printf 'total number of lines: %d' "$linecnt" # Bash 4.2
set +m
shopt -s lastpipe
printf '%s\n' hi{,,,,,} | while read -r "lines[x++]"; do :; done
printf 'total number of lines: %d' "${#lines[@]}"
Bash نگارش 4.2 رفتار فوقالذکرِ kshمانند در Bash را معرفی نموده. یک هُشدار آن است که کنترل job نباید فعال باشد، که به این وسیله سودمندی آن در یک پوسته محاورهای محدود میگردد. برای مثالهای بیشتری در ارتباط با چگونگی خواندن ورودی و خُرد کردن آن به کلمات، پرسش و پاسخ شماره 1 را ببینید.
پرسش و پاسخ 24 (آخرین ویرایش 2012-03-05 03:38:07 توسط ormaaj)
فقط یک فایل شروع اولیه(start-up) متفاوت تعیین کنید:
bash --rcfile /my/custom/bashrc
نوع دیگر پرسش: من اسکریپتی دارم که یک محیط را تنظیم میکند، و من میخواهم در انتهای آن، کنترل را به کاربر بدهم.
در انتهای اسکریپت دستور exec bash را برای آغاز کردن پوسته محاورهای قرار بدهید. این پوسته از محیط ارث میبرد(که شامل مستعارها نیست، اما اشکالی ندارد، زیرا مستعارها بسیار بد میباشند). البته، همچنین باید مطمئن شوید که اسکریپت شما در یک ترمینال اجرا میشود -- در غیر اینصورت، باید به عنوان مثال با استفاده از دستور exec xterm -e bash آنرا ایجاد کنید.
پرسش و پاسخ 23 (آخرین ویرایش 2010-08-20 12:27:03 توسط rrcs-24-39-102-30)
حسابگر داخلی BASH فقط از اعداد صحیح استفاده میکند:
$ echo $((10/3)) 3
برای اکثر عملیات درگیر با اعداد دارای ممیزشناور، باید از یک برنامه خارجی استفاده شود، به عنوان نمونه از bc، AWK یا dc:
$ echo "scale=3; 10/3" | bc 3.333
فرمان "scale=3" به برنامه bc اعلام میکند که سه رقم دقت بعد از نقطه اعشاری لازم است.
همان مثال با dc( ماشین حساب شسته رفته و سبُکتر از bc):
$ echo "3 k 10 3 / p" | dc 3.333
k دقت اعشار را به 3 رقم تنظیم میکند، و p بالاترین قسمت پُشته را با یک کاراکتر سطر جدید چاپ میکند. به هر حال، پُشته جایگزین نمیشود.
اگر میخواهید اعداد اعشاری را مقایسه کنید(کوچکتر از، یا بزرگتر از) ، و bc گنو را دارید، میتوانید چنین کنید:
# Bash if (( $(bc <<< "1.4 < 2.5") )); then echo "1.4 is less than 2.5." fi
اما، x < y توسط تمام نگارشهای bc پشتیبانی نمیشود:
# خیر HP-UX 10.20 این با برخی نگارشها کار میکند اما با imadev:~$ bc <<< '1 < 2' syntax error on line 1,
اگر میخواهید قابل حمل باشید، به مورد ماهرانهتری نیاز دارید:
# POSIX case $(echo "1.4 - 2.5" | bc) in -*) echo "1.4 is less than 2.5";; esac
این مثال 2.5 را از 1.4 تفریق میکند، و علامت نتیجه را کنترل میکند.اگر منفی باشد، عدد اول کوچکتر از دومی است. در حقیقت، ما با خروجی bc به عنوان عدد رفتار نمیکنیم، با آن به صورت یک رشته رفتار میکنیم، و فقط به اولین کاراکتر آن نگاه میکنیم.
میراث نگارش (Bourne):
# Bourne case "`echo "1.4 - 2.5" | bc`" in -*) echo "1.4 is less than 2.5";; esac
AWK نیز میتواند برای محاسبات به کار برود:
$ awk 'BEGIN {printf "%.3f\n", 10 / 3}'
3.333
در اینجا تفاوت ظریف اما مهمی بین راه حل bc و awk وجود دارد: bc فرمانها و عبارات را از ورودی استاندارد میخواند. از طرف دیگر awk عبارت را به عنوان بخشی از برنامه برآورد میکند. عبارتها در ورودی استاندارد سنجیده نمیشوند، یعنی فرمان echo 10/3 | awk '{print $0}' به جای ارزیابی، نتیجه عبارت 10/3 را چاپ میکند.
نگارشهای جدیدتر پوسته zsh و پوسته Korn یک حساب ممیز شناور داخلی، همراه با توابعی مانند sin() یا cos() در خود دارند. بنابراین بسیاری از این محاسبات، در ksh میتوانند به طور بومی انجام شوند:
# ksh93 $ echo $((3.00000000000/7)) 0.428571428571428571
مقایسه برابری دو عدد با ممیز شناور ، در واقع کار نابخردانهای میباشد، دو محاسبهای که روی کاغذ نتیجه یکسانی ارائه خواهند داد ممکن است در نتایج عددی ممیز شناور تفاوت بسیار جزئی ناشی از گِرد کردن و کوتاهسازی نشان بدهد. اگر میخواهید تعیین کنید که آیا دو عدد با ممیز شناور یکسان هستند، میتوانید:
یک عدد را از دیگری تفریق نموده و مقدار خالص تفاضل را با مقدار ناچیزی به انتخاب خود مقایسه نمایید.
یکی از موارد بسیار اندکی که Bash به طور واقعی میتواند با اعداد ممیز شناور انجام بدهد، گرد نمودن آنها با استفاده از printf است:
# Bash 3.1 # به یکدیگر نزدیک باشند b و a ببینید اگر # گرد کردن آنها تا دو رقم اعشار و مقایسه نتایج به عنوان رشته را a=3.002 b=2.998 printf -v a1 %.2f $a printf -v b1 %.2f $b if [[ $a1 = "$b1" ]]; then echo "a and b are the same, roughly"; fi
پیشبینی احتیاطی: بسیاری از مشکلاتی که به محاسبات ممیز شناور شباهت دارند در حقیقت میتوانند فقط با اعداد صحیح حل بشوند، و بنابراین به این ابزارها نیازی نمیباشد-- به عنوان مثال، مشکلات مربوط به اعداد کسری. برای نمونه، جهت کنترل اینکه آیا نسبت دو عدد x و y، برابر با نسبت 4:3 یا 16:9 میباشد، میتوانید از موردی مشابه این سطرها استفاده کنید:
# Bash # Variables x and y are integers if (( $x*9-$y*16==0 )) ; then echo "16:9." elif (( $x*3-$y*4==0 )) ; then echo "4:3." else echo "Neither 16:9 nor 4:3." fi
در یک بررسی استادانهتر، بدون استفاده از محاسبات ممیز شناور، میتوانست تشخیص داده شود نزدیکترین نسبت 4:3 است یا 16:9. توجه نمایید که این مثال بسیار سادهای میباشد و ظاهراً اعداد ممیز شناور و تقسیم را در گیر میسازد، که با اعداد صحیح و بدون تقسیم حل میشود. اگر امکان داشته باشد، به طور معمول تبدیل مشکل شما به محاسبات صحیح، ثمربخشتر از به کار بردن محاسبات ممیز شناور است.
پرسش و پاسخ 22 (آخرین ویرایش ed 2012-11-26 10:07:41 توسط geirha)
یک تعداد ابزار برای این مورد در دسترس است. که استفاده از هریک به عوامل زیادی بستگی دارد، البته مهمترین عامل آن است که چه چیزی را ویرایش میکنیم.
ویرایش فایلها مستلزم دقت است. تنها ابزار استاندارد که به طور واقعی فایل را ویرایش میکند ed است. سایر روشها میتوانند به کار بروند، اما آنها با فایل موقتی و mv درگیر میشوند (یا ابزارهای غیر استاندارد، یاملحقات POSIX).
ed ویرایشگر خط فرمانی استاندارد UNIX است. در اینجا برخی ترکیب های دستوری رایج برای تعویض رشته olddomain.com با رشته newdomain.com در فایلی به نام file آمده است. هر چهار فرمان کار یکسانی را با درجات متفاوتی از قابلیت حمل و کارایی انجام میدهند:
# Bash در پوسته ed -s file <<< $'g/olddomain\\.com/s//newdomain.com/g\nw\nq' # printf با وجود Bourne در پوسته printf '%s\n' 'g/olddomain\.com/s//newdomain.com/g' w q | ed -s file printf 'g/olddomain\\.com/s//newdomain.com/g\nw\nq' | ed -s file # printf بدون وجود Bourne در پوسته ed -s file <<! g/olddomain\\.com/s//newdomain.com/g w q !
برای تعویض یک رشته در تمام فایلهای شاخه جاری، فقط یکی از موارد فوق را در حلقه قرار بدهید:
for file in ./*; do
[[ -f $file ]] && ed -s "$file" <<< $'g/old/s//new/g\nw\nq'
done
برای انجام این کار به صورت بازگشتی، آسانترین راه فعال نمودن
for file in ./**/*; do
[[ -f $file ]] && ed -s "$file" <<< $'g/old/s//new/g\nw\nq'
done
اگر شما bash نگارش 4 ندارید، میتوانید از find استفاده کنید. متأسفانه، تغذیه ورودی استاندارد ed با یکایک فایلها اندکی کسل کننده است:
find . -type f -exec bash -c 'printf "%s\n" "g/old/s//new/g" w q | ed -s "$1"' _ {} \;
اگر متغیرهای پوسته به عنوان رشتههای جایگزینی یا جستجو به کار بروند، ed مناسب نیست. نه sed، نه هیچ ابزاری که از عبارتهای منظم استفاده کند، مناسب نمیباشند. استفاده از کُد awk همراه با تغییر مسیرها و mv را در انتهای این پرسش و پاسخ ملاحظه کنید.
gsub_literal "$search" "$rep" < "$file" > tmp && mv tmp "$file"
sed ویرایشگر جریانی است، نه ویرایشگر فایل. با وجود این، اشخاص اصرار دارند آن را به طور نامناسب در هرجایی جهت ویرایش فایلها به کار ببرند. این برنامه فایلها را ویرایش نمیکند. sed گنو(و بعضی sedهای BSD) دارای گزینه -i میباشند که یک کپی ایجاد میکند و فایل اصلی را با کپی تعویض میکند. یک عملیات پر خرج، اما اگر شما از کُد غیرقابل حمل، I/O بالاسری، اثرات جانبی نامساعد(از قبیل خرابی پیوندهای نمادین) لذت میبرید، این میتواند یک انتخاب باشد:
sed -i 's/old/new/g' ./* # GNU sed -i '' 's/old/new/g' ./* # FreeBSD
افرادی از شما که پرل نگارش 5 دارند، میتوانند همان کار را با استفاده از این کُد انجام بدهند:
perl -pi -e 's/old/new/g' ./*
کاربرد بازگشتی find:
find . -type f -exec perl -pi -e 's/old/new/g' {} \; # شما هنوز + ندارد find اگر
find . -type f -exec perl -pi -e 's/old/new/g' {} + # اگر دارد
اگر در عوض جایگزینی میخواهید سطرها را حذف کنید:
# را حذف میکند foo هر سطر شامل عبارت منظم پرل perl -ni -e 'print unless /foo/' ./*
برای جایگزینی تمام "unsigned" با "unsigned long" در همان مثال، در صورتی که "unsigned int" یا "unsigned long" نباشند ...:
find . -type f -exec perl -i.bak -pne \
's/\bunsigned\b(?!\s+(int|short|long|char))/unsigned long/g' {} \;
تمام مثالهای فوق از عبارتهای منظم استفاده میکنند، به آن معنی که، دارای مشکلاتی همانند کُد sed قبلی میباشند، ایده آزمایش تعبیه متغیرهای پوسته در آنها ایدهای ترسناک است، و رفتار با یک کمیت انتخابی به عنوان رشته لفظی، در بهترین حالت نیز ناراحت کننده است.
علاوه براین، پرل، بدون آنکه استعداد نهانی تصادم با کاراکترهای علائم را داشته باشد، میتواند برای عبور دادن متغیرها به درون هم رشتههای جستجو و هم رشتههای جایگزین شونده به کار برود:
in="input (/string" out="output string" perl -pi -e $'$quoted_in=quotemeta($ENV{\'in\'}); s/$quoted_in/$ENV{\'out\'}/g' ./*
یا، سادهتر:
in=$search out=$replace perl -pi -e 's/\Q$ENV{"in"}/\Q$env{"out"}/g' ./*
اگر یک متغیر است، این کار میتواند(و باید) به طور خیلی ساده با بسط پارامتر Bash انجام بشود:
var='some string'; search=some; rep=another
# Bash در پوسته
var=${var//"$search"/$rep}
در POSIX خیلی دشوارتر است:
# POSIX تابع
# string_rep SEARCH REPL STRING طرز استفاده
# تعویض میکند STRING در REPL را با SEARCH تمام نمونههای
string_rep() {
# ارزش گذاری متغیرها
in=$3
unset out
# نباید خالی باشد SEARCH متغیر
test -n "$1" || return
while true; do
# نباشد حلقه قطع میشود"$in" دیگر در SEARCH اگر
case "$in" in
*"$1"*) : ;;
*) break;;
esac
# الحاق میکند "$out" را به REPL و SERCH راتا رسیدن به اولین نمونه "$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" نقلقولی شده بود، به هرحال با نقلقولها به صورت لفظی رفتار میگردید.
بسطهای پارامترِ مانند این، به طور مفصلتر در پرسش و پاسخ شماره 100 بحث شده است.
اگر جریان است، بنابراین از
some_command | sed 's/foo/bar/g'
sed از عبارتهای منظم استفاده میکند. در مثال ما، foo و bar رشتههای لفظی هستند. اگر آنها متغیر بودند(به عنوان مثال، ورودی کاربر)، به منظور اجتناب از خطاها، آنها میبایست با دقت بسیار پوشش داده میشدند. این کار خیلی غیر عملی است، و کوشش برای انجام آن کُد شما را بینهایت مستعد باگها مینماید. تعبیه متغیرهای پوسته در فرمانهای sed هرگز ایده خوبی نیست.
شما این کار را میتوانستید در خود Bash توسط ترکیب بسط پارامتر با پرسش و پاسخ شماره 1 انجام بدهید:
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
اگر میخواهید پردازشی بیش از فقط یک جستجو و تعویض ساده انجام بدهید، شاید این بهترین گزینه باشد. توجه داشته باشید که آخرین مثال حلقه را در یک پوسته فرعی اجرا میکند. برای اطلاعات بیشتر در این مورد پرسش و پاسخ شماره 24 را ببینید.
شاید متوجه شده باشید، به هرحال آن حلقه bash بالا برای مجموعه بزرگ دادهها خیلی کُند است. بنابراین چطور موردی پیدا کنیم که بتواند رشتههای لفظی را تعویض نماید؟ خوب، میتوانستید AWK را به کار ببرید. تابع ذیل با خواندن از ورودی استاندارد و نوشتن در خروجی استاندارد، تمام نمونههای STR را با REP تعویض میکند.
# gsub_literal STR REP : نحوه استفاده
# stdout و نوشتن در stdin تعویض میکند با خواندن از REP را با STR تمام نمونههای
gsub_literal() {
# نمیتواند خالی باشد STR
[[ $1 ]] || return
# string manip needed to escape '\'s, so awk doesn't expand '\n' and such
awk -v str="${1//\\/\\\\}" -v rep="${2//\\/\\\\}" '
# طول رشته جستجو را به دست میآورد
BEGIN {
len = length(str);
}
{
# رشته خروجی تهی
out = "";
# ادامه حلقه تا موقعی که رشته جستجو در سطر هست
while (i = index($0, str)) {
# پیوست کردن هر چیز قبل از رشته جستجو و رشته جایگزین
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}'
پرسش و پاسخ 21 (آخرین ویرایش 2012-08-12 19:11:59 توسط e36freak)