فصل دوازدهم - آرگومان‌ها و آپشن‌ها

از Linuxreview Wiki
پرش به: ناوبری, جستجو


آرگومان، هر ورودی است که در هنگام اجرای یک برنامه (به هر زبانی) در خط فرمان، به آن داده می‌شود. اولین آرگومان (اندیس صفر) همیشه نام/مسیر فایل برنامه است.

یک آپشن نوعی آرگومان است که با علامت - که dash یا خط فاصله خوانده می‌شود، شروع می‌شود. اگر با یک dash شروع شود، به آن short option و اگر با دوتا شروع شود long option می‌گویند

مثلا h- در این مثال، یک short option است:

progname -h

و help-- یک long option است:

progname --help

در این تصویر، دسته‌بندی آرگومان‌های خط فرمان را، به همراه یک مثال می‌بینید:


مثال: دستور زیر را در نظر بگیرید:

$ ls -aF fruit

در اینجا آرگومان‌‌های دستور ls دو رشته‌ی aF- و fruit هستند در حالی که آپشن این دستور تنها aF- است.

برای نشان دادن استفاده‌ی آپشن‌ها اسکریپت زیر را نظر بگیرید که عملیات ایجاد یا خواندن یک فایل tar را برای شما انجام میدهد:

USAGE="Usage: $0 [-c|-t] [file|directory]"
case "$1" in
	-t) TARGS="-tvf $2" ;;
	-c) TARGS="-cvf $2.tar $2" ;;
	*) echo "$USAGE"
		exit 0
		;;
esac

حالا پس از اجرای این اسکریپت جمله‌ی زیر نمایش داده میشود:

Usage: ./mytar [-c|-t] [file|directory]

در این اسکریپت برای ایجاد یک فایل tar از دستور زیر

$ ./mytar -c fruits

و برای خواندن آن از دستور زیر استفاده میشود

$ ./mytar -t fruits


محتویات

[نهفتن]

دستور basename

این دستور آدرس نسبی یا مطلق یک فایل را گرفته و نام آنرا نمایش میدهد. شکل کلی این دستور به صورت زیر است:

basename file

مثال:

$ basename /usr/bin/sh

که منجر به تولید خروجی زیر میشود:

sh

مثال: به طریق زیر میتوان از دستور basename در اسکریپت قبل استفاده کرد:

USAGE="Usage: `basename $0` [-c|-t] [file|directory]"

که خروجی زیر را تولید میکند:

Usage: mytar [-c|-t] [file|directory]

اشکالات رایج در کار با آرگومان‌ها

حالا اسکریپت mytar با استفاده‌ از آپشن‌ها تعین میکند که کدام اسکریپت اجرا شود. ولی مشکل دیگری که شما هنوز حل نکرده‌اید حالتی است که آرگومان دوم به اسکریپت ارسال نشود. البته شما نگرانی بابت ارسال شدن آرگومان اول ندارید زیرا در ساختار case با اسفتاده از کاراکتر * حالات ناخواسته نیز تعریف شده‌اند.
ساده‌ترین راه برای بررسی تعداد آرگومان‌های ارسال شده به برنامه استفاده از متغیر #$ است. این متغیر تعداد آرگومان ارسال شده به برنامه را در خود ذخیره میکند. استفاده از این متغیر در این اسکریپت به صورت زیر میشود:

#!/bin/sh
USAGE="Usage: `basename $0` [-c|-t] [file|directory]"
if [ $# -lt 2 ] ; then
	echo "$USAGE"
	exit 1
fi
case "$1" in
	-t) TARGS="-tvf $2" ;;
	-c) TARGS="-cvf $2.tar $2" ;;
	*) echo "$USAGE"
		exit 0
		;;
esac
tar $TARGS

در این اسکریپت ابتدا بررسی میشود که تعداد آرگومان‌‌های ارسالی برابر ۲ است یا خیر.
این اسکریپت تقریبا بدون اشکال هست. اما شما همچنان میتوانید کارایی آن‌را افزایش دهید. به عنوان مثال این برنامه تنها اولین آرگومان را به عنوان یک فایل در نظر میگیرد و همچنین بررسی نمیکند که آرگومان ارسال شده واقعا یک فایل است یا خیر.
شما میتوانید تمام آرگومان‌های ارسال شده را به وسلیه‌ی متغیر ویژه‌ي @# بررسی و مورد پردازش قرار دهید یا به عبارتی دیگر میتوانید چندین فایل را به این اسکریپت ارسال کنید. در اینصورت آپشن t- در این اسکریپت به صورت زیر بازنویسی میشود:

case "$1" in
	-t) TARGS="-tvf"
		for i in "$@" ; do
			if [ -f "$i" ] ; then tar $TARGS "$i" ; fi ;
		done
		;;
	-c) TARGS="-cvf $2.tar $2" ;
		tar $TARGS
		;;
	*) echo "$USAGE" ;
		exit 0
		;;
esac

تفاوت اصلی که در استفاده از آپشن t- ایجاد شده است، استفاده از یک حلقه‌ی for برای بررسی تمام آرگومان‌‌های ارسال شده است. در اینصورت اگر آرگومان ارسال یک فایل باشد آنگاه به یک فایل tar تبدیل میشود.

