如何将目录移动到同名目录中?

如何将目录移动到同名目录中?

我有一个包含多个文件的目录foo

.
└── foo
    ├── a.txt
    └── b.txt

我想将其移动到同名目录中:

.
└── foo
    └── foo
        ├── a.txt
        └── b.txt

我目前正在创建一个临时目录bar,然后移入foo并重bar命名barfoo

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或者与-print0renamewith 一起使用-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

相关内容