فصل 16- فرمان‌ها، برنامه‌ها و فیلترهای خارجی

‎16.9‎- فرمان‌های متفرقه

فرمان‌هایی که در گروه خاصی قرار نمی‌گیرند

jot‏، ‏seq

این برنامه‌ها رشته‌ای از اعداد صحیح با یک مقدار افزایشی قابل انتخاب‌ به وسیله کاربر، بیرون می‌دهند.

کاراکتر پیش‌فرض جداکننده هر عدد صحیح یک سطر جدید است، اما با استفاده از گزینه ‎-s‎ می‌تواند تغییر داده شود.

bash$ seq 5
 1
 2
 3
 4
 5

bash$ seq -s : 5
1:2:3:4:5

هر دو فرمان jot‏، و ‏seq در یک حلقه for به کار می‌آیند.

مثال ‎16-54‎. کاربرد ‏seq برای تولید شناسه‌های حلقه

#!/bin/bash
#  کاربرد ‎seq‎

echo

for a in `seq 80`      #  یا  ‎for a in $( seq 80 )‎
# همانند ‎for a in 1 2 3 4 5 ... 80‎   (تایپ کردن زیادی را صرفه‌جویی می‌کند!).
#       استفاده از ‎jot‎  ‏(در صورتیکه در سیستم شما موجود باشد)‏ نیز ممکن است.
do
  echo -n "$a "
done                   # 1 2 3 4 5 ... 80
#          مثال استفاده از خروجی یک فرمان برای تولید ‎[list]‎ در یک حلقه ‎for‎

echo; echo


COUNT=80               # بله، ‎seq‎ یک پارامتر قابل تعویض هم می‌پذیرد.

for a in `seq $COUNT`  # یا  ‎for a in $( seq $COUNT )‎
do
  echo -n "$a "
done                   # 1 2 3 4 5 ... 80

echo; echo

BEGIN=75
END=80

for a in `seq $BEGIN $END`
#  با دادن دو شناسه، ‎seq‎ شمارش را از شناسه اول آغاز
#+       می‌کند و تا رسیدن به شناسه دوم ادامه می‌دهد.
do
  echo -n "$a "
done                  # 75 76 77 78 79 80

echo; echo

BEGIN=45
INTERVAL=5
END=80

for a in `seq $BEGIN $INTERVAL $END`
#با دادن سه شناسه ، ‎seq‎ شمارش را از اولی شروع کرده
#+شناسه دوم را برای فاصله اعداد به کار می‌برد، و تا
#+                 رسیدن به شناسه سوم ادامه می‌دهد.
do
  echo -n "$a "
done                 # 45 50 55 60 65 70 75 80

echo; echo

exit 0

یک مثال ساده‌تر:

#مجموعه‌ای از ‎10‎ فایل، نام گذاری شده به صورت
#+file.1‏، file.2‏ . . . file.10 تولید می‌کند.
COUNT=10
PREFIX=file

for filename in `seq $COUNT`
do
  touch $PREFIX.$filename
  # یا می‌توان عملیات دیگری از قبیل ‎rm‎‏، ‎grep‎، و غیره انجام داد.
done

مثال ‎16-55‎. شمارش حرف

#!/bin/bash

# ‎letter-count.sh:‎ شمارش موارد حضور حرف در یک فایل متن
#                        نوشته شده توسط ‎Stefano Palmeri‎
#            با مجوز در راهنمای ‎ABS‎ استفاده گردیده است.
#          به طور مختصر توسط نگارنده ویرایش گردیده است.

MINARGS=2          #   اسکریپت حداقل دو شناسه لازم دارد.
E_BADARGS=65
FILE=$1

let LETTERS=$#-1   #تعداد حروف تعیین شده (به عنوان شناسه خط فرمان)
                   #    (کم کردن یک از تعداد کل شناسه‌های خط فرمان)


show_help(){
	   echo
           echo Usage: `basename $0` file letters  
           echo Note: `basename $0` arguments are case sensitive.
           echo Example: `basename $0` foobar.txt G n U L i N U x.
	   echo
}

# .کنترل تعداد شناسه‌های خط فرمان
if [ $# -lt $MINARGS ]; then
   echo
   echo "Not enough arguments."
   echo
   show_help
   exit $E_BADARGS
fi  


#         .کنترل موجود بودن فایل
if [ ! -f $FILE ]; then
    echo "File \"$FILE\" does not exist."
    exit $E_BADARGS
fi



#    .شمارش تعداد موارد حضور حرف
for n in `seq $LETTERS`; do
      shift
      if [[ `echo -n "$1" | wc -c` -eq 1 ]]; then                 # بررسی شناسه
             echo "$1" -\> `cat $FILE | tr -cd  "$1" | wc -c`     #      شمارش
      else
             echo "$1 is not a  single char."
      fi  
done

exit $?

# این اسکریپت دقیقاً دارای همان کارکرد اسکریپت letter-count2.sh 
#+                            است، اما سریع‌تر از آن است. چرا؟

jot برنامه سودمند کلاسیک یونیکس که تا اندازه‌ای تواناتر از seq است، به طور معمول در یک توزیع استاندارد لینوکس گنجانده نمی‌شود. به هر حال بسته rpm آن جهت دانلود در MIT repository قابل دسترس است.

بر خلاف seq، برنامه jot با استفاده گزینه ‎-r‎ می‌تواند یک سری تصادفی از اعداد تولید نماید.

bash$ jot -r 3 999
 1069
 1272
 1428


getopt

فرمان getopt گزینه‌های خط فرمان دارای یک خط تیره مقدم را تجزیه می‌کند. این فرمان خارجی به فرمان getopts داخلی Bash شباهت دارد. به کار بردن getopt با استفاده از نشانگر ‎-l‎ کار کردن با گزینه‌های بلند را میسر می‌سازد.

مثال ‎16-56‎. کاربرد getopt برای تجزیه گزینه‌های خط فرمان

#!/bin/bash
# کاربرد getopt
# موقع فراخوانی این اسکریپت، موارد زیر را امتحان کنید:
#   sh ex33a.sh -a
#   sh ex33a.sh -abc
#   sh ex33a.sh -a -b -c
#   sh ex33a.sh -d
#   sh ex33a.sh -dXYZ
#   sh ex33a.sh -d XYZ
#   sh ex33a.sh -abcd
#   sh ex33a.sh -abcdZ
#   sh ex33a.sh -z
#   sh ex33a.sh a
#               نتایج هر یک از موارد بالا را توضیح دهید.

E_OPTERR=65

if [ "$#" -eq 0 ]
then       # اسکریپت حداقل یک شناسه خط فرمان لازم دارد.
  echo "Usage $0 -[options a,b,c]"
  exit $E_OPTERR
fi  

set -- `getopt "abcd:" "$@"`
# پارامترهای مکانی را به شناسه‌های خط فرمان تنظیم می‌کند.
# اگر شما به جای ‎$@‎ از ‎$*‎ استفاده نمایید، چه پیش می‌آید؟

while [ ! -z "$1" ]
do
  case "$1" in
    -a) echo "Option \"a\"";;
    -b) echo "Option \"b\"";;
    -c) echo "Option \"c\"";;
    -d) echo "Option \"d\" $2";;
     *) break;;
  esac

  shift