توجه: برای مشاهده‌ی آرگومان‌های ارسال شده به یک تابع میتوان از دو متغیر ویژه‌ی *# و @# استفاده کرد. تفاوت اصلی این دو متغیر در نحوه‌ی تفکیک نام‌هایی که به عنوان آرگومان‌ ارسال میشوند است. زمانی که شما از متغیر *# استفاده میکنید این متغیر فاصله بین نام‌فایل‌ها را در نظر نمیگرد. به عنوان مثال اگر نام فایلی به صورت my tar file.tar باشد، و به عنوان یک آرگومان ارسال شود، متغیر *# نام این فایل را به عنوان سه آرگومان my و tar و file.tar در نظر میگیرد که در بسیار موارد میتواند مشکل ساز شود. در صورتی که متغیر @# نام هر فایل را به عنوان یک آرگومان در نظر میگیرد.



در اسکریپت آخر باز هم ممکن است مشکلات بسیار کوچکی وجود داشته باشد. اگر دقیق‌تر به آخرین اسکریپتی که ایجاد شده نگاه کنیم، مشاهده میشود تمام آرگومان‌‌هایی که به برنامه ارسال میشوند که شامل آرگومان اول، 1$، نیز است، به عنوان فایل در نظر گرفته میشوند. و از آنجا که از آرگومان اول به عنوان نحوه‌ی انجام اسکریپت استفاده میشود نباید از این روش استفاده کرد. در نتیجه باید آرگومان اول را از حلقه‌ی for حذف کرد. برای این‌منظور از دستور shift استفاده میشود.
مشکل دیگر زمانی میتواند اتفاق بیفتد که برنامه نتواند محتوای یک فایل tar را نمایش دهد. از آنجا که متغیر ?$ کد خروج آخرین دستور را در خود ذخیره میکند میتوان در این حالت از این متغیر برای تشخیص وقوع یا عدم‌وقوع خطا استفاده کرد.
با این توضیحات اسکریپت نهایی به صورت زیر پیاده سازی میشود:

#!/bin/sh
USAGE="Usage: `basename $0‍` [-c|-t] [files|directories]"

if [ $# -lt 2 ] ; then
	echo "$USAGE" ;
	exit 1 ;
fi

case "$1" in
	-t) shift ; TARGS="-tvf" ;
		for i in "$@" ; do
			if [ -f "$i" ] ; then
				FILES=‍`tar $TARGS "$i" 2>/dev/null`
				if [ $? -eq 0 ] ; then
					echo ; echo "$i" ; echo "$FILES"
				else
					echo "ERROR: $i not a tar file."
				fi
			else
				echo "ERROR: $i not a file."
			fi
		done
		;;
	-c) shift ; TARGS="-cvf" ;
		tar $TARGS archive.tar "$@"
		;;
	*) echo "$USAGE"
		exit 0
		;;
esac

exit $?


جداسازی آپشن‌ها در شل

دو راه برای جدا سازی آپشن‌های یک دستور در شل وجود دارد. روش اول استفاده از ساختار case است که در بخش نمونه‌ای از آن را مشاهده کردید. روش دومی که در این بخش بررسی میشود استفاده از دستور getops است. شکل کلی این دستور به صورت زیر است:

getopts option-string variable

در این ساختار option-string رشته‌ای از کاراکترهای آپشن‌های مختلف است که دستور getops باید در نظر بگیرد. و variable نام متغیری است که این آپشن‌ها باید بر روی آن اعمال شوند.
مراحل انجام دستور getops به صورت زیر است:


استفاده از getopts

در حال تکمیل...

#!/bin/sh
USAGE="Usage: `basename $0` [-v] [-f] [filename] [-o] [filename]";

VERBOSE=false

while getopts f:o:v OPTION ; do
	case "$OPTION" in
		f) INFILE="$OPTARG" ;;
		o) OUTFILE="$OPTARG" ;;
		v) VERBOSE=true ;;
		\?) echo "$USAGE" ;
		exit 1
		;;
	esac
done

shift `echo "$OPTIND - 1" | bc`

if [ -z "$1" -a -z "$INFILE" ] ; then
	echo "ERROR: Input file was not specified."
	exit 1
fi

if [ -z "$INFILE" ] ; then INFILE="$1" ; fi

: ${OUTFILE:=${INFILE}.uu}

if [ -f "$INFILE" ] ; then
	if [ "$VERBOSE" = "true" ] ; then
		echo "uuencoding $INFILE to $OUTFILE... \c"
	fi
	uuencode $INFILE $INFILE > $OUTFILE ; RET=$?

	if [ "$VERBOSE" = "true" ] ; then
		MSG="Failed" ; if [ $RET -eq 0 ] ; then MSG="Done." ; fi
		echo $MSG
	fi
fi
exit 0
ابزارهای شخصی
گویش‌ها
فضاهای نام
عملکردها
گشتن
کتاب‌ها
مقاله‌ها
جعبه‌ابزار