空格中断循环的文件名,find命令


34

我有一个脚本,可以搜索多个子文件夹中的所有文件并将其归档到tar。我的剧本是

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

find命令为我提供以下输出

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

但是FILE变量仅存储路径./F1/F1-2013-03-19的第一部分,然后存储下一部分160413.csv

我尝试过使用readwhile循环,

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

但我收到以下错误

bash: read: `./F1/F1-2013-03-19': not a valid identifier

谁能建议另一种方法?

更新资料

如以下答案中所建议,我更新了脚本

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

我得到的输出是

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

4
看来您应该IFS=$'\n'在`for循环之前进行设置,以使其按每一行进行解析
kiri

Answers:


36

在此处使用forwith find是错误的方法,例如,请参阅有关您正在打开的蠕虫罐的这篇文章。

推荐的方法是使用findwhileread描述在这里。下面是一个适合您的示例:

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

这样,您可以使用null(\0)字符来分隔文件名,这意味着空格和其他特殊字符的变化不会引起问题。

为了使用find找到的文件更新档案,您可以将其输出直接传递给tar

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

请注意,您不必区分存档是否存在,tar将明智地进行处理。还请注意使用-printf此处以避免./在归档中包含该位。


谢谢,这几乎可行。唯一的事情是将其存档./为tar。./.tar tar: ./archive.tar: file is the archive; not dumped
Ubuntuser 2013年

@Ubuntuser您可以添加一个简单的检查以查看if [[ "$FILE" == "./" ]]; then continue
kiri

@Ubuntuser:您可以避免./使用-printf更新后的答案。但是,无论是否包含它,都不应有任何区别,因为它仅引用当前目录。我还提供find/tar了您可能想要使用的替代组合。
2013年

对于那些想要sort在迭代之前获得结果的人,需要sort -z使用null分隔符。
Adambean

13

尝试for像这样引用循环:

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

没有引号,bash根本无法很好地处理空格和换行符(\n)...

也尝试设置

IFS=$'\n'

1
为$ IFS +1。这说明了分隔符。

1
这是对我有用的解决方案。我曾经comm用来比较排序的文件列表,尽管文件名中的变量无效,但文件名中有空格。然后,我看到了cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html,使用IFS = $(echo -en“ \ n \ b”)设置$ IFS的解决方案为我工作。
pbhj

加上双引号,优雅,简单,漂亮-谢谢!
大富翁


4

除了正确的报价外,您还可以知道 find您使用NULL分隔符,然后在while循环中读取和处理结果

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

这应该处理所有与POSIX兼容的文件名-请参阅 man find

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.

这只是对我有用的解决方案。谢谢。
codefreak


1

我做了这样的事情来查找可能包含空格的文件。

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

像魅力一样工作:)



0

我认为您最好使用find-exec选项。

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

然后,Find使用系统调用执行命令,以便保留空格和换行符(而不是管道,这需要引用特殊字符)。请注意,无论归档文件是否已存在,“ tar -c”都有效,并且(至少使用bash){}和+都不需要加引号。


-1

正如minerz029建议的那样,您需要引用find命令的扩展名。您还需要引用$FILE循环中所有的替换。

for FILE in "$(find . -type f  -name '*.*')"
do
    if [ ! -f archive.tar ]; then
        tar -cpf archive.tar "$FILE"
    else 
        tar -upf archive.tar "$FILE" 
    fi
done

注意,$()语法应优先于反引号的使用;看到这个U&L问题。我还删除了[[关键字,并用[命令替换了它,因为它是POSIX。


关于[[[,似乎[[是较新的,并支持更多功能,例如遍历和正则表达式匹配。[[只在bash,但不是sh
kiri

@ minerz029是的。我就是这么说的 我不知道您所指的是全球[[支持。根据格雷格(Greg)的Wiki,内部没有发生混乱[[
约瑟夫R.13年

[ "ab" == a? ] && echo "true"然后尝试[[ "ab" == a? ]] && echo "true"
kiri 2013年

@ minerz029这不是问题。这些是正则表达式(松散解释)。这不是问题,因为它的a*意思是“后跟任意数量的字符的a”,而不是“其名称以开头a且后有任意数量的字符的所有文件”。尝试[ ab = a* ] && echo true[[ ab == a* ]] && echo true
Joseph R.

嗯,[[仍然做正则表达式而[没有。一定感到困惑
Kiri 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.