done

# به طور معمول در اسکریپت، استفاده از ‎getopts‎ داخلی مناسب‌تر است.
#                                     اسکریپت ‎ex33.sh‎ را ببینید.

exit 0

به طوریکه ‎Peggy Russell‎ اشاره می‌کند:

بیشتر اوقات برای پردازش صحیح فضای سفید و نقل‌قول‌ها قرار دادن یک eval لازم است.

args=$(getopt -o a:bc:d -- "$@")
eval set -- "$args"

برای یک شبیه‌سازی ساده شده فرمان getopt مثال ‎10-5‎ را مشاهده نمایید.

run-parts

فرمان run-parts ‎‎[1]‎‎ تمام اسکریپت‌ها در یک دایرکتوری هدف را، به طور متوالی به ترتیب نام فایل مرتب شده اسکی، اجرا می‌کند. البته، لازم است اسکریپت‌ها دارای مجوز اجرا باشند.

سرویس cron daemon برای اجرای اسکریپت‌ها در دایرکتوری‌های ‎/etc/cron.*‎ فرمان run-parts را فراخوانی می‌کند.

yes

فرمان yes در رفتار پیش‌فرضش، یک رشته متوالی از کاراکتر y دنبال شده با یک تعویض سطر را به stdout تغذیه می‌کند. یک ‎control-C‎ اجرای آن را خاتمه می‌دهد. یک رشته خروجی متفاوت می‌تواند تعیین بشود، همچون در ‎yes different string‎، که به طور مداوم ‎different string‎ را در stdout بیرون می‌دهد.

شاید کسی بپرسد که مقصود از آن چیست. از خط فرمان یا در یک اسکریپت، خروجی yes می‌تواند به برنامه‌ای که در انتظار ورودی کاربر است، لوله‌کشی یا تغییر مسیر داده شود. در نتیجه، این نوعی نگارش ضعیف از expect می‌شود.

‎yes | fsck /dev/hda1‎ فرمان fsck را به طور غیر محاوره‌ای اجرا می‌کند (احتیاط!).

‎yes | rm -r dirname‎ دارای همان نتیجه ‎rm -rf dirname‎ است (احتیاط!).

WARNINGموقع لوله‌کشی yes به یک فرمان سیستمی بالقوه پر مخاطره، از قبیل fsck یا fdisk احتیاط شرط عقل است. این کار می‌تواند دارای نتایج پیش‌بینی نشده باشد.


فرمان yes متغیرها را تجزیه می‌کند، یا به طور دقیق‌تر، متغیرهای تجزیه شده را بازتاب می‌دهد. برای مثال:

bash$ yes $BASH_VERSION
 3.1.17(1)-release
 3.1.17(1)-release
 3.1.17(1)-release
 3.1.17(1)-release
 3.1.17(1)-release
 . . .

این «ویژگی» خاص ممکن است برای تولید فوری یک فایل اسکی بسیار بزرگ به کار برود:

bash$ yes $PATH > huge_file.txt
Ctl-C
به طور بسیار سریع ‎Ctl-C‎ را بزنید، یا آنکه ممکن است فایلی بزرگتر از آنچه توقع داشتید به دست بیاورید.

فرمان yes می‌تواند در یک تابع اسکریپت بسیار ساده شبیه‌سازی بشود.

yes ()
{                  # شبیه‌سازی ناقابل ‎yes‎ 
  local DEFAULT_TEXT="y"
  while [ true ]   #    یک حلقه بی پایان.
  do
    if [ -z "$1" ]
    then
      echo "$DEFAULT_TEXT"
    else           #  در صورت وجود شناسه
      echo "$1"    #    آن را نمایش بدهد
    fi
  done             #تنها موارد غایب، گزینه‌های ‎--help‎ و ‎--version‎ هستند.
}                  

banner

