Bash中的双引号与星号文件名扩展

Bash中的双引号与星号文件名扩展,bash,quotes,asterisk,expansion,Bash,Quotes,Asterisk,Expansion,在目录~/temp/a/foo/和~/temp/b/foo-foo/中,我有一些文件名为bar1,bar2,bar1,bar2,等等 我正在尝试编写一行Bash,将包含“foo”作为名称最后一部分的目录中的所有这些文件复制到相应的“foo”文件夹上方的文件夹中 只要文件名中没有空格,这是一项简单的任务,但是在处理foo-foo目录时,以下两个命令失败: for dir in `find . -type d -name '*foo'` ; do cp $dir/* "$(echo $dir|sed

在目录
~/temp/a/foo/
~/temp/b/foo-foo/
中,我有一些文件名为
bar1
bar2
bar1
bar2
,等等

我正在尝试编写一行Bash,将包含“foo”作为名称最后一部分的目录中的所有这些文件复制到相应的“foo”文件夹上方的文件夹中

只要文件名中没有空格,这是一项简单的任务,但是在处理
foo-foo
目录时,以下两个命令失败:

for dir in `find . -type d -name '*foo'` ; do cp $dir/* "$(echo $dir|sed 's_foo__g')" ; done
cp
命令无法将
“foo foo”
的最后一个
foo
作为同一目录名的一部分。)

“$dir/*”
未展开。)

像用
“$(echo$dir/*)”
替换
$dir/*
这样的尝试更不成功


有没有一种简单的方法可以扩展
$dir/*
,以便
cp
理解?

~/temp/b/foo-foo/
重命名为没有空格的对象,例如
~/temp/b/foo-foo/
,然后重新执行您试图执行的操作。在那之后,如果你真的需要的话,你可以用空格把它重新命名为原来的名字。顺便说一句,我自己从来不使用包含空格的文件名,只是为了避免像现在这样的复杂情况

这里的问题不在于
cp
,而在于
for
,因为默认情况下,它按单词而不是按目录名拆分子shell的输出

一个懒惰的解决方法是使用
而不是
,并按如下方式逐行处理目录列表:

find . -type d -name '*foo' | while read dir; do cp "$dir"/* "$(echo $dir | sed 's_foo__g')" ; done
这应该可以解决您的空间问题,但这绝不是一个万无一失的解决方案


请参阅Charles Duffy的答案以获得更准确的解决方案。

不仅循环的
错误--
sed也不是此项工作的正确工具

while IFS= read -r -d '' dir; do
  cp "$dir" "${dir/foo/}"
done < <(find . -type d -name '*foo' -print0)
而IFS=read-r-d''dir;做
cp“$dir”“${dir/foo/}”

done<我试图简化一个问题,该问题涉及数十个文件夹和数百个名称难看的文件。重命名它们似乎是次优选项,特别是因为我以后必须还原文件夹名称。请看几乎正确的,但您确实应该对管道的内容进行NUL分隔——否则,恶意创建的包含换行符的文件名可能会导致复制任意文件……顺便说一下,
dir
永远不会包含空格作为
for
工作方式的人工制品,这也是不准确的;单词分割不是for语义的一部分,如果
IFS
设置为不包含空格,字符串分割输出可以包含空格。也就是说,由于其他原因,依赖字符串拆分是错误的(例如,因为glob扩展同时发生)。谢谢,这似乎可以解决我在现实世界中遇到的所有文件复制问题。在我的简化示例中,它实际上失败了,这是一个有趣的细节。(sed删除
foo-foo
文件夹名中的
foo
s)。@CharlesDuffy“几乎正确”。。。这与事实有点不符,呃:-)但这正是我添加“绝非万无一失”的原因。谢谢你的提示,我将重新表述我为
@uvett写的
,我敦促你接受查尔斯·达菲的答案,而不是我的答案。它更准确,解释更清楚,并且能处理所有的角点情况。你确定那里需要
IFS=
?棒极了!尽管janos建议接受这个答案作为解决方案,但事实证明,代码实际上并没有移动任何文件。Bash 4.2.24抱怨cp无法统计“”。我不能100%确定行
cp“$dir”“${dir/foo/}”
的作用。它真的试图将文件复制到
foo
目录上方的现有目录吗?@uvett Dan指出了这个错误。如果这不能解决问题,请确保文件以
#开头/bin/bash
;此代码不能与
#一起使用/bin/sh
@janos
IFS=
可防止删除尾随空格。(这也可以通过不指定
dir
并允许将读取的内容分配给
$REPLY
)来避免。对不起,运气不好。只要我使用
cp-r
,并且
foo
目录包含
foo
前面的字符,我就能够用这些行移动文件,但是在这种情况下,行
cp-r“$dir”“${dir/foo/}”
(显然)基于旧目录名创建一个新目录,减去
foo
部分。对于名为only
foo
的目录,命令失败。关于如何解决这个问题,我的第一个想法与我创建这个线程的想法完全相同:将Charles的
cp“$dir”“${dir/foo/}”
替换为
cp“$dir/*”
“${dir/foo}”
(因为引号阻止了星号扩展,所以毫无用处)。
while IFS= read -r -d '' dir; do
  cp "$dir" "${dir/foo/}"
done < <(find . -type d -name '*foo' -print0)