我有一个包含多个文件的目录foo
:
.
└── foo
├── a.txt
└── b.txt
我想将其移动到同名目录中:
.
└── foo
└── foo
├── a.txt
└── b.txt
我目前正在创建一个临时目录bar
,然后移入foo
并重bar
命名bar
为foo
:
mkdir bar
mv foo bar
mv bar foo
但这感觉有点麻烦,我必须选择一个bar
尚未使用的名称。
有没有更优雅或更直接的方法来实现这一目标?如果这很重要的话,我在 macOS 上。
答案1
要在当前目录中安全地创建一个临时目录,其名称尚未被使用,您可以mktemp -d
像这样使用:
tmpdir=$(mktemp -d "$PWD"/tmp.XXXXXXXX) # using ./tmp.XXXXXXXX would work too
该mktemp -d
命令将在给定路径创建一个目录,X
路径名末尾的 -es 替换为随机字母数字字符。它将返回创建的目录的路径名,我们将该值存储在tmpdir
. 1
tmpdir
然后,当遵循您已经执行的相同过程时,可以使用该变量,并bar
替换为"$tmpdir"
:
mv foo "$tmpdir"
mv "$tmpdir" foo
unset tmpdir
最后的unset tmpdir
只是删除变量。
1 通常,一个应该能够将TMPDIR
环境变量设置为想要使用 来创建临时文件或目录的目录路径mktemp
,但 macOS 上的实用程序似乎与其他 BSD 系统上的相同实用程序的工作方式略有不同,并且将创建目录在一个完全不同的地方。不过,上述内容在 macOS 上是有效的。使用稍微方便tmpdir=$(TMPDIR=$PWD mktemp -d)
甚至tmpdir=$(TMPDIR=. mktemp -d)
只是一个问题在 macOS 上,如果默认临时目录位于另一个分区并且该foo
目录包含大量数据(即速度会很慢)。
答案2
在 macOS 上,您可以rename
使用 Homebrew 安装命令(Perl 脚本):
brew install rename
然后使用-p
(a la mkdir
) 让它创建任何必要的目录,并-A
添加前缀:
% mkdir -p foo/bar; touch foo/{a,b}.txt foo/bar/c.txt
% rename -p -A foo/ foo/*
% tree foo
foo
└── foo
├── a.txt
├── b.txt
└── bar
└── c.txt
运行以-n
显示更改而不重命名(试运行):
% rename -p -A foo/ foo/* -n
'foo/a.txt' would be renamed to 'foo/foo/a.txt'
'foo/b.txt' would be renamed to 'foo/foo/b.txt'
'foo/bar' would be renamed to 'foo/foo/bar'
如果您有点文件,那么简单的文件*
将无法拾取它们,请使用其他方法rename
:
在 bash 中,(误)使用
GLOBIGNORE
来*
匹配点文件:$ GLOBIGNORE=.; rename -p -A foo/ foo/* -n 'foo/.baz' would be renamed to 'foo/foo/.baz' 'foo/a.txt' would be renamed to 'foo/foo/a.txt' 'foo/b.txt' would be renamed to 'foo/foo/b.txt' 'foo/bar' would be renamed to 'foo/foo/bar'
find
或者与-print0
和rename
with 一起使用-0
:% find foo -mindepth 1 -maxdepth 1 -print0 | rename -0 -p -A foo/ -n Reading filenames from STDIN Splitting on NUL bytes 'foo/b.txt' would be renamed to 'foo/foo/b.txt' 'foo/a.txt' would be renamed to 'foo/foo/a.txt' 'foo/bar' would be renamed to 'foo/foo/bar' 'foo/.baz' would be renamed to 'foo/foo/.baz'
答案3
只要内容不足以超过最大参数限制(并且您不介意“可接受的”错误消息),那么它不需要比这更复杂:
mkdir foo/foo
mv foo/* foo/foo
处理隐藏文件的修正:
mkdir foo/foo
mv foo/{.,}* foo/foo
答案4
你已经很清楚了。您可以为瞬态目录选择不同的名称,例如带有当前日期/时间(以纳秒为单位)的目标名称以及我们的 PID 作为复合后缀,但这仍然假设该目录尚不存在:
dir=foo # The directory we want to nest
now=$(date +'%s_%N') # Seconds and nanoseconds
mkdir "$dir.$now.$$" # Create a transient directory
mv -f "$dir" "$dir.$now.$$/" # Move our directory inside transient directory
mv -f "$dir.$now.$$" "$dir" # Rename the transient directory to the name with which we started
如果您想要一个有保证的稳健解决方案,我会循环mkdir
直到它成功
now=$(date +'%s_%N')
while ! mkdir -m700 "$dir.$now.$$" 2>/dev/null
do
sleep 0.$$
now=$(date +'%s_%N')
done