• Shell:常见错误总结(一)


    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参数是递归处理,将指定目录下的所有文件与子目录一并处理。


    __EOF__

  • 本文作者: 时间之旅
  • 本文链接: https://www.cnblogs.com/Rohn/p/16048766.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    【面试系列】Java面试知识篇(一)
    【Flocking算法】海王的鱼塘是怎样炼成的
    IO进程及相关函数
    用Python抓取京东商品评论
    八、kotlin的高阶函数
    Linux--进程--进程-父进程退出
    Ansible最佳实践之委派任务和事实
    机器学习——实践
    开放式耳机百元价位怎么选、公认最好的百元开放式耳机
    【ROS2知识】将urdf用在Gazebo上
  • 原文地址:https://www.cnblogs.com/Rohn/p/16048766.html