با استفاده از یک کاراکتر ASCII (پیش‌فرض ‎#‎) شناسه‌ها را به صورت یک banner عمودی بزرگ در stdout نمایش می‌دهد. این خروجی می‌تواند جهت دریافت نسخه کاغذی به یک چاپگر تغییر مسیر داده شود.

توجه نمایید که banner احتمالاً به دلیل آن که دیگر مفید به حساب نمی‌آید از بسیاری توزیع‌های لینوکس حذف گردیده است.

printenv

تمام متغیرهای محیطی تنظیم شده برای یک کاربر خاص را نمایش می‌دهد.

bash$ printenv | grep HOME
HOME=/home/bozo

lp

فرمان‌های lp و lpr، فایل(ها) را برای چاپ شدن به صورت یک نسخه چاپی، به صف چاپ ‎[2]‎می‌فرستند. منشاء نام‌ این فرمان‌ها به چاپگرهای خطی یک دوران دیگر باز می‌گردد. ‎[3]‎

bash$ lp file1.txt
 یا
bash lp <file1.txt

بیشتر اوقات، لوله‌کشی خروجی قالب‌بندی شده فرمان pr به lp مفید است.

bash$ pr -options file1.txt | lp

برنامه‌های قالب‌بندی، از قبیل groff و Ghostscript می‌توانند خروجی‌شان را به طور مستقیم به lp بفرستند.

bash$ groff -Tascii file.tr | lp
bash$ gs -options | lp file.ps

فرمان‌های مرتبط، عبارتند از: lpq، برای نمایش صف چاپ، و lprm، برای حذف کارها از صف چاپ.

tee

[‎یونیکس یک ایده از عمل لوله‌کشی گذرگاه‌ها اقتباس می‌کند.]

این یک عملگر تغییر مسیر، اما با یک تفاوت است. همانند سه راهی لوله‌کشی، این فرمان «هدایت کردن» خروجی فرمان یا فرمان‌ها از داخل یک لوله به یک فایل، اما بدون اثر گذاری بر روی نتیجه را اجازه می‌دهد. برای چاپ کردن یک پردازش درحال پیشرفت در یک فایل یا کاغذ، شاید برای پیگردی آن پردازش به منظور اشکالزدایی، سودمند است.

cat listfile* | sort | tee check.file | uniq > result.file
#                      ^^^^^^^^^^^^^^   ^^^^    

#فایل ‎check.file‎ قبل از اینکه سطرهای تکراری توسط فرمان uniq
#+ حذف گردند، شامل «listfile»های مرتب شده بهم پیوسته می‌شود.

مترجم: بدون در نظر گرفتن بخش ‎| tee check.file‎ در سطر فرمان فوق، عملکرد آن الحاق فایل‌های ‎listfile‎ و تحویل نتیجه آن به فرمان sort برای مرتب نمودن این خروجی و تحویل نتیجه حاصله به فرمان uniq بود که این فرمان نیز سطرهای تکراری را حذف نموده و خروجی را با تغییر مسیر به فایل result.file‎ ارسال می‌نمود.
اکنون نیز همین کار صورت گرفته است. اما با این تفاوت که در بین راه قبل از تحویل خروجی به فرمان uniq، خروجی به ‎tee check.file‎ نیزداده می‌شود و در عین حال بدون هرگونه تغییر به راه خود در لوله ادامه می‌دهد. مشابه لوله‌کشی آب هست، اما قدری تفاوت دارد. بر خلاف آب، در محل انشعاب چیزی از خروجی کم نمی‌شود.

mkfifo

این فرمان گمنام، یک لوله با نام، یک بافر اولین ورودی-اولین خروجی موقتی برای انتقال داده‌ها مابین پردازش‌ها، تولید می‌کند. ‎[4]‎ به طور عادی، یک پردازش در ‎FIFO‎ می‌نویسد، و دیگری از آن می‌خواند. مثال ‎A-14‎ را ببینید.

#!/bin/bash
#   این اسکریپت کوتاه توسط ‎Omair Eshkenazi‎ نوشته شده.
#   با مجوز در راهنمای ‎ABS‎ استفاده گردیده (با تشکر!).

mkfifo pipe1   #    بله، لوله‌ها می‌توانند نامیده شوند.
mkfifo pipe2   #  عنوان «لوله با نام» از این جهت است.

(cut -d' ' -f1 | tr "a-z" "A-Z") >pipe2 <pipe1 &
ls -l | tr -s ' ' | cut -d' ' -f3,9- | tee pipe1 |
cut -d' ' -f2 | paste - pipe2

rm -f pipe1
rm -f pipe2

#وقتی اسکریپت خاتمه می‌یابد، ‎kill‎ کردن پردازش‌های پس‌زمینه لازم نیست(چرا؟).

exit $?

اکنون، فراخواندن اسکریپت و نمایش خروجی‎[7]‎:

sh mkfifo-example.sh
4830.tar.gz          BOZO
pipe1   BOZO
pipe2   BOZO
mkfifo-example.sh    BOZO
Mixed.msg BOZO

pathchk

این فرمان اعتبار یک نام فایل را بررسی می‌کند. اگر نام فایل از حداکثر طول مجاز (‎255‎ کاراکتر) تجاوز نماید یا یک یا چندتا از دایرکتوری‌ها در مسیرش قابل جستجو نباشد، آنوقت یک پیغام خطا نتیجه می‌شود.

متاسفانه، pathchk یک کد خطای قابل تشخیص برگشت نمی‌دهد، و بنابراین، تقریباً در یک اسکریپت بلااستفاده است . به جای آن عملگرهای بررسی فایل را در نظر بگیرید.

dd

اگر چه این فرمان تا اندازه‌ای ناشناس و قدیمی ‎data duplicator‎(رونوشت برداری داده‌ها) از برنامه‌ای برای مبادله داده‌ها بین کامپیوترهای کوچک یونیکس و کامپیوترهای بزرگ آی بی ام (‎IBM mainframes‎) بر روی نوارهای مغناطیسی سرچشمه گرفته است، بازهم موارد استفاده‌اش را دارد. فرمان dd به سادگی، اما با تبدیل، یک فایل (یا ‎stdin/stdout‎) را کپی می‌کند. تبدیل‌ها شامل ‎ASCII/EBCDIC [5]‎، حالت کوچک و بزرگ حروف، مبادله جفت بایت‌ها میان ورودی وخروجی، و پریدن و-یا کوتاه‌ کردن ابتدا یا انتهای فایل ورودی می‌گردد.

#   تبدیل تمام فایل به حروف بزرگ:

dd if=$filename conv=ucase > $filename.uppercase
#                    lcase   # برای تبدیل به حروف کوچک

برخی گزینه‌های اصلی برای dd عبارتند از:

  • if=INFILE

    INFILE فایل مبداء است.

  • of=OUTFILE

    OUTFILE فایل مقصد است، فایلی که داده‌ها در آن نوشته خواهند شد.

  • bs=BLOCKSIZE

    این گزینه، اندازه هر بلوک از داده‌ها است که خوانده یا نوشته می‌شود، به طور معمول توانی از عدد ‎2‎ است.

  • skip=BLOCKS

    تعداد بلوک‌های داده برای پرش از آنها در ‎INFILE‎ قبل از شروع عمل کپی. این گزینه موقعی که INFILE دارای «داده‌های ناخواسته» یا داده‌های آشفته در سرآیندش باشد یا وقتی که تنها کپی قسمتی از INFILE مورد نظر باشد، مفید است.

  • seek=BLOCKS

    تعداد بلوک‌های داده برای پرش در OUTFILE قبل از شروع کپی، رها کردن فضای خالی از داده‌ در ابتدای OUTFILE.

  • count=BLOCKS

    فقط کپی این تعداد بلوک از داده‌ها، به جای تمام INFILE.

  • conv=CONVERSION

    نوع تبدیل برای اعمال شدن روی داده‌های INFILE قبل از نوشتن در OUTFILE.

یک فرمان ‎dd --help‎ تمام گزینه‌هایی را که این برنامه سودمند قدرتمند می‌پذیرد لیست می‌کند.

مثال ‎16-57‎. اسکریپتی که خودش را کپی می‌کند

#!/bin/bash
# self-copy.sh

# .این اسکریپت خودش را کپی می‌کند

file_subscript=copy

dd if=$0 of=$0.$file_subscript 2>/dev/null
# ‎dd‎ خاموش کردن پیغام‌های       ^^^^^^^^^^^

exit $?

#  برنامه‌ای که تنها خروجی‌اش کد منبع خودش است 
#+  توسط ‎Willard Quine‎ یک quine نامیده می‌شود.
#     آیا این اسکریپت واجد شرایط یک ‎quine‎ هست؟

مثال ‎16-58‎. به کار گیری dd

#!/bin/bash
# exercising-dd.sh

#  اسکریپت توسط ‎Stephane Chazelas‎ نوشته شده است.
# اندکی توسط مولف راهنمای ‎ABS‎ ویرایش گردیده است.

infile=$0           # این اسکریپت
outfile=log.txt     #  فایل خروجی
n=8
p=11

dd if=$infile of=$outfile bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null
#    کاراکترهای n تا p ‏(‎8‎ تا ‎11‎) از این اسکریپت (bash) را استخراج می‌کند.

# ----------------------------------------------------------------------

echo -n "hello vertical world" | dd cbs=1 conv=unblock 2> /dev/null
#عبارت «‎hello vertical world‎» را به طور عمودی به طرف پایین بازتاب می‌دهد.
#    چرا؟  چون هر کاراکتری را که ‎dd‎ صادر می‌کند، یک سطر جدید دنبال می‌کند.
#مترجم: با وجود تبدیل ‎conv=unblock‎ پس از هر cbs-size (در اینجا یک بایت است)، یک سطر جدید درج می‌شود

exit $?

برای اثبات تجربی اینکه dd چطور همه‌فن حریف است، بیایید از آن برای اخذ ضربه کلیدها استفاده کنیم.

مثال ‎16-59‎. ضبط کردن ضربه کلیدها

#!/bin/bash
#  ‎dd-keypress.sh:ضبط ضربه کلیدها بدون نیاز به فشردن ENTER.


keypresses=4                 # .تعداد ضربه کلیدها جهت تصرف کردن


old_tty_setting=$(stty -g)   #     .ذخیره تنظیمات قدیمی ترمینال

echo "Press $keypresses keys."
stty -icanon -echo           #    .از کار انداختن وضعیت متعارفی
                             #      .غیر فعال کردن بازتاب موضعی
keys=$(dd bs=1 count=$keypresses 2> /dev/null)
#در صورتیکه if‏(فایل ورودی) مشخص نشود dd از stdin استفاده می‌کند.

stty "$old_tty_setting"      #   .بازیابی تنظیمات قدیمی ترمینال

echo "You pressed the \"$keys\" keys."

#          با تشکر از ‎Stephane Chazelas‎ برای نشان دادن این روش.
exit 0

فرمان dd می‌تواند دستیابی غیر ترتیبی به یک جریان داده را انجام بدهد.

echo -n . | dd bs=1 seek=4 of=file conv=notrunc
#       ^  مترجم: به طور مستقیم کاراکتر echo شده را روی بایت 5 فایل می‌نویسد
#  گزینه ‎conv=notrunc‎ به معنی آن است که
#+    فایل خروجی کوتاه‌سازی نخواهد شد .
# با تشکر از ‎S.C.‎

فرمان dd می‌تواند داده‌های خام یا تصویر دیسک‌ها را به (یا از) دستگاه‌هایی مانند گرداننده‌های دیسک نرم یا نوارهای مغتاطیسی کپی کند (مثال ‎A-5‎). یک مورد استفاده رایج آن، ایجاد فلاپی‌های بوت شدنی است.

‎dd if=kernel-image of=/dev/fd0H1440

به طور مشابهی، dd می‌تواند تمام محتویات یک دیسک نرم، حتی فرمت شده با یک سیستم عامل «بیگانه» را به صورت یک فایل image به دیسک سخت کپی کند.

dd if=/dev/fd0 of=/home/bozo/projects/floppy.img

بعلاوه، dd می‌تواند فلاش دیسک‌ها و کارت‌های ‎SD‎ بوت شدنی تولید کند.

dd if=image.iso of=/dev/sdb

مثال ‎16-60‎. آماده‌سازی یک کارت ‎SD‎ قابل بوت شدن برای Raspberry Pi

#!/bin/bash
# rp.sdcard.sh
#آماده کردن یک کارت ‎SD‎ با یک image قابل بوت برای ‎Raspberry Pi‎

# ‎$1 =‎ image نام فایل 
# ‎$2 =‎ (فایل دستگاه) ‎sd‎ کارت
# در غیر اینصورت از پیش‌فرض‌ها استفاده می‌شود.

DEFAULTbs=4M                                #         اندازه بلوک، ‎4 mb‎ پیش‌فرض.
DEFAULTif="2013-07-26-wheezy-raspbian.img"  #  توزیع به طور معمول مورد استفاده.
DEFAULTsdcard="/dev/mmcblk0"                #  می‌تواند متفاوت باشد، بررسی کنید!
ROOTUSER_NAME=root                          #باید به عنوان کاربر ارشد اجرا شود!
E_NOTROOT=81
E_NOIMAGE=82

username=$(id -nu)                          # چه کسی این اسکریپت را اجرا می‌کند؟
if [ "$username" != "$ROOTUSER_NAME" ]
then
  echo "This script must run as root or with root privileges."
  exit $E_NOTROOT
fi

if [ -n "$1" ]
then
  imagefile="$1"
else
  imagefile="$DEFAULTif"
fi

if [ -n "$2" ]
then
  sdcard="$2"
else
  sdcard="$DEFAULTsdcard"
fi

if [ ! -e $imagefile ]
then
  echo "Image file \"$imagefile\" not found!"
  exit $E_NOIMAGE
fi

echo "Last chance to change your mind!"; echo
read -s -n1 -p "Hit a key to write $imagefile to $sdcard [Ctl-c to exit]."
echo; echo

echo "Writing $imagefile to $sdcard ..."
dd bs=$DEFAULTbs if=$imagefile of=$sdcard

exit $?

#                           :تمرین‌ها
#          --------------------------------------------
#                             ‎(1‎ کنترل خطای اضافی را فراهم کنید.
# ‎(2‎ ایجاد تشخیص خودکار فایل دستگاه کارت ‎SD‎ در اسکریپت (دشوار!)‏.
#   ‎(3‎ ایجاد تشخیص خودکار فایل تصویر‎(*img)‎ در ‎$PWD‎ توسط اسکریپت.

سایر کاربردهای dd شامل مقداردهی اولیه فایل‌های موقت ‎swap‎ (مثال ‎31-2‎) و ramdiskها (مثال ‎31-3‎) است. حتی می‌تواند یک کپی سطح پایین از کل یک پارتیشن دیسک سخت انجام بدهد، اگر چه لزوماً این پیشنهاد نمی‌شود.

برخی افراد (که احتمالاً کار بهتری برای صرف وقتشان ندارند) به طور دایم در حال اندیشیدن به کاربردهای جالب dd هستند.

مثال ‎16-61‎. حذف کردن یک فایل به طور امن

#!/bin/bash
# ‎blot-out.sh:‎ پاک کردن «تمام» ردپای یک فایل

#   این اسکریپت یک فایل هدف را به طور متناوب با بایت‌های
#+.تصادفی و سپس قبل از حذف فایل، با صفرها رونویسی می‌کند
#  بعد از آن، حتی آزمایش سکتورهای دیسک با روش‌های متداول
#+                  .فایل داده اولیه را فاش نخواهد ساخت

PASSES=7         #       تعداد دفعات رونویسی نمودن فایل
                 #  افزایش این متغیر، بویژه روی فایل‌های
                 #+  .بزرگ، اجرای اسکریپت را کند می‌سازد
BLOCKSIZE=1      # ‎I/O‎ با ‎/dev/urandom‎ به اندازه بلوک 1
#+            نیاز دارد، وگرنه نتایج غریبی به دست می‌آید
E_BADARGS=70     #              .کد خروج خطاهای گوناگون
E_NOT_FOUND=71
E_CHANGED_MIND=72

if [ -z "$1" ]   #          .نام فایل تعیین نگردیده است
then
  echo "Usage: `basename $0` filename"
  exit $E_BADARGS
fi

file=$1

if [ ! -e "$file" ]
then
  echo "File \"$file\" not found."
  exit $E_NOT_FOUND
fi  

echo; echo -n "Are you absolutely sure you want to blot out \"$file\" (y/n)? "
read answer
case "$answer" in
[nN]) echo "Changed your mind, huh?"
      exit $E_CHANGED_MIND
      ;;
