在bash脚本中使用Ger正确的空格

时间:2013-08-10 21:43:05

标签: bash scripting

我对bash脚本没有那么有经验,所以考虑在实践中研究它。最近我试图制作一个简单的脚本,它应该显示至少1 GB大小的所有文件,并且在名称中遇到问题转义的空格。 如果我这样做,它在终端工作正常:

$ find /home/dem -size +1000M -print|sed -e 's/ /\\ /'
/home/dem/WEB/CMS/WP/Themes/Premium_elegant_themes/ETPSD.rar
/home/dem/VirtualBox\ VMs/Lubuntu13.04x86/Lubuntu13.04x86.vdi
/home/dem/VirtualBox\ VMs/Win7/Win7-test.vdi
/home/dem/VirtualBox\ VMs/FreeBSD9.1/FreeBSD9.1.vdi
/home/dem/VirtualBox\ VMs/backup_Lubuntu13.04x86/Lubuntu13.04x86.vdi
/home/dem/VirtualBox\ VMs/Beini-1.2.3/Beini-1.2.3.vdi
/home/dem/VirtualBox\ VMs/BackTrack5RC3/BackTrack5RC3.vdi
/home/dem/VirtualBox\ VMs/WinXPx32/WinXPx32.vdi

但是在这个剧本中:

#!/bin/bash

for i in "$( find /home/dem -size +1000M -print|sed -e 's/ /\\ /' )"
 do 
  res="$( ls -lh $i )"
  echo $res
done 

它会出错,因为您可能会看到左侧部分被剥离:

ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/Lubuntu13.04x86/Lubuntu13.04x86.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/Win7/Win7-test.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/FreeBSD9.1/FreeBSD9.1.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/backup_Lubuntu13.04x86/Lubuntu13.04x86.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/Beini-1.2.3/Beini-1.2.3.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/BackTrack5RC3/BackTrack5RC3.vdi: No such file or directory
ls: cannot access /home/dem/VirtualBox\: No such file or directory
ls: cannot access VMs/WinXPx32/WinXPx32.vdi: No such file or directory
-rw-rw-r-- 1 dem dem 3.1G Jul 13 02:54 /home/dem/Downloads/BT5R3-GNOME-32/BT5R3-GNOME-32.iso -rw------- 1 dem dem 1.1G Dec 27 2012 /home/dem/WEB/CMS/WP/Themes/Premium_elegant_themes/ETPSD.rar

我需要脚本来显示带有空格的文件+检索ls -lh所做的每个文件的实际大小。 没有sed格式:

$ find /home/dem -size +1000M -print
/home/dem/WEB/CMS/WP/Themes/Premium_elegant_themes/ETPSD.rar
/home/dem/VirtualBox VMs/Lubuntu13.04x86/Lubuntu13.04x86.vdi
/home/dem/VirtualBox VMs/Win7/Win7-test.vdi
/home/dem/VirtualBox VMs/FreeBSD9.1/FreeBSD9.1.vdi
/home/dem/VirtualBox VMs/backup_Lubuntu13.04x86/Lubuntu13.04x86.vdi
/home/dem/VirtualBox VMs/Beini-1.2.3/Beini-1.2.3.vdi
/home/dem/VirtualBox VMs/BackTrack5RC3/BackTrack5RC3.vdi
/home/dem/VirtualBox VMs/WinXPx32/WinXPx32.vdi

3 个答案:

答案 0 :(得分:3)

xargs对于简单的情况非常有用,但在处理路径中带有换行符的文件名时,它需要-0(NUL分隔的输入)才能正常运行(在UNIX上是合法的)。如果你真的需要将文件名读入shell脚本,你可以这样做:

while IFS='' read -r -d '' filename; do
  ls -lh "$filename"
done < <(find /home/dem -size +1000M -print0)

...或者像这样,使用现代版本的POSIX标准find中的功能来复制xargs的行为:

find /home/dem -size +1000M -exec ls -lh '{}' +

答案 1 :(得分:2)

只需使用xargs

find /home/dem -size +1000M -print0 | xargs -0 ls -lh

答案 2 :(得分:2)

在shell脚本中,参数除以空格,如果您要查找包含空格的文件名,则可能会很麻烦。当您使用for循环时,这是一个问题,因为for循环会将每个空格视为参数分隔符:

$ ls -l
this is file number one
this is file number two

$ for file in $(find . -type f)
> do
>     echo "My file is '$file'"
> done
my file is 'this'
my file is 'is'
my file is 'file'
my file is 'number'
my file is 'one'
my file is 'this'
my file is 'is'
my file is 'file'
my file is 'number'
my file is 'two'

在这种情况下,for将每个空格视为一个单独的文件,这是您不想要的。 for还存在其他问题:

  • for循环在完成$(...)中的命令处理之前无法启动。
  • 可以超出命令行缓冲区。 shell执行的操作是在$(...)中执行命令,并用该命令的结果替换$(...)。如果您使用返回数十万个文件的find命令,则可能会超出命令行缓冲区。更糟糕的是,它将默默地发生。除非你看一下,否则你永远不会知道文件被删除了。事实上,我已经看到有人使用这种类型的for ... $(...)循环测试shell脚本的地方认为一切都很好,但是命令在非常危急的情况下失败。
  • 效率低下,因为它必须生成一个单独的shell进程。好吧,它已经不是那么大了,但还是......

更好的方法是使用while read循环。在BASH,它看起来像这样:

find ... -print0 | while read -d $'\0' file
do
   ....
done

-print0参数打印出所有找到的文件,但用NULL字符分隔它们。 while read -d\$0 ...语法会破坏NULL字符上的参数名称,而不会像通常那样破坏新行。因此,即使您的文件中包含新行(并且在Unix中允许文件名包含新行,while read -d\$0...仍将正确读取您的文件名。

更好的是,这解决了其他一些问题:

  • 命令行缓冲区无法重载。
  • 您的while read循环将与find并行执行。 find无需先找到所有文件。
  • 您不会产生单独的流程。

观察:

$ ls -l
this is file number one
this is file number two

$ find . -type f -print0 | while read -d\$0 file
>     echo "My file is '$file'"
> done
my file is 'this is file number one'
my file is 'this is file number two'

顺便说一下,另一个名为xargs的命令有一个类似的参数:

find . -type f -mtime +100 -print0 | xargs -0 rm

xargs命令从STDIN获取文件名,并将它们传递给给定的命令。它保证传递的参数不会超过命令行缓冲区。如果他们这样做,xargs将多次运行传递给它的命令。

通常,(如forxargs解析空白上的文件名。但是,您可以传递一个参数来解析空值上的名称。

这个参数从系统到系统的区别

对不起喊叫,但我需要说清楚。不同的系统具有xargs命令的不同参数,您需要参考联机帮助页以查看系统采用的参数。在我的Mac上,它是-0。在GNU上,它是--null,尽管一些Linux发行版也采用-0。而且,某些Unix版本甚至可能没有这个参数。