这些实用工具发出一个整数序列,具有用户可选择的增量。
每个整数之间的默认分隔符是一个换行符,但可以使用-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 # Using "seq" echo for a in `seq 80` # or for a in $( seq 80 ) # Same as for a in 1 2 3 4 5 ... 80 (saves much typing!). # May also use 'jot' (if present on system). do echo -n "$a " done # 1 2 3 4 5 ... 80 # Example of using the output of a command to generate # the [list] in a "for" loop. echo; echo COUNT=80 # Yes, 'seq' also accepts a replaceable parameter. for a in `seq $COUNT` # or 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` # Giving "seq" two arguments starts the count at the first one, #+ and continues until it reaches the second. 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` # Giving "seq" three arguments starts the count at the first one, #+ uses the second for a step interval, #+ and continues until it reaches the third. do echo -n "$a " done # 45 50 55 60 65 70 75 80 echo; echo exit 0 |
一个更简单的例子
# Create a set of 10 files, #+ named file.1, file.2 . . . file.10. COUNT=10 PREFIX=file for filename in `seq $COUNT` do touch $PREFIX.$filename # Or, can do other operations, #+ such as rm, grep, etc. done |
例 16-55. 字母计数"
#!/bin/bash # letter-count.sh: Counting letter occurrences in a text file. # Written by Stefano Palmeri. # Used in ABS Guide with permission. # Slightly modified by document author. MINARGS=2 # Script requires at least two arguments. E_BADARGS=65 FILE=$1 let LETTERS=$#-1 # How many letters specified (as command-line args). # (Subtract 1 from number of command-line args.) 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 } # Checks number of arguments. if [ $# -lt $MINARGS ]; then echo echo "Not enough arguments." echo show_help exit $E_BADARGS fi # Checks if file exists. if [ ! -f $FILE ]; then echo "File \"$FILE\" does not exist." exit $E_BADARGS fi # Counts letter occurrences . for n in `seq $LETTERS`; do shift if [[ `echo -n "$1" | wc -c` -eq 1 ]]; then # Checks arg. echo "$1" -\> `cat $FILE | tr -cd "$1" | wc -c` # Counting. else echo "$1 is not a single char." fi done exit $? # This script has exactly the same functionality as letter-count2.sh, #+ but executes faster. # Why? |
![]() | 与 seq 相比,jot 功能更强大一些,它是一个经典的 UNIX 实用程序,通常不包含在标准的 Linux 发行版中。但是,可以从 MIT 仓库下载源 rpm。 与 seq 不同,jot 可以使用-r选项进行更改。
|
getopt 命令解析以 短划线开头的命令行选项。此外部命令对应于 getopts Bash 内置命令。 使用 getopt 允许通过-l标志来处理长选项,这也允许参数重新排列。
例 16-56. 使用 getopt 解析命令行选项
#!/bin/bash # Using getopt # Try the following when invoking this script: # 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 # Explain the results of each of the above. E_OPTERR=65 if [ "$#" -eq 0 ] then # Script needs at least one command-line argument. echo "Usage $0 -[options a,b,c]" exit $E_OPTERR fi set -- `getopt "abcd:" "$@"` # Sets positional parameters to command-line arguments. # What happens if you use "$*" instead of "$@"? 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 # It is usually better to use the 'getopts' builtin in a script. # See "ex33.sh." exit 0 |
![]() | 正如 Peggy Russell 指出的那样
|
有关 getopt 的简化模拟,请参见例 10-5。
run-parts 命令 [1]按 ASCII 排序的文件名顺序依次执行目标目录中的所有脚本。 当然,脚本需要具有执行权限。
默认情况下,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(小心!)。
![]() | yes 命令解析变量,或者更准确地说,它回显解析后的变量。 例如
此特殊"功能"可用于动态创建非常大的 ASCII 文件
|
yes 命令可以使用一个非常简单的脚本函数来模拟。
yes () { # Trivial emulation of "yes" ... local DEFAULT_TEXT="y" while [ true ] # Endless loop. do if [ -z "$1" ] then echo "$DEFAULT_TEXT" else # If argument ... echo "$1" # ... expand and echo it. fi done # The only things missing are the } #+ --help and --version options. |
使用 ASCII 字符(默认为“#”)将参数打印为
stdout到打印机的大型垂直横幅。 这可以重定向到打印机以进行硬拷贝。
请注意,banner 已从许多 Linux 发行版中删除,大概是因为它不再被认为有用。
显示为特定用户设置的所有环境变量。
bash$ printenv | grep HOME HOME=/home/bozo |
lp 和 lpr 命令将文件发送到打印队列,以便打印成硬拷贝。 [2]这些命令将其名称追溯到另一个时代的行式打印机。 [3]
bash$lp file1.txt或者bashlp <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(用于从打印队列中删除作业)。
[UNIX 借鉴了管道行业的思想。]
这是一个重定向运算符,但有所不同。 像管道工的 三通管一样,它允许将管道中命令的输出分流到文件中,而不会影响结果。 这对于将正在进行的过程打印到文件或纸张上很有用,可能是为了跟踪它以进行调试。
(redirection) |----> to file | ==========================|==================== command ---> command ---> |tee ---> command ---> ---> output of pipe =============================================== |
cat listfile* | sort | tee check.file | uniq > result.file # ^^^^^^^^^^^^^^ ^^^^ # The file "check.file" contains the concatenated sorted "listfiles," #+ before the duplicate lines are removed by 'uniq.' |
此晦涩的命令创建一个命名管道,一个临时的先进先出缓冲区,用于在进程之间传输数据。 [4]通常,一个进程写入 FIFO,另一个进程从中读取。 请参阅 例 A-14。
#!/bin/bash # This short script by Omair Eshkenazi. # Used in ABS Guide with permission (thanks!). mkfifo pipe1 # Yes, pipes can be given names. mkfifo pipe2 # Hence the designation "named pipe." (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 # No need to kill background processes when script terminates (why not?). exit $? Now, invoke the script and explain the output: sh mkfifo-example.sh 4830.tar.gz BOZO pipe1 BOZO pipe2 BOZO mkfifo-example.sh BOZO Mixed.msg BOZO |
此命令检查文件名的有效性。 如果文件名超过最大允许长度(255 个字符)或者其路径中的一个或多个目录不可搜索,则会产生错误消息。
不幸的是,pathchk 不会返回可识别的错误代码,因此它在脚本中几乎毫无用处。 请考虑使用 文件测试运算符。
虽然这个有些晦涩且令人恐惧的data duplicator 命令最初是作为在 UNIX 小型计算机和 IBM 大型机之间交换磁带数据的实用程序而出现的,但它仍然有其用途。 dd 命令只是复制文件(或stdin/stdout),但具有转换功能。 可能的转换包括 ASCII/EBCDIC,[5]大写/小写,在输入和输出之间交换字节对,以及跳过和/或截断输入文件的头部或尾部。
# Converting a file to all uppercase: dd if=$filename conv=ucase > $filename.uppercase # lcase # For lower case conversion |
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 数据的转换类型。
一个dd --help列出了此强大实用程序的所有选项。
例 16-57. 复制自身的脚本
#!/bin/bash # self-copy.sh # This script copies itself. file_subscript=copy dd if=$0 of=$0.$file_subscript 2>/dev/null # Suppress messages from dd: ^^^^^^^^^^^ exit $? # A program whose only output is its own source code #+ is called a "quine" per Willard Quine. # Does this script qualify as a quine? |
例 16-58. 练习 dd
#!/bin/bash # exercising-dd.sh # Script by Stephane Chazelas. # Somewhat modified by ABS Guide author. infile=$0 # This script. outfile=log.txt # Output file left behind. n=8 p=11 dd if=$infile of=$outfile bs=1 skip=$((n-1)) count=$((p-n+1)) 2> /dev/null # Extracts characters n to p (8 to 11) from this script ("bash"). # ---------------------------------------------------------------- echo -n "hello vertical world" | dd cbs=1 conv=unblock 2> /dev/null # Echoes "hello vertical world" vertically downward. # Why? A newline follows each character dd emits. exit $? |
为了演示 dd 的多功能性,让我们使用它来捕获击键。
例 16-59. 捕获击键
#!/bin/bash # dd-keypress.sh: Capture keystrokes without needing to press ENTER. keypresses=4 # Number of keypresses to capture. old_tty_setting=$(stty -g) # Save old terminal settings. echo "Press $keypresses keys." stty -icanon -echo # Disable canonical mode. # Disable local echo. keys=$(dd bs=1 count=$keypresses 2> /dev/null) # 'dd' uses stdin, if "if" (input file) not specified. stty "$old_tty_setting" # Restore old terminal settings. echo "You pressed the \"$keys\" keys." # Thanks, Stephane Chazelas, for showing the way. exit 0 |
dd 命令可以对数据流进行随机访问。
echo -n . | dd bs=1 seek=4 of=file conv=notrunc # The "conv=notrunc" option means that the output file #+ will not be truncated. # Thanks, S.C. |
dd 命令可以将原始数据和磁盘映像复制到设备(例如软盘和磁带驱动器)以及从设备复制数据和磁盘映像(例 A-5)。 一种常见的用途是创建启动软盘。
dd if=kernel-image of=/dev/fd0H1440
同样,dd 也可以将软盘的全部内容(即使是使用"外部" OS 格式化的软盘)作为映像文件复制到硬盘驱动器。
dd if=/dev/fd0 of=/home/bozo/projects/floppy.img
dd if=image.iso of=/dev/sdb
例 16-60. 为 Raspberry Pi 准备可启动的 SD 卡
#!/bin/bash # rp.sdcard.sh # Preparing an SD card with a bootable image for the Raspberry Pi. # $1 = imagefile name # $2 = sdcard (device file) # Otherwise defaults to the defaults, see below. DEFAULTbs=4M # Block size, 4 mb default. DEFAULTif="2013-07-26-wheezy-raspbian.img" # Commonly used distro. DEFAULTsdcard="/dev/mmcblk0" # May be different. Check! ROOTUSER_NAME=root # Must run as root! E_NOTROOT=81 E_NOIMAGE=82 username=$(id -nu) # Who is running this script? 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 $? # Exercises: # --------- # 1) Provide additional error checking. # 2) Have script autodetect device file for SD card (difficult!). # 3) Have script sutodetect image file (*img) in $PWD. |
dd 的其他应用包括初始化临时交换文件(例 31-2)和 ramdisk(例 31-3)。 它甚至可以对整个硬盘分区进行低级复制,尽管不一定建议这样做。
人们(大概是没有其他事情可做)一直在思考 dd 的有趣应用。
例 16-61. 安全地删除文件
#!/bin/bash # blot-out.sh: Erase "all" traces of a file. # This script overwrites a target file alternately #+ with random bytes, then zeros before finally deleting it. # After that, even examining the raw disk sectors by conventional methods #+ will not reveal the original file data. PASSES=7 # Number of file-shredding passes. # Increasing this slows script execution, #+ especially on large target files. BLOCKSIZE=1 # I/O with /dev/urandom requires unit block size, #+ otherwise you get weird results. E_BADARGS=70 # Various error exit codes. E_NOT_FOUND=71 E_CHANGED_MIND=72 if [ -z "$1" ] # No filename specified. 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}') # Field 5 is file length. pass_count=1 chmod u+w "$file" # Allow overwriting/deleting the file. echo while [ "$pass_count" -le "$PASSES" ] do echo "Pass #$pass_count" sync # Flush buffers. dd if=/dev/urandom of=$file bs=$BLOCKSIZE count=$flength # Fill with random bytes. sync # Flush buffers again. dd if=/dev/zero of=$file bs=$BLOCKSIZE count=$flength # Fill with zeros. sync # Flush buffers yet again. let "pass_count += 1" echo done rm -f $file # Finally, delete scrambled and shredded file. sync # Flush buffers a final time. echo "File \"$file\" blotted out and deleted."; echo exit 0 # This is a fairly secure, if inefficient and slow method #+ of thoroughly "shredding" a file. # The "shred" command, part of the GNU "fileutils" package, #+ does the same thing, although more efficiently. # The file cannot not be "undeleted" or retrieved by normal methods. # However . . . #+ this simple method would *not* likely withstand #+ sophisticated forensic analysis. # This script may not play well with a journaled file system. # Exercise (difficult): Fix it so it does. # Tom Vier's "wipe" file-deletion package does a much more thorough job #+ of file shredding than this simple script. # http://www.ibiblio.org/pub/Linux/utils/file/wipe-2.0.0.tar.bz2 # For an in-depth analysis on the topic of file deletion and security, #+ see Peter Gutmann's paper, #+ "Secure Deletion of Data From Magnetic and Solid-State Memory". # http://www.cs.auckland.ac.nz/~pgut001/pubs/secure_del.html |
od 或 八进制转储过滤器将输入(或文件)转换为八进制(以 8 为基数)或其他基数。 这对于查看或处理二进制数据文件或以其他方式无法读取的系统设备文件很有用,例如/dev/urandom,并用作二进制数据的过滤器。
head -c4 /dev/urandom | od -N4 -tu4 | sed -ne '1s/.* //p' # Sample output: 1324725719, 3918166450, 2989231420, etc. # From rnd.sh example script, by St�phane Chazelas |
执行二进制文件的十六进制、八进制、十进制或 ASCII 转储。 此命令大致相当于上面的 od,但不如 od 有用。 可以与 dd 和 less 结合使用以查看二进制文件的内容。
dd if=/bin/ls | hexdump -C | less # The -C option nicely formats the output in tabular form. |
以十六进制形式或作为反汇编列表(使用-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 . . . |
此命令生成一个“魔法饼干”,一个 128 位(32 个字符)的伪随机十六进制数,通常用作 X 服务器的授权“签名”。 这也可以在脚本中用作"快速'n 肮脏"随机数。
random000=$(mcookie) |
当然,脚本也可以使用 md5sum 来达到同样的目的。
# Generate md5 checksum on the script itself. random001=`md5sum $0 | awk '{print $1}'` # Uses 'awk' to strip off the filename. |
mcookie 命令提供了另一种生成 "唯一" 文件名的方法。
例 16-62. 文件名生成器
#!/bin/bash # tempfile-name.sh: temp filename generator BASE_STR=`mcookie` # 32-character magic cookie. POS=11 # Arbitrary position in magic cookie string. LEN=5 # Get $LEN consecutive characters. prefix=temp # This is, after all, a "temp" file. # For more "uniqueness," generate the #+ filename prefix using the same method #+ as the suffix, below. suffix=${BASE_STR:POS:LEN} # Extract a 5-character string, #+ starting at position 11. temp_filename=$prefix.$suffix # Construct the filename. echo "Temp filename = "$temp_filename"" # sh tempfile-name.sh # Temp filename = temp.e19ea # Compare this method of generating "unique" filenames #+ with the 'date' method in ex51.sh. exit 0 |
这个实用程序可以在不同的计量单位之间进行转换。 虽然通常以交互模式调用,但 units 可以在脚本中使用。
例 16-63. 将米转换为英里
#!/bin/bash # unit-conversion.sh # Must have 'units' utility installed. convert_units () # Takes as arguments the units to convert. { cf=$(units "$1" "$2" | sed --silent -e '1p' | awk '{print $2}') # Strip off everything except the actual conversion factor. 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." # What happens if you pass incompatible units, #+ such as "acres" and "miles" to the function? exit 0 # Exercise: Edit this script to accept command-line parameters, # with appropriate error checking, of course. |
m4 是一个隐藏的宝藏,是一个强大的宏 [6] 处理过滤器,实际上是一种完整的语言。 虽然最初是作为 RatFor 的预处理器编写的,但 m4 证明可以作为独立的实用程序使用。 事实上,除了其广泛的宏扩展功能之外,m4 还结合了 eval、tr 和 awk 的一些功能。
Linux Journal 的 2002 年 4 月刊上发表了一篇关于 m4 及其用途的非常好的文章。
这个基于 X 的 echo 变体会在桌面上弹出一个消息/查询窗口。
xmessage Left click to continue -button okay |
doexec 命令允许将任意参数列表传递给二进制可执行文件。 特别是,传递argv[0](它对应于脚本中的 $0) 允许通过各种名称调用可执行文件,然后它可以根据调用的名称执行不同的操作集。 这相当于一种间接的方式将选项传递给可执行文件。
例如,/usr/local/bin目录可能包含一个名为 "aaa" 的二进制文件。 调用 doexec /usr/local/bin/aaa list 将列出当前工作目录中所有以 "a" 开头的文件,而调用(使用相同的可执行文件)doexec /usr/local/bin/aaa delete 将删除这些文件。
![]() | 可执行文件的各种行为必须在可执行文件本身的代码中定义,类似于 shell 脚本中以下内容
|
dialog 工具系列提供了一种从脚本调用交互式 "dialog" 框的方法。 更复杂的 dialog 变体 -- gdialog、Xdialog 和 kdialog -- 实际上会调用 X-Windows widgets。
sox,或 "sound exchange" 命令播放并对声音文件执行转换。 事实上,/usr/bin/play可执行文件(现在已弃用)只是 sox 的一个 shell 包装器。
例如,sox soundfile.wav soundfile.au 将 WAV 声音文件更改为(Sun 音频格式)AU 声音文件。
Shell 脚本非常适合批量处理 sox 操作。 有关示例,请参见 Linux Radio Timeshift HOWTO 和 MP3do 项目。
[1] | 这实际上是一个从 Debian Linux 发行版改编的脚本。 | |
[2] | 打印队列是"排队等待"打印的作业组。 | |
[3] | 大型机械行式打印机一次在一张连接的 绿条纸上打印一行类型,并伴随着 很大的噪音。 如此打印的硬拷贝被称为打印输出。 | |
[4] | 有关此主题的精彩概述,请参见 Andy Vaught 在 Linux Journal 1997 年 9 月刊上发表的文章 命名管道简介。 | |
[5] | EBCDIC(发音为 "ebb-sid-ick")是 Extended Binary Coded Decimal Interchange Code(扩展二进制编码十进制交换码)的缩写,这是一种过时的 IBM 数据格式。 的一个奇怪的应用conv=ebcdicdd 的选项是作为一种快速简便但不是很安全的文本文件编码器。
| |
[6] | 宏是一个符号常量,它扩展为命令字符串或对参数的一组操作。 简单地说,它是一个快捷方式或缩写。 |