*)    echo "Blotting out file \"$file\".";;
esac


flength=$(ls -l "$file" | awk '{print $5}')  #فیلد ‎5‎ طول فایل است.
pass_count=1

chmod u+w "$file"             # .رونویسی و حذف فایل را مجاز می‌کند

echo

while [ "$pass_count" -le "$PASSES" ]
do
  echo "Pass #$pass_count"
  sync         #        خالی کردن بافرها
  dd if=/dev/urandom of=$file bs=$BLOCKSIZE count=$flength
               # نوشتن با بایت‌های اتفاقی
  sync         # دوباره خالی کردن بافرها
  dd if=/dev/zero of=$file bs=$BLOCKSIZE count=$flength
               #       پُر کردن با صفرها
  sync         # بازهم تخلیه مجدد بافرها
  let "pass_count += 1"
  echo
done  


rm -f $file    #سرانجام، حذف فایل درهم ریخته و خُرد شده
sync           #      خالی کردن بافرها برای آخرین نوبت

echo "File \"$file\" blotted out and deleted."; echo


exit 0

#  این روش، تا اندازه‌ای امن است، اگرچه روشی کم بازده و کند
#+      .برای پاره پاره کردن و حذف یک فایل به طور کامل است
#  فرمان ‎shred‎ که بخشی از بسته «fileutils» گنو است، هر چند
#+           .به طور کارآمدتر، اما همین کار را انجام می‌دهد

