Blog:博客园 个人

译自BashPitfalls

本文总结了编写Shell脚本中的常见错误。

for f in $(ls *.mp3)

最常犯的错之一就是编写这样的循环:

for f in $(ls *.mp3); do    # Wrong!
some command $f # Wrong!
done for f in $(ls) # Wrong!
for f in `ls` # Wrong! for f in $(find . -type f) # Wrong!
for f in `find . -type f` # Wrong! files=($(find . -type f)) # Wrong!
for f in ${files[@]} # Wrong!

确实,如果可以将ls的输出或者find作为文件名列表并对其进行迭代,看起来确实没啥问题。但是,这类方法是有缺陷的。

比如:

  • 如果文件名包含空格,for循环会将空格也分割(默认IFS为空格、\n、``\t),例如01 - Don't Eat the Yellow Snow.mp3for循环会分割成01-Don'tEattheYellowSnow.mp3`。
  • 如果文件名包含glob字符(例如*),包含它的单词将被识别为模式并用与其匹配的所有文件名列表替换。
  • 如果命令替换返回多个文件名,则无法区分第一个文件名的结束位置和第二个文件名的开始位置。路径名可以包含除NUL之外的任何字符。是的,这包括换行符。
  • ls实用程序可能会损坏文件名。根据您使用的平台、使用(或未使用)的参数以及其标准输出是否指向终端,ls可能会随机决定将文件名中的某些字符替换为“?”,或者干脆不打印它们。永远不要尝试解析ls的输出。ls完全是不必要的。它是一个外部命令,其输出专门供人读取,而不是由脚本解析。
  • 命令替代(Command Substitution)从其输出中剥离所有尾随换行符。这看起来可能是可取的,因为ls添加了一个换行符,但是如果列表中的最后一个文件名以换行符结束,则命令替代会删除换行符。

正确做法:

for file in ./*.mp3; do    # Better! and…
some command "$file" # …always double-quote expansions!
done

cp $file $target

如果 $file$target中有空格(如果没有修改$IFS),cp $file $target执行会报错,例如复制文件01 - Don't Eat the Yellow Snow.mp3/mn/usb

cp 01 - Don't Eat the Yellow Snow.mp3 /mnt/usb

会报以下错误:

cp: cannot stat ‘01’: No such file or directory
cp: cannot stat ‘-’: No such file or directory
cp: cannot stat ‘Don't’: No such file or directory
cp: cannot stat ‘Eat’: No such file or directory
cp: cannot stat ‘the’: No such file or directory
cp: cannot stat ‘Yellow’: No such file or directory
cp: cannot stat ‘Snow.mp3’: No such file or directory

正确做法:

cp -- "$file" "$target"

强烈建议:引用变量的时候,一定要加双引号。

Filenames with leading dashes

如果文件名带有-,命令可能错误把它当作参数。

解决的方法之一是,在变量前面加--,例如:

cp -- "$file" "$target"

--是告诉命令,停止扫描参数。

注意:此方法的潜在问题,必须确保每条命令都要插入--,这很容易遗漏。

还有一种方法是使用相对路径或者绝对路径。例如:

for i in ./*.mp3; do
cp "$i" /target

done

在这种情况下,即使开头包含-的文件,也可以确保变量始终包含类似./-foo.mp3的文件,这样就比较安全。

[ $foo = "bar" ]

如果[中引用的变量不存在或为空,则[命令最终将如下所示:

[ = "bar" ] # Wrong!

Tips:=是二元一次运算符,不是一元一次运算符。

如果变量包含内部空格,则会在[命令看到它之前将其拆分成单独的单词,例如:

[ multiple words here = "bar" ]

看起来没啥问题,但在[]语法中是错误的,正确方式是加上双引号:

# POSIX
[ "$foo" = bar ] # Right! # Bash / Ksh
[[ $foo == bar ]] # Right!

cd $(dirname "$f")

这也是引用错误。正确做法:

cd -P -- "$(dirname -- "$f")"

Tips:-p参数是递归处理,将指定目录下的所有文件与子目录一并处理。

最新文章

  1. html5、canvas绘制本地时钟
  2. 【WP 8.1开发】上下文菜单
  3. JS自定义属性兼容
  4. Python编程中的反模式
  5. 利用二维矩阵求spanning tree
  6. popViewControllerAnimated 后,对页面内UITableView 内数据刷新
  7. PHP 递归创建目录
  8. 【Java】理解 UDDI
  9. AndroidUI 视图动画-自定义动画效果 (Animation)
  10. 通过virtualbox最小化安装centos 6.3后无法上网解决办法
  11. kaptcha 验证码组件使用
  12. MFC CDHtmlDialog 加载本地资源
  13. 自动化部署--shell脚本--3
  14. Java 11 - Java String类
  15. 由初始化线程池引发的NoClassDefFoundError 异常分析
  16. Tomcat 8
  17. 【GIS】postgres(postgis) --》nodejs+express --》geojson --》leaflet
  18. 使用mothur进行OTU聚类
  19. linux 搭建 redis
  20. #leetcode刷题之路36-有效的数独

热门文章

  1. 【Linux/Oracle】ORA-00031:标记要终止的会话 解决
  2. 2021美团安洵暗泉re部分复现
  3. C#设置进程PATH环境变量值解决某些Win32DLL找不到路径问题
  4. rabbit-vue3-ts-小兔鲜儿2022新版-系列开篇
  5. 《深度探索C++对象模型》第二章 | 构造函数语意学
  6. windbg调试系列教程:sos扩展的介绍和使用
  7. Ansible-DevOps
  8. 浅谈C#字符串构建利器StringBuilder
  9. Windows系统散列值获取分析与防范
  10. 记一次腾讯云服务器centos linux可视化桌面安装并进行远程桌面登录及其安装中文包