我正在尝试复制一个目录下的一堆文件,其中一些文件名中有空格和单引号。 当我尝试使用find
,grep
和xargs
一起运行时,我会得到以下错误:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
有没有更加强大的使用 xargs
的建议?
我是在使用 BSD 版本的 xargs
,系统为 Mac OS X 10.5.3(Leopard)。
我正在尝试复制一个目录下的一堆文件,其中一些文件名中有空格和单引号。 当我尝试使用find
,grep
和xargs
一起运行时,我会得到以下错误:
find .|grep "FooBar"|xargs -I{} cp "{}" ~/foo/bar
xargs: unterminated quote
有没有更加强大的使用 xargs
的建议?
我是在使用 BSD 版本的 xargs
,系统为 Mac OS X 10.5.3(Leopard)。
find /usr/pcapps/ -mount -type f -size +1000000c | perl -lpe ' s{ }{\\ }g ' | xargs ls -l | sort +4nr | head -200
find . -name '*FooBar*' -exec cp -t /tmp -- {} +
cp -t
标志允许在cp
的开头附近给出目标目录,而不是在结尾处。 从man cp
中得知:-t, --target-directory=DIRECTORY copy all SOURCE arguments into DIRECTORY
--
标志告诉 cp
将其后面的所有内容都解释为文件名,而不是标志,因此以 -
或 --
开头的文件不会让 cp
混淆;您仍然需要这个标志,因为 -
/--
字符由 cp
解释,而其他特殊字符则由 shell 解释。
find -exec command {} +
变体本质上与 xargs 相同。来自 man find
:
-exec command {} + This variant of the -exec action runs the specified command on the selected files, but the command line is built by appending each selected file name at the end; the total number of invoca‐ matched files. The command line is built in much the same way that xargs builds its command lines. Only one instance of `{}' is allowed within the command, and (when find is being invoked from a shell) it should be quoted (for example, '{}') to protect it from interpretation by shells. The command is executed in the starting directory. If any invocation returns a non-zero value as exit status, then find returns a non-zero exit status. If find encounters an error, this can sometimes cause an immedi‐ ate exit, so some pending commands may not be run at all. This variant of -exec always returns true.
如果直接在find中使用它,就可以避免使用管道或shell调用,因此您不需要担心文件名中的任何糟糕字符。
使用Bash(而不是POSIX),您可以使用进程替换将当前行获取到一个变量中。这使您可以使用引号来转义特殊字符:
while read line ; do cp "$line" ~/bar ; done < <(find . | grep foo)
对我而言,我尝试着做一些不同的事情。我想将我的 .txt 文件复制到 tmp 文件夹中。这些 .txt 文件名包含空格和撇号字符。在我的 Mac 上可以工作。
$ find . -type f -name '*.txt' | sed 's/'"'"'/\'"'"'/g' | sed 's/.*/"&"/' | xargs -I{} cp -v {} ./tmp/
-print0
和-0
开关(例如AIX find和xargs),您可以使用这个看起来很糟糕的代码: find . -name "*foo*" | sed -e "s/'/\\\'/g" -e 's/"/\\"/g' -e 's/ /\\ /g' | xargs cp /your/dest
这里sed将负责转义xargs中的空格和引号。
在AIX 5.3上测试通过。
我在Solaris上使用了Bill Star的答案,稍作修改:
find . -mtime +2 | perl -pe 's{^}{\"};s{$}{\"}' > ~/output.file
这将在每行周围加上引号。虽然使用“-l”选项可能会有所帮助,但我没有使用它。
我正在处理的文件列表可能包含“-”,但不包含换行符。我还没有将输出文件与其他命令一起使用,因为我想在通过xargs大规模删除它们之前先查看找到了什么。
Bill Starr的Perl版本无法很好地处理嵌入式换行符(只能处理空格)。对于那些没有GNU工具的操作系统,比如Solaris,一个更完整的版本可能是(使用sed)...
find -type f | sed 's/./\\&/g' | xargs grep string_to_find
根据您的需要调整查找和grep参数或其他命令,但是sed将修复嵌入式换行符/空格/制表符。
$ true | xargs echo no data
no data
$ true | xargsL echo no data # No output
#! /bin/sh
# Line-based version of "xargs" (one pathname per line which may contain any
# amount of whitespace except for newlines) with the added bonus feature that
# it will not execute the command if the input file is empty.
#
# Version 2018.76.3
#
# Copyright (c) 2018 Guenther Brunthaler. All rights reserved.
#
# This script is free software.
# Distribution is permitted under the terms of the GPLv3.
set -e
trap 'test $? = 0 || echo "$0 failed!" >& 2' 0
if IFS= read -r first
then
{
printf '%s\n' "$first"
cat
} | sed 's/./\\&/g' | xargs ${1+"$@"}
fi
将脚本放入$PATH中的某个目录中,不要忘记使用以下命令使其可执行:$ chmod +x xargsL
我稍微尝试了一下,并开始考虑修改xargs,意识到对于我们在这里讨论的用例来说,用Python进行简单的重新实现是一个更好的想法。
首先,整个过程只有大约80行代码,这意味着很容易弄清楚正在发生什么,如果需要不同的行为,您可以在比在Stack Overflow等地方获得回复所需的时间更短的时间内将其混入新脚本中。
请参见https://github.com/johnallsup/jda-misc-scripts/blob/master/yargs和https://github.com/johnallsup/jda-misc-scripts/blob/master/zargs.py。
使用写作如yargs(并安装了Python 3),您可以键入:
find .|grep "FooBar"|yargs -l 203 cp --after ~/foo/bar
一次性复制203个文件。(这里的203只是一个占位符,使用像203这样奇怪的数字可以清楚地表明这个数字没有其他意义。)
如果您真的想要更快且不需要Python的东西,请以zargs和yargs为原型,在C++或C中重新编写。
--delimiter
选项(-d
)。尝试使用\n
作为分隔符,这可以防止xargs
将带空格的行分隔成多个单词/参数。 - MattBianco