#فایل با روش‌های معمولی نمی‌تواند «undelete» یا بازیابی گردد
#                                          . . . به هر حال
#+ این روش ساده، احتمالاً  در برابر آنالیزهای پیچیده دادگاهی
#+                                 .تاب مقاومت نخواهد داشت

#   این اسکریپت ممکن است با یک سیستم‌فایل jfs خوب عمل نکند.
#تمرین (دشوار)‏: آن را برای برطرف نمودن این نقص، اصلاح کنید.



#  بسته ‎wipe‎ حذف فایل ‎Tom Vier‎ کار ‎shred‎ کردن کامل فایل را
#+                  .بسیار بهتر از این اسکریپت انجام می‌دهد
# http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2

#برای یک تجزیه و تحلیل عمیق در باره مبحث حذف فایل و امنیت، مقاله
#+                  ‎Peter Gutmann‎ تحت عنوان زیر را ملاحظه نمایید.
#+               «حذف ایمن داده‌ها از حافظه حالت جامد و مغناطیسی»
#     http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html

همچنین، مدخل ‎dd thread‎ در فهرست کتاب‌ها را ببینید.

od

فیلتر od یا ‎octal dump‎، ورودی (یا فایل‌ها) را به اکتال (مبنای 8) یا مبناهای دیگر تبدیل می‌کند. این فیلتر برای مشاهده یا پردازش فایل‌های داده باینری یا فایل‌های دستگاه غیر قابل خواندن سیستم از قبیل ‎/dev/urandom‎، و به عنوان یک فیلتر برای داده‌های باینری سودمند است.

