私はいくつかのファイルを含むディレクトリ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を使用しています。
現在のディレクトリに、まだ取得されていない名前で一時ディレクトリを安全に作成するには、次のように_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通常、1つのすべきTMPDIR
環境変数を、mktemp
を使用して一時ファイルまたはディレクトリを作成するディレクトリパスに設定できますが、macOSのユーティリティこれに関しては、他のBSDシステムの同じユーティリティとは微妙に異なる動作をするようで、ディレクトリをまったく別の場所に作成します。ただし、上記はmacOSで機能します。もう少し便利なtmpdir=$(TMPDIR=$PWD mktemp -d)
またはtmpdir=$(TMPDIR=. mktemp -d)
を使用しても、デフォルトの一時ディレクトリが別のパーティションとfoo
ディレクトリに大量のデータが含まれていました(つまり、速度が遅くなります)。
MacOSでは、Homebrewを使用してrename
コマンド(Perlスクリプト)をインストールできます。
brew install rename
次に、-p
(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'
または、-print0
とfind
を使用し、-0
とrename
を使用します。
% 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'
内容が最大パラメーター制限を超えるのに十分でない限り(そして、「許容できる」エラーメッセージを気にしない限り)、これはこれ以上複雑にする必要はありません。
mkdir foo/foo
mv foo/* foo/foo
隠しファイルを処理するための修正:
mkdir foo/foo
mv foo/{.,}* foo/foo
私はその逆を提案します。ディレクトリは移動せず、そのコンテンツのみを移動します。
_.
└── foo
├── a.txt
└── b.txt
_
_mkdir foo/foo
_
_.
└── foo
├── foo
├── a.txt
└── b.txt
_
_cd foo
mv $(ls | grep -v '^foo$') foo
cd -
_
_.
└── foo
└── foo
├── a.txt
└── b.txt
_
あなたがbashを持っているなら、あなたもすることができます
_shopt -s extglob
cd foo
mv !(foo) foo
cd -
_
(説明されているように ここ )lsとgrepの実行を回避します。
このソリューションの利点は、それが本当に簡単であるということです。
短所(コメントで指摘されているとおり):
ls -A
_は修正しますが、_ls -a
_は修正しません)mv "$(ls | grep -v '^foo$')" foo
ほとんどの欠点はbashトリックを使用することで対処できますが、クレイジーなファイル名を処理する必要がある場合は、他の回答で説明されているように、より堅牢なアプローチを使用することをお勧めします。
あなたはすでにそれをかなり釘付けしました。ナノ秒単位の現在の日付/時刻のターゲット名や複合サフィックスとしての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
mkdir foo/foo && mv foo/!(foo) foo/foo
ソースフォルダー(foo)があるディレクトリにcdする必要があります。
次に、上記のコマンドを実行します。同じ名前のと呼ばれるフォルダを作成し、親fooの内容を子fooディレクトリに移動します(子ディレクトリを除く、つまり!の指定を除く)。
子fooディレクトリがすでに存在する場合は、最初の部分を無視して、mvコマンドを実行するだけです。
mv foo/!(foo) foo/foo
MacOSでは、extglobオプションを設定する必要がある場合があります。
shopt -s extglob
上記の提案に加えて、rsyncをチェックアウトすることもできます。始める前に、常に--dry-run
オプションなしで実行する前に、rsyncを使用してください。 --dry-run
は、実際の生活で何が起こるかを教えてくれます。
rsyncには、ファイルのコピー/移動に役立つだけでなく、プロセスを高速化できる多くのオプションがあります。ここにあなたの仕事を終わらせる一つの方法があります。 --remove-source-files
optionは、移動プロセス後にファイルをソースから削除します(実際には、コピーとそれに続く削除です)。 rsyncコマンドが最初にfooの内容を読み取り、fooという既存のディレクトリ内にディレクトリfooを作成してから、コピープロセスを開始することに注意してください。それはfooのソースファイルを削除することで終わります。注意:ディレクトリ名のスラッシュには特に注意してください。
多くの人が上記で指摘したように(シンボリックリンクなど)他の考慮事項がありますが、man rsync
は、必要なオプションを選択するのに役立ちます。ここでのメモ:ファイルの解決策を求めてきたので、これは機能します。これは、残された空のディレクトリを削除しません。追加の手順なしでこれを行う方法を私は知りませんので、誰かがこれに追加できる場合は、事前に感謝します!
mkdir foo
cd foo
touch aaa bbb ccc ddd
cd ..
rsync -av --remove-source-files foo foo/
a =アーカイブモード
v =詳細
単にfooに触れないでください。同じ名前の問題は発生しません。代わりに、その中に同じ名前のディレクトリを作成し、そこにすべてを移動します。そのために$ _トリックを使用し、&&を1行にする:
_cd foo && mkdir $_ && mv * $_
_
これにより、無視できる無害なエラー(mv:rename foo to foo/foo:Invalid argument)がスローされます。
他の答えに対する利点:ディレクトリ名を一度入力するだけでよいので、タイプミスを間違えることはありません。
これを他の回答と組み合わせてさらに効果を上げることができます。
mv {.,}* $_
_は隠しファイルを処理します(無視できるエラーが増える代わりに)mv ./!(foo)
はエラーメッセージを回避しますが、_shopt -s extglob
_を設定した場合のみこれは実際には一見単純ですが、他の誰もこの答えをまだ提供していないことに驚いています。そのため、ここでは例として以下を使用します...
$ mkdir foo
$ mkdir bar
$ mkdir ./bar/foo
$ touch ./bar/foo/file1 && touch ./bar/foo/file2 && touch ./bar/foo/file3
$ ls ./
bar/ foo/
$ ls ./bar/foo
file1 file2 file3
魔法の部分がやって来る
$ mv ./bar/foo ./foo/
$ ls ./foo/
foo/
$ ls ./foo/foo/
file1 file2 file3
なぜこれが機能するのですか?私は初心者なので、正確な答えはわかりませんが、末尾の/
文字がフォルダでどのように処理されるかにかかっていることは理解しています。
そのmvコマンドをよく見てください
$ mv ./bar/foo ./foo/
ファイルのターゲットフォルダーが末尾の/
なしで指定されているのに注意してください。ターゲットフォルダーの名前を共有する宛先は、末尾の/
'を使用して指定されています。これはあなたの魔法の切符であり、あなたが注意を払っていないなら、あなたをロバに噛ませることができます。
現時点ではこれ以上の詳細な回答を提供することはできませんが、流暢に知識のある誰かが私の回答を自由に編集してくれるでしょう。
いずれにしても、ls
コマンドを1回使用するだけで、中間フォルダーを作成しないので、これは最も単純で簡単な解決策のようです。
うまくいきますように、乾杯!