在bash脚本中运行带空格字符的命令

时间:2011-01-10 00:15:01

标签: bash shell imagemagick

我有一个包含文件列表的文件:

02 of Clubs.eps
02 of Diamonds.eps
02 of Hearts.eps
02 of Spades.eps
...

我正在尝试将这些转换为多种尺寸的png格式。我用来做这个的脚本是:

while read -r line
do
    for i in 80 35 200
    do
        convert $(sed 's/ /\\ /g' <<< Cards/${line}) -size ${i}x${i} ../img/card/$(basename $(tr ' ' '_' <<< ${line} | tr '[A-Z]' '[a-z]') .eps)_${i}.png;
    done
done < card_list.txt

然而,这不起作用,显然试图拆分每个单词,导致以下错误输出:

convert: unable to open image `Cards/02\': No such file or directory @ error/blob.c/OpenBlob/2514.
convert: no decode delegate for this image format `Cards/02\' @ error/constitute.c/ReadImage/532.
convert: unable to open image `of\': No such file or directory @ error/blob.c/OpenBlob/2514.
convert: no decode delegate for this image format `of\' @ error/constitute.c/ReadImage/532.
convert: unable to open image `Clubs.eps': No such file or directory @ error/blob.c/OpenBlob/2514.

如果我将转换更改为echo,结果看起来正确,如果我复制一行并在shell中自己运行它可以正常工作:

convert Cards/02\ of\ Clubs.eps -size 80x80 ../img/card/02_of_clubs_80.png
convert Cards/02\ of\ Clubs.eps -size 35x35 ../img/card/02_of_clubs_35.png
convert Cards/02\ of\ Clubs.eps -size 200x200 ../img/card/02_of_clubs_200.png
convert Cards/02\ of\ Diamonds.eps -size 80x80 ../img/card/02_of_diamonds_80.png
convert Cards/02\ of\ Diamonds.eps -size 35x35 ../img/card/02_of_diamonds_35.png
convert Cards/02\ of\ Diamonds.eps -size 200x200 ../img/card/02_of_diamonds_200.png
convert Cards/02\ of\ Hearts.eps -size 80x80 ../img/card/02_of_hearts_80.png
convert Cards/02\ of\ Hearts.eps -size 35x35 ../img/card/02_of_hearts_35.png
convert Cards/02\ of\ Hearts.eps -size 200x200 ../img/card/02_of_hearts_200.png
convert Cards/02\ of\ Spades.eps -size 80x80 ../img/card/02_of_spades_80.png

更新

只是添加引号(见下文)与上面的结果相同,我一直在使用sed添加反斜杠

convert '"'Cards/${line}'"' -size ${i}x${i} ../img/card/$(basename $(tr ' ' '_' <<< ${line} | tr '[A-Z]' '[a-z]') .eps)_${i}.png;

我尝试了双引号和单引号

3 个答案:

答案 0 :(得分:3)

Shell可以正常分割行,并使用变量使代码可读 - 避免使用水平滚动条...

while read -r line
do
    for i in 80 35 200
    do
        epsfile="Cards/$line"
        pngbase=$(basename "$line" .eps | tr ' A-Z' '_a-z')
        pngfile="../img/card/${pngbase}_${i}.png"
        convert "$epsfile" -size ${i}x${i} "$pngfile"
    done
done < card_list.txt

如果必须处理包含空格的文件名,则需要在将名称传递给命令时将其用双引号括起来。将所有文件操作压缩成一行的复杂代码几乎无法读取。您可以在一个命令中完成整个音译,如上所示,这也简化了事情。

即使80个字符不是硬限制,但值得将其保持为粗略限制,因为如果行更长,则可能无法读取,并且代码必须可读且易于维护。

答案 1 :(得分:2)

只需引用带有空格或其他特殊字符的文件名即可。此外,您不需要sed或tr。

请参阅关于“变量替换”的bash文档(pps 21-22,bash Pocket Reference,O'Reilly)

通过将转换逻辑放入函数

来简化循环
# cvt file size 
cvt() {
 infile="$1"  # quote in case filename has blanks
 size="$2"
 outfile="${infile/ /_}"              # replace spaces with underlines
 outfile="${outfile##*/}"             # remove any directory path
 outfile="${outfile,,*}"              # downcase the filename
 outfile="${outfile%.eps}_$size.png"  # remove suffix, add size and new suffix
 ( set -x ; convert "Cards/$infile" -size ${size}x$size "../img/card/$outfile" )
}

while read -r line ; do
  for i in 80 35 200 ; do
    cvt "$line" $i
  done
done < card_list.txt

答案 2 :(得分:1)

像Hai说的那样,将文件名括在“”中。否则,shell将读取行上的空格作为下一个参数的分隔符。

例如

VAR="test file"
cat $VAR # outputs the contents of 'test', followed by the contents of 'file'

cat "$VAR" # outputs the contents of 'test file'

编辑: 简而言之,你的问题是使用sed来逃避空间。它没有做你正在考虑的事情 - 它正在产生文字字符串“02 \ of \ Clubs.eps”而不是“02. of Clubs.eps”。用“Cards / $ {line}”替换sed部分。