head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p'
# خروجی نمونه: ‎1324725719, 3918166450, 2989231420‎ و غیره.

#  از اسکریپت مثال ‎rnd.sh‎، توسط ‎Stéphane Chazelas‎

همچنین مثال ‎9-16‎ و مثال ‎A-36‎ را ببینید.

hexdump

یک رونوشت‌برداری هگزادسیمال، اکتال، دسیمال، یا ‎ASCII‎ از یک فایل باینری انجام می‌دهد. این فرمان کمابیش معادل فرمان od فوق است، اما تقریباً بدون استفاده است. شاید در ترکیب با dd و less برای دیدن محتویات یک فایل باینری به کار برود.

dd if=/bin/ls | hexdump -C | less
# گزینه ‎-C‎ به طور زیبایی خروجی را در شکل جدولی قالب‌بندی می‌کند.

objdump

اطلاعات در باره یک فایل object یا باینری قابل اجرا را در قالب هگزادسیمال یا به صورت یک لیست‌گیری پیاده شده(disassembled) (با گزینه ‎-d‎) نمایش می‌دهد.

bash$ objdump -d /bin/ls

 /bin/ls:     file format elf32-i386

 Disassembly of section .init:

 080490bc <.init>:
  80490bc:       55                      push   %ebp
  80490bd:       89 e5                   mov    %esp,%ebp
  . . .

mcookie

این فرمان یک «magic cookie»، یک عدد هگزادسیمال شبه‌تصادفی ۱۲۸ بیتی (۳۲ کاراکتر) تولید می‌کند که به طور معمول به عنوان یک «علامت مشخصه» اجازه دادن به وسیله خادم X به کار می‌رود. همچنین برای استفاده در یک اسکریپت به عنوان یک عدد تصادفی «سریع و کم هزینه» نیز مفید است.

random000=$(mcookie)

البته، یک اسکریپت می‌تواند از md5sum برای همین منظور استفاده نماید.

#تولید مجموع مقابله‌ای(checksum) md5 روی خود اسکریپت.
random001=`md5sum $0 | awk '{print $1}'`
#از awk برای درآوردن نام فایل استفاده می‌کند.

فرمان mcookie روش دیگری برای تولید نام فایل «منحصر به فرد» ارایه می‌کند.

مثال ‎16-62‎. تولید کننده نام فایل

#!/bin/bash
# ‎tempfile-name.sh:‎ تولید کننده نام‌فایل موقت

BASE_STR=`mcookie`   #     ‎magic cookie‎ سی و دو کاراکتری.
POS=11               #   محل انتخابی در رشته ‎magic cookie‎
LEN=5                #   گرفتن تعداد ‎$LEN‎ کاراکتر متوالی.

prefix=temp          #   به هر حال، این یک فایل موقت است.
                     #برای «یکتایی» بیشتر، پیشوند نام‌فایل
                     #+  را با به کار بردن همان روش تولید
                     #+         پسوند پایین تولید نمایید.

suffix=${BASE_STR:POS:LEN}
                     #  بیرون کشیدن یک رشته 5 کاراکتری که
                     #+          از موقعیت ‎11‎ شروع می‌شود.

temp_filename=$prefix.$suffix
                     #                    ساختن نام فایل.

echo "Temp filename = "$temp_filename""
exit 0
bash$ sh tempfile-name.sh
 Temp filename = temp.e19ea
#این روش تولید نام‌فایل «یکتا» را با روش ‎date‎ در ‎ex51.sh‎‎ مقایسه کنید.

units

این برنامه سودمند واحدهای اندازه مختلف را به یکدیگر تبدیل می‌کند . در حالیکه، به طور معمول در وضعیت محاوره‌ای فراخوانی می‌گردد، units ممکن است در یک اسکریپت نیز مورد استفاده بیابد.

مثال ‎16-63‎. تبدیل متر به مایل

#!/bin/bash
# unit-conversion.sh
#باید برنامه ‎units‎ نصب شده باشد.


convert_units ()  #واحدها برای تبدیل را به عنوان شناسه می‌پذیرد.
{
  cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}')
                  #بیرون آوردن هر چیزی غیر از عامل واقعی تبدیل.
  echo "$cf"
}  

Unit1=miles
Unit2=meters
cfactor=`convert_units $Unit1 $Unit2`
quantity=3.73

result=$(echo $quantity*$cfactor | bc)

echo "There are $result $Unit2 in $quantity $Unit1."

#اگر واحدهای ناسازگار از قبیل acres(یک واحد سطح) و مایل
#+                به تابع داده شود، چه اتفاقی رخ می‌دهد؟

exit 0

#تمرین: این اسکریپت را برای پذیرش پارامترهای خط فرمان،
#     البته همراه با کنترل خطای متناسب، ویرایش نمایید.

m4

یک گنج پنهان، m4 یک فیلتر پردازش ماکرو ‎[6]‎ قدرتمند، واقعاً یک زبان کامل است. هرچند که در اصل به عنوان یک پیش‌پردازشگر برای ‎RatFor‎ نوشته شد، m4 لباس سودمند بودن به عنوان یک برنامه خودکفا را بر تن نمود. در حقیقت، m4 برخی از توانایی‌های eval‎، tr، و awk را با امکانات بسط ماکروی گسترده‌اش ترکیب می‌کند.

شماره آوریل ‎2002‎ نشریه Linux Journal دارای یک مبحث بسیار دقیق در مورد m4 و موارد استفاده‌ آن است.

مثال ‎16-64‎. کاربرد m4

#!/bin/bash
# ‎m4.sh:‎ کاربرد پردازشگر ماکروی ‎m4‎

# رشته‌ها
string=abcdA01
echo "len($string)" | m4                            #   7
echo "substr($string,4)" | m4                       # A01
echo "regexp($string,[0-1][0-1],\&Z)" | m4          # 01Z

