Bourne Shell 脚本/附录 D: 食谱
发布新的食谱条目 |
如果您使用标题框,则无需在正文中放置标题。 |
在编写应根据文件扩展名执行不同操作的 bash 脚本时,以下模式很有用。
#filepath should be set to the name(with optional path) of the file in question
ext=${filepath##*.}
if [[ "$ext" == txt ]] ; then
#do something with text files
fi
(来源:slike.com Bash FAQ).
此食谱演示了如何按照模式重命名多个文件。
在这个例子中,用户有大量的屏幕截图。该用户想要使用 Bourne 兼容的 shell 重命名这些文件。这是一个在 shell 提示符下显示文件名的 "ls"。目标是将像 "snapshot1.png" 这样的图像重命名为 "nethack-kernigh-22oct2005-01.png"。
$ ls
snapshot1.png snapshot25.png snapshot40.png snapshot56.png snapshot71.png
snapshot10.png snapshot26.png snapshot41.png snapshot57.png snapshot72.png
snapshot11.png snapshot27.png snapshot42.png snapshot58.png snapshot73.png
snapshot12.png snapshot28.png snapshot43.png snapshot59.png snapshot74.png
snapshot13.png snapshot29.png snapshot44.png snapshot6.png snapshot75.png
snapshot14.png snapshot3.png snapshot45.png snapshot60.png snapshot76.png
snapshot15.png snapshot30.png snapshot46.png snapshot61.png snapshot77.png
snapshot16.png snapshot31.png snapshot47.png snapshot62.png snapshot78.png
snapshot17.png snapshot32.png snapshot48.png snapshot63.png snapshot79.png
snapshot18.png snapshot33.png snapshot49.png snapshot64.png snapshot8.png
snapshot19.png snapshot34.png snapshot5.png snapshot65.png snapshot80.png
snapshot2.png snapshot35.png snapshot50.png snapshot66.png snapshot81.png
snapshot20.png snapshot36.png snapshot51.png snapshot67.png snapshot82.png
snapshot21.png snapshot37.png snapshot52.png snapshot68.png snapshot83.png
snapshot22.png snapshot38.png snapshot53.png snapshot69.png snapshot9.png
snapshot23.png snapshot39.png snapshot54.png snapshot7.png
snapshot24.png snapshot4.png snapshot55.png snapshot70.png
首先,要在快照 1 到 9 之前添加 "0"(零),编写一个 for 循环(实际上是一个简短的 shell 脚本)。
- 使用?这是一个单个字符的文件名模式。使用它,我可以匹配快照 1 到 9,但可以通过说来错过 10 到 83snapshot?.png.
- 使用${参数#模式}用从开头删除的模式替换参数的值。这是为了去掉 "snapshot",以便我可以放入 "snapshot0"。
- 在实际运行循环之前,插入一个 "echo" 来测试命令是否正确。
$ for i in snapshot?.png; do echo mv "$i" "snapshot0${i#snapshot}"; done
mv snapshot1.png snapshot01.png
mv snapshot2.png snapshot02.png
mv snapshot3.png snapshot03.png
mv snapshot4.png snapshot04.png
mv snapshot5.png snapshot05.png
mv snapshot6.png snapshot06.png
mv snapshot7.png snapshot07.png
mv snapshot8.png snapshot08.png
mv snapshot9.png snapshot09.png
看起来不错,所以通过删除 "echo" 来运行它。
$ for i in snapshot?.png; do mv "$i" "snapshot0${i#snapshot}"; done
一个 ls 证实了这很有效。
现在将前缀 "snapshot" 更改为 "nethack-kernigh-22oct2005-"。运行一个类似于上一个循环的循环
$ for i in snapshot*.png; do \
> mv "$i" "nethack-kernigh-22oct2005-${i#snapshot}" \
> done
这为用户节省了键入 83 个 "mv" 命令的麻烦。
内置的getopts不支持长选项,因此需要外部的getopt是必需的。(在某些系统上,getopt 也不支持长选项,因此下一个示例将无法正常工作。)
eval set -- $(getopt -l install-opts: "" "$@")
while true; do
case "$1" in
--install-opts)
INSTALL_OPTS=$2
shift 2
;;
--)
shift
break
;;
esac
done
echo $INSTALL_OPTS
对getopt引用并重新排序在$@. set中找到的命令行参数,然后用$@的输出替换getopt
在高级 Bash 脚本指南中也可以找到 getopt 使用的另一个示例
在这个食谱中,我们想要处理一个大型文件列表,但我们必须为每个文件运行一个命令。在这个例子中,我们想要将一些声音文件的采样率转换为 44100 赫兹。命令是sox file.ogg -r 44100 conv/file.ogg,它将file.ogg转换为一个新文件conv/file.ogg。我们还想要跳过已经为 44100 赫兹的文件。
首先,我们需要我们文件的采样率。一种方法是使用file命令
$ file *.ogg
audio_on.ogg: Ogg data, Vorbis audio, mono, 44100 Hz, ~80000 bps
beep_1.ogg: Ogg data, Vorbis audio, stereo, 44100 Hz, ~193603 bps
cannon_1.ogg: Ogg data, Vorbis audio, mono, 48000 Hz, ~96000 bps
...
(此示例中的文件来自 秘密玛丽奥编年史。) 我们可以使用grep -v过滤掉所有包含 '44100 Hz' 的行
$ file *.ogg | grep -v '44100 Hz'
cannon_1.ogg: Ogg data, Vorbis audio, mono, 48000 Hz, ~96000 bps
...
jump_small.ogg: Ogg data, Vorbis audio, mono, 8000 Hz, ~22400 bps
live_up.ogg: Ogg data, Vorbis audio, mono, 22050 Hz, ~40222 bps
...
我们使用 "grep" 和 "file" 完成了,所以现在我们想要删除其他信息,只留下要传递给 "sox" 的文件名。我们使用文本实用程序cut。选项-d将每行在冒号处分成字段;-f1选择第一个字段。
$ file *.ogg | grep -v '44100 Hz' | cut -d: -f1
cannon_1.ogg
...
jump_small.ogg
live_up.ogg
...
我们可以使用另一个管道来提供标准输入上的文件名,但 "sox" 将它们作为参数。我们使用xargs,它将使用来自标准输入的参数重复运行命令。该-n1选项指定每个命令一个参数。例如,我们可以运行echo sox重复
$ file *.ogg | grep -v '44100 Hz' | cut -d: -f1 | xargs -n1 echo sox
sox cannon_1.ogg
...
sox itembox_set.ogg
sox jump_small.ogg
...
但是,这些命令是错误的。例如,cannon_1.ogg 的完整命令是sox cannon_1.ogg -r 44100 conv/cannon_1.ogg。"xargs" 将插入传入数据到由 " {} " 表示的占位符中。我们在此管道中使用此策略。如果有疑问,首先我们可以使用 "echo" 构建一个测试管道
$ file *.ogg | grep -v '44100 Hz' | cut -d: -f1 | \
> xargs -i 'echo sox {} -r 44100 conv/{}'
sox cannon_1.ogg -r 44100 conv/cannon_1.ogg
...
sox itembox_set.ogg -r 44100 conv/itembox_set.ogg
sox jump_small.ogg -r 44100 conv/jump_small.ogg
...
它起作用了,所以让我们删除 "echo" 并运行 "sox" 命令
$ mkdir conv
$ file *.ogg | grep -v '44100 Hz' | cut -d: -f1 | \
> xargs -i 'sox {} -r 44100 conv/{}'
等待一段时间后,转换后的文件将出现在conv子目录中。以上三行代码单独完成了整个转换。
如果您有 GStreamer,则命令gst-launch filesrc location=filename ! decodebin ! audioconvert ! esdsink将播放任何格式的声音或音乐文件,只要您拥有 GStreamer 插件。此脚本将播放文件列表,可以选择循环播放它们。(将 "esdsink" 替换为您最喜欢的接收器。)
#!/bin/sh
loop=false
if test x"$1" == x-l; then
loop=true
shift
fi
while true; do
for i in "$@"; do
if test -f "$i"; then
echo "${0##*/}: playing $i" > /dev/stderr
gst-launch filesrc location="$i" ! decodebin ! audioconvert ! esdsink
else
echo "${0##*/}: not a file: $i" > /dev/stderr
fi
done
if $loop; then true; else break; fi
done
此脚本演示了一些常见的 Bourne shell 策略
- "loop" 是一个布尔变量。它之所以有效是因为它的值 "true" 和 "false" 都是 Unix 命令(有时也是 shell 内置命令),因此您可以在if和while语句中使用它们作为条件。
- shell 内置命令 "shift" 从参数列表中删除 $1,从而将 $2 移到 $1,将 $3 移到 $2,等等。此脚本使用它来处理 "-l" 选项。
- 替换${0##*/}提供 $0 中最后一个斜线后的所有内容,因此是 "playlist",而不是 "/home/musicfan/bin/playlist"。