【问题标题】:File names with spaces in BASHBASH 中带空格的文件名
【发布时间】:2011-04-27 10:21:54
【问题描述】:

我正在尝试编写将大照片裁剪并调整为高清壁纸的脚本。

#! /bin/bash


for i in `ls *.jpg`
do
    width=`identify -format '%w' $i`
    height=`identify -format '%h' $i`

    if [ `echo "$width/$height > 16/9" | bc -l` ]
    then
        exec `convert $i -resize 1920 -gravity Center -crop '1920x1080+0+0' +repage temp`
    else
        exec `convert $i -resize x1080 -gravity Center -crop 1920x1080+0+0 +repage temp`
    fi

    rm $i
    mv temp $i
done

但似乎该脚本在文件名中有空格(如 Tumble Weed.jpg)方面存在问题。我该如何解决这个问题?

【问题讨论】:

  • 答案已经在下面给出了,但我想补充一点,shell 脚本对于处理文件名中的空格真的很糟糕,因为很多列表都是用空格分隔的——尤其是命令参数。例如,尝试通过 scp 复制带空格的文件!没有手动转义几乎是不可能的。
  • 您应该完整阅读http://mywiki.wooledge.org/BashPitfalls :-) 它会大大提高您的 bash 技能

标签: linux bash


【解决方案1】:

在 bash 中,使用 find 替换字符串:

${string//substring/replacement}
将$substring 的所有匹配项替换为$replacement。

所以这行得通:

find . -type f -name '*.jpg' | while read i ; do /bin/mv -f "$i" ${i// /_}; done

【讨论】:

    【解决方案2】:

    使用 read 来规避空格问题。像这样写循环看起来有点不自然,但效果更好:

    find . -type f -iname "*.jpg" | while read i
    do
        # your original code inside the loop using "$i" instead of $i
    done
    

    使用-iname,您还可以获得可能具有不同大小写扩展名的 jpg 文件,例如 .JPG。 “iname”中的“i”表示忽略大小写。

    【讨论】:

    • 事实上,这会失败,名称带有文字反斜杠、带有文字换行符的名称以及以空格结尾的名称。
    • 要修复它,将-print0 添加到find,并使shell 以while IFS= read -r -d '' i 结尾; -r 修复了反斜杠,-d '' 和 -print0 修复了文字换行符,IFS= 修复了对以空格结尾的名称的支持。
    【解决方案3】:
     #! /bin/bash 
     mkfifo ./lsOutput
     ls -1 *.jpg > ./lsOutput
        while read line  
        do 
    
        width=`identify -format '%w' "$line"` 
        height=`identify -format '%h' "$line"` 
    
        if [ `echo "$width/$height > 16/9" | bc -l` ] 
        then 
            exec `convert "$line" -resize 1920 -gravity Center -crop '1920x1080+0+0' +repage temp` 
        else 
            exec `convert "$line" -resize x1080 -gravity Center -crop 1920x1080+0+0 +repage temp` 
        fi 
    
        rm "$line"
        mv temp "$line"
     done<./lsOutput
    

    【讨论】:

    • 可以对流和 FIFO 进行一些解释。我喜欢 read 命令只查找 '\n' 来获取字符串。
    【解决方案4】:

    我建议这样写 for-line:

    for i in *.jpg
    

    并将$i 封装在双引号中:"$i"。


    如果你坚持

    `ls *.jpg`
    

    样式,(例如,如果您从更复杂的命令中获取文件名)您可以尝试将 IFS 设置为 \n:

    IFS='\n'
    

    比较这两种执行方式:

    $ for f in `ls *`; do echo $f; done
    hello
    world
    test
    
    $ IFS='\n'; for f in `ls *`; do echo $f; done
    hello world
    test
    

    【讨论】:

      【解决方案5】:

      首先,您不需要ls。通过在 backtics 中使用 ls,您可以隐式地使 bash 将字符串解析为一个列表,该列表由空格分隔。相反,让 bash 生成列表并在没有此类怪癖的情况下将其分开:

      此外,您需要将所有 $i 用法括在引号中,以使 bash 将其作为一个整体替换,而不是作为拆分为单独单词的字符串。

      以下是演示这两种想法的脚本:

      for i in *.jpg ; do 
        echo "$i";
      done
      

      【讨论】:

      • 这不总是有效,因此具有误导性。这总是有效的。 SAVEIFS=$IFS;IFS=$(echo -en "\n\b");for f in $(ls -lah);do echo "$f" done;IFS=$SAVEIFS
      【解决方案6】:

      在文件名周围使用双引号。像这样:

      width=`identify -format '%w' "$i"`
      

      请注意"$i"周围的双引号。

      【讨论】:

        猜你喜欢
        • 2010-12-07
        • 1970-01-01
        • 1970-01-01
        • 2012-04-08
        • 2017-10-18
        • 2011-01-24
        • 1970-01-01
        相关资源
        最近更新 更多