#محاسباتی
var=99
echo "incr($var)" | m4                              # 100
echo "eval($var / 3)" | m4                          #  33

exit

xmessage

این گونه مبتنی بر X از echo یک پنجره پیغام-پرس‌وجو در میز کار ظاهر می‌کند.

xmessage Left click to continue -button okay

zenity

برنامه سودمند zenity برای نمایش widgetهای محاوره GTK+ تنظیم گردیده و برای اهداف اسکریپت‌نویسی بسیار مناسب است.

doexec

فرمان doexec امکان عبور دادن یک فهرست انتخابی از شناسه‌ها را به فایل اجرایی باینری فراهم می‌کند. مخصوصاً، عبور دادن ‎argv[0]‎ (که مطابق است با ‎$0‎ در یک اسکریپت) اجازه می‌دهد فایل اجرایی توسط چند نام فراخوانی گردد، و آنوقت می‌تواند مجموعه‌های مختلفی از عملیات را بر اساس نامی‌ که با آن فراخوانی شده، انجام بدهد. آنچه نتیجه می‌شود، روش غیرمستقیم عبور دادن گزینه‌ها به یک فایل اجرایی است.

برای مثال، دایرکتوری ‎/usr/local/bin‎ می‌تواند شامل یک فایل باینری به نام ‎aaa‎ باشد. فراخوانی ‎doexec /usr/local/bin/aaa list‎ تمام فایل‌ها در دایرکتوری کاری جاری را که با یک «a» شروع می‌شوند list خواهد نمود، در حالیکه فراخوانی ‎doexec /usr/local/bin/aaa delete‎ (همان فایل اجرایی با delete)‏ آن فایل‌ها را delete خواهد نمود.


رفتارهای متفاوت فایل اجرایی باید در داخل خود کد قابل اجرا تعیین بشود، قابل قیاس با موردی مانند پایین در یک اسکریپت پوسته:
case `basename $0` in
"name1" ) do_something;;
"name2" ) do_something_else;;
"name3" ) do_yet_another_thing;;
*       ) bail_out;;
esac


dialog

ابزارهای خانواده dialog روشی برای احضار کردن کادرهای محاوره‌ای «dialog» از داخل یک اسکریپت فراهم می‌نمایند. گونه‌های پیچیده‌تر dialog ‏-- gdialog‏، Xdialog، و kdialog -- در واقع widgetهای پنجره‌های X را فراخوانی می‌کنند.

sox

فرمان sox، یا «sound exchange» نواختن فایل‌های صوتی و تبدیل‌ آنها را انجام می‌دهد. در حقیقت، فایل اجرایی ‎/usr/bin/play‎ ( در حال حاضر منسوخ) چیزی غیراز یک لفاف پوسته برای sox نیست.

برای مثال، sox soundfile.wav soundfile.au یک فایل صوتی WAV را به یک فایل صوتی AU (قالب صوتی Sun) تبدیل می‌کند.

اسکریپت‌های پوسته به طور مطلوبی مناسب عملیات پردازش دسته‌ای sox روی فایل‌های صوتی هستند. برای مثال، Linux Radio Timeshift HOWTO و MP3do Project را ملاحظه کنید.

یادداشت‌ها

[1]

این در حقیقت یک اسکریپت وفق داده شده برای توزیع دبیان لینوکس است.

[2]

صف چاپ، گروهی از کارهای «در حال انتظار» برای چاپ شدن است.

[3]

چاپگرهای خطی مکانیکی بزرگ، در هر زمان یک سطر منفرد از نمونه را با همنوازی مقدار قابل ملاحظه‌ای سر و صدا روی صفحه‌های بهم پیوسته کاغذ greenbar (کاغذ دارای نوارهای سبز و سفید) چاپ می‌کردند. به خروجی کاغذیِ بدین‌گونه چاپ شده، به عنوان یک نتیجه نهایی مراجعه می‌شد.

[4]

برای یک مرور اجمالی بسیار خوب از این مبحث، گفتار ‎Andy Vaught‎، مقدمه‌ای بر لوله‌های بانام، در شماره سپتامبر ‎1997‎ نشریه Linux Journal را ملاحظه نمایید.

[5]

‎EBCDIC‎ (تلفظ شده به صورت «‎ebb-sid-ick‎») یک سرنام برای «‎Extended Binary Coded Decimal Interchange Code‎»، یک قالب داده غیر رایج ‎IBM‎، است. یک کاربرد نا آشنای گزینه ‎conv=ebcdic‎ فرمان dd مانند یک کد کننده سریع و آسان فایل متن است، اما خیلی امن نیست.

cat $file | dd conv=swab,ebcdic > $file_encrypted
# .کد گذاری(نامفهوم به نظر می‌آید)‏
#بعلاوه، گزینه swab نیز شاید برای کمی ابهام اضافه.

cat $file_encrypted | dd conv=swab,ascii > $file_plaintext
# .خارج کردن از کد

[6]

یک macro یک ثابت نمادین است که به یک رشته فرمان یا مجموعه‌ای از عملیات روی پارامترها بسط می‌یابد. به سادگی آن را همچون یک میانبر یا مخفف تعبیر کنید.

یادداشت مترجم

[7]

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

bash$ ls -l
-rw-rw-r-- 1 bozo bozo    440 Jun 30 16:59 mkfifo-example.sh

ابتدا با استفاده از فرمان mkfifo دو لوله‌بانام pipe1 و pipe2 ایجاد می‌گردد. در این مرحله خواهیم داشت:

bash$ ls -l
-rw-rw-r-- 1 bozo bozo    440 Jun 30 16:59 mkfifo-example.sh
prw-rw-r-- 1 bozo bozo    0 Jun 30 17:57 pipe1
prw-rw-r-- 1 bozo bozo    0 Jun 30 17:57 pipe2

اما من در ادامه باز هم برای سهولت، این دو سطر اخیر خروجی را نیز نادیده خواهم گرفت و فرض می‌کنم خروجی فرمان ls در زمان اجرا همان یک سطر اول خواهد بود و توضیح را بر این فرض بنا می‌کنم.
بخش اصلی اسکریپت را دوسطر تشکیل می‌دهد:

#                  سطر اول
 (cut -d' ' -f1 | tr "a-z" "A-Z") >pipe2 <pipe1 &
#   سطر دوم (که به علت طول آن در دوسطر نوشته شده)
 ls -l | tr -s ' ' | cut -d' ' -f3,9- | tee pipe1 |
 cut -d' ' -f2 | paste - pipe2

در سطر نخست، بخش داخل پرانتز یک خط لوله را تشکیل می‌دهد که بواسطه ‎<pipe1‎ از pipe1 می‌خواند و بواسطه ‎>pipe2‎ در pipe2 می‌نویسد. با استفاده از کاراکتر &  این سطر در پس‌زمینه قرار داده شده که منتظر می‌ماند تا داده‌ای به لوله با نام pipe1 وارد شود و جریان یافته و از pipe2 خارج شود. اکنون تا قبل از اجرای سطر دوم در اینجا هیچ جریانی برقرار نیست و کار خاصی انجام نمی‌شود (خط لوله‌ای ایجاد شده ولی داده‌ای در آن جریان ندارد). بنابراین توضیح را از سطر دوم ادامه می‌دهیم:
سطر دوم با دستور ‎ls -l‎ آغاز می‌شود که قبلاً در مورد خروجی آن صحبت نموده‌ایم و فرض کردیم این خروجی فقط یک سطر باشد. این خروجی در ادامه سطر اول به فیلتر tr به صورت ‎tr -s ' '‎ لوله‌کشی شده است. کاربرد این فیلتر با این گزینه موجب می‌گردد تا در صورتی که کاراکتر نقل‌قول شده ِ پس از آن ،که در اینجا فاصله است، به طور متوالی در ورودی موجود باشد، موارد تکرار متوالی آن حذف شود (در اینجا از این طریق چند فاصله متوالی به یک فاصله تبدیل می‌شود چون در نظر است از فاصله به عنوان کاراکتر جدا کننده فیلد برای فرمان cut استفاده گردد، این کار لازم است).
اکنون آنچه حاصل شده ‎-rw-rw-r-- 1 bozo bozo 440 Jun 30 16:59 mkfifo-example.sh‎ است که به فیلتر cut به صورت ‎cut -d' ' -f3,9-‎ لوله‌کشی گردیده است. همانطور که می‌دانیم از فرمان cut برای استخراج فیلد استفاده می‌شود که گزینه ‎-d‎ برای تعیین جداکننده فیلد است و در اینجا کاراکتر فاصله را معین نموده. گزینه ‎-f‎ نیز برای مشخص کردن فیلد (در اینجا فیلد ۳ و ۹) است. بنابراین خروجی این فیلتر ‎bozo mkfifo-example.sh‎ خواهد بود.
تا اینجا کاملاً واضح و فاقد پیچیدگی بوده و به سادگی می‌توان با اجرای ls -l | tr -s ' ' | ‎cut -d' ' -f3,9-‎ در خط فرمان، نتیجه آن را مشاهده نمود، که چیزی غیر از لیست فایل‌های دایرکتوری شامل فیلد مالک و نام فایل نیست.
کاری که پس از این صورت خواهد گرفت، تبدیل فیلد اول به حروف بزرگ و جابجا نمودن این دو فیلد و به دست آوردن خروجی ‎mkfifo-example.sh BOZO‎ خواهد بود.
در اینجا با استفاده از فرمان tee‎ یک سه‌راهی ایجاد می‌گردد و جریان داده یعنی خروجی فیلتر قبلی از دو مسیر راه را ادامه می‌دهد:
راه اصلی و راه فرعی
راه فرعی tee pipe1‎ است. بنابراین ‎bozo mkfifo-example.sh‎ به pipe1 وارد می‌شود و به این ترتیب جریان در خط لوله‌ای که سطر اول اسکریپت تشکیل داده و در پس‌زمینه قرار دارد، برقرار گردیده و ورودی به ‎cut -d' ' -f1‎ می‌رسد که این فیلتر فیلد اول آن را جدا نموده و نتیجه را به ‎tr "a-z" "A-Z"‎ تحویل می‌دهد و این نیز حروف خروجی را به حروف بزرگ تبدیل می‌کند. نتیجه حاصله BOZO خواهد بود که بواسطه ‎>pipe2‎ در pipe2 نوشته می‌شود.
از طرفی در ادامه راه اصلی نتیجه حاصله در محل انشعاب یعنی ‎bozo mkfifo-example.sh‎ به ‎cut -d' ' -f2‎ تحویل می‌گردد که فیلد دوم آن را استخراج می‌کند و نتیجه یعنی ‎mkfifo-example.sh‎ را به ‎paste - pipe2‎ می‌دهد.
فرمان paste همانطور که قبلاً بیان گردیده "ابزاری برای ادغام فایل‌های مختلف با یکدیگر در یک فایل واحدِ چند ستونی" است. در اینجا برای فایل اول از -  استفاده گردیده که باعث می‌گردد ورودی استاندارد به عنوان فایل اول در نظر گرفته شود. بنابراین، دریافتی از ورودی استانداردش (که خروجی فرمان قبلی خط لوله است)، یعنی ‎mkfifo-example.sh‎ را با محتوای pipe2 (در انتهای راه فرعی) یعنی BOZO ادغام نموده نتیجه حاصل به صورت ‎mkfifo-example.sh BOZO‎ را در خروجی استاندارد ارایه می‌دهد.
البته در صورتی که خروجی فرمان ‎ls -l‎ به طور کامل در نظر گرفته شود، نتیجه به صورت زیر خواهد بود.

mkfifo-example.sh BOZO
pipe1 BOZO
pipe2 BOZO

چون هنگامی که اسکریپت دستور ‎ls -l‎ را اجرا می‌کند دو لوله با نام pipe1 و pipe2 نیز در دایرکتوری کاری ایجاد گردیده‌اند و در لیست‌گیری ظاهر می‌شوند.
چنانچه چندین فایل در دایرکتوری وجود داشت نیز تغییری در فرایندی که ذکر گردید حاصل نمی‌شد و فقط به ازای هر فایل یک سطر، شامل نام فایل و نام مالک آن به خروجی اضافه می